Count records excluding duplicates [duplicate] - dc.js

I am stuck at a unique problem involving dc.js and crossfilter. I have some data which i need to display using Number Charts powered by dc.js. However i found minimal documentation for the number charts and hence posting my query.
Here is the JSFiddle for what i have conceptualized so far.
I basically want to show the unique project count in box 1 which in this case would be 3, unique place count in box 2 which in this case would be 11 and the screen failure rate which would be 2/15*100 i.e. 15.3%
Currently i have made this working using jquery but thats just a hack. I would like to have these number charts based on cross table aggregation so that i can drill down on the data.
I have come across examples for reductions to calculate counts but they were for bar charts but in the number chart we need to have a value accessor for displaying data.
Can someone help me out please?
PS:
Here is the jquery code that i wrote. Dont know if this would be helpful.
$(document).ready(function() {
var baseURL = window.location.origin;
$.ajax({
url : baseURL + '/api/PlaceTable',
type : 'GET',
data : {},
async : true,
dataType : "json",
success : function(response) {
//Project Count
var projectIdCount = [];
for (i = 0; i < response.length; i++) {
if(response[i].Project != undefined){
if($.inArray(response[i].Project, projectIdCount) === -1){
projectIdCount.push(response[i].Project);
}
}
}
$('#number-box1').text(ProjectIdCount.length);
//Place Count
var placeIdCount = [];
for (i = 0; i < response.length; i++) {
if(response[i].Place != undefined){
if($.inArray(response[i].Place, placeIdCount) === -1){
placeIdCount.push(response[i].Place);
}
}
}
And for displaying a running sum of a column containing binary values i used this code, which worked in the number chart:
numberChart
.valueAccessor(function(x){ return +flag.groupAll().reduceCount().reduceSum(function(d) { return d.Flag; }).value();})
.group(ndx.groupAll());

The failure percentage calculation is a separate problem which I think you've asked elsewhere. To get the unique count, it is pretty easy to make a "fake groupAll" which returns the number of unique keys in its value method.
We'll also need to filter out the empty bins since crossfilter doesn't do that automatically.
function bin_counter(group) {
return {
value: function() {
return group.all().filter(function(kv) {
return kv.value > 0;
}).length;
}
};
}
var projectGroup = project.group();
projectCount
.valueAccessor(function(x){ return x;})
.group(bin_counter(projectGroup));
Updated fiddle here, still ignoring the failure% part:
http://jsfiddle.net/gordonwoodhull/vct0dzou/1/

Related

unique Count of values in dimension using crossfilter (dc.js) [duplicate]

I am stuck at a unique problem involving dc.js and crossfilter. I have some data which i need to display using Number Charts powered by dc.js. However i found minimal documentation for the number charts and hence posting my query.
Here is the JSFiddle for what i have conceptualized so far.
I basically want to show the unique project count in box 1 which in this case would be 3, unique place count in box 2 which in this case would be 11 and the screen failure rate which would be 2/15*100 i.e. 15.3%
Currently i have made this working using jquery but thats just a hack. I would like to have these number charts based on cross table aggregation so that i can drill down on the data.
I have come across examples for reductions to calculate counts but they were for bar charts but in the number chart we need to have a value accessor for displaying data.
Can someone help me out please?
PS:
Here is the jquery code that i wrote. Dont know if this would be helpful.
$(document).ready(function() {
var baseURL = window.location.origin;
$.ajax({
url : baseURL + '/api/PlaceTable',
type : 'GET',
data : {},
async : true,
dataType : "json",
success : function(response) {
//Project Count
var projectIdCount = [];
for (i = 0; i < response.length; i++) {
if(response[i].Project != undefined){
if($.inArray(response[i].Project, projectIdCount) === -1){
projectIdCount.push(response[i].Project);
}
}
}
$('#number-box1').text(ProjectIdCount.length);
//Place Count
var placeIdCount = [];
for (i = 0; i < response.length; i++) {
if(response[i].Place != undefined){
if($.inArray(response[i].Place, placeIdCount) === -1){
placeIdCount.push(response[i].Place);
}
}
}
And for displaying a running sum of a column containing binary values i used this code, which worked in the number chart:
numberChart
.valueAccessor(function(x){ return +flag.groupAll().reduceCount().reduceSum(function(d) { return d.Flag; }).value();})
.group(ndx.groupAll());
The failure percentage calculation is a separate problem which I think you've asked elsewhere. To get the unique count, it is pretty easy to make a "fake groupAll" which returns the number of unique keys in its value method.
We'll also need to filter out the empty bins since crossfilter doesn't do that automatically.
function bin_counter(group) {
return {
value: function() {
return group.all().filter(function(kv) {
return kv.value > 0;
}).length;
}
};
}
var projectGroup = project.group();
projectCount
.valueAccessor(function(x){ return x;})
.group(bin_counter(projectGroup));
Updated fiddle here, still ignoring the failure% part:
http://jsfiddle.net/gordonwoodhull/vct0dzou/1/

Adding External filters for Kendo UI Grid

What i want to achieve is:
Have a Master Grid. Clicking on a row of this grid, i want to filter the rows of the ChildGrid.
What i have done so far:
function updateChildGridRows(field, operator, value) {
// get the kendoGrid element.
var gridData = $("#childGrid").data("kendoGrid");
var filterField = field;
var filterValue = value;
// get currently applied filters from the Grid.
var currFilterObj = gridData.dataSource.filter();
// if the oject we obtained above is null/undefined, set this to an empty array
var currentFilters = currFilterObj ? currFilterObj.filters : [];
// iterate over current filters array. if a filter for "filterField" is already
// defined, remove it from the array
// once an entry is removed, we stop looking at the rest of the array.
if (currentFilters && currentFilters.length > 0) {
for (var i = 0; i < currentFilters.length; i++) {
if (currentFilters[i].field == filterField) {
currentFilters.splice(i, 1);
break;
}
}
}
if (filterValue != "") {
currentFilters.push({
field: filterField,
operator: "eq",
value: filterValue
});
}
gridData.dataSource.filter({
filters: currentFilters
}); }
I got this code from the following jsfiddle:
http://jsfiddle.net/randombw/27hTK/
I have attached the MasterGrid's Change event to MasterGridSelectionChange() method. From there i am calling my filter method.
But when i click on the MasterGrid's row, all of the rows in my ChildGrid are getting removed.
One thing i can understand is, if i give wrong column name in the filter list, all the rows will be removed. But even though i have given correct ColumnName, my rows are getting deleted.
Sorry for the long post.
Please help me with this issue, as i am stuck with this for almost 4 days!
Thanks.
You can define ClientDetailTemplateId for the master grid and ToClientTemplate() for the Child grid
read Grid Hierarchy for a example

unique values count in dc.js + crossfilter

I am stuck at a unique problem involving dc.js and crossfilter. I have some data which i need to display using Number Charts powered by dc.js. However i found minimal documentation for the number charts and hence posting my query.
Here is the JSFiddle for what i have conceptualized so far.
I basically want to show the unique project count in box 1 which in this case would be 3, unique place count in box 2 which in this case would be 11 and the screen failure rate which would be 2/15*100 i.e. 15.3%
Currently i have made this working using jquery but thats just a hack. I would like to have these number charts based on cross table aggregation so that i can drill down on the data.
I have come across examples for reductions to calculate counts but they were for bar charts but in the number chart we need to have a value accessor for displaying data.
Can someone help me out please?
PS:
Here is the jquery code that i wrote. Dont know if this would be helpful.
$(document).ready(function() {
var baseURL = window.location.origin;
$.ajax({
url : baseURL + '/api/PlaceTable',
type : 'GET',
data : {},
async : true,
dataType : "json",
success : function(response) {
//Project Count
var projectIdCount = [];
for (i = 0; i < response.length; i++) {
if(response[i].Project != undefined){
if($.inArray(response[i].Project, projectIdCount) === -1){
projectIdCount.push(response[i].Project);
}
}
}
$('#number-box1').text(ProjectIdCount.length);
//Place Count
var placeIdCount = [];
for (i = 0; i < response.length; i++) {
if(response[i].Place != undefined){
if($.inArray(response[i].Place, placeIdCount) === -1){
placeIdCount.push(response[i].Place);
}
}
}
And for displaying a running sum of a column containing binary values i used this code, which worked in the number chart:
numberChart
.valueAccessor(function(x){ return +flag.groupAll().reduceCount().reduceSum(function(d) { return d.Flag; }).value();})
.group(ndx.groupAll());
The failure percentage calculation is a separate problem which I think you've asked elsewhere. To get the unique count, it is pretty easy to make a "fake groupAll" which returns the number of unique keys in its value method.
We'll also need to filter out the empty bins since crossfilter doesn't do that automatically.
function bin_counter(group) {
return {
value: function() {
return group.all().filter(function(kv) {
return kv.value > 0;
}).length;
}
};
}
var projectGroup = project.group();
projectCount
.valueAccessor(function(x){ return x;})
.group(bin_counter(projectGroup));
Updated fiddle here, still ignoring the failure% part:
http://jsfiddle.net/gordonwoodhull/vct0dzou/1/

Assigning functions to multiple slickgrids

Please help to solve this very annoying problem. I am using a for loop to iterate over an array of data and create multiple grids. It is working well but the filter function is not binding properly (it only binds to the LAST grid created) Here is the code:
// this function iterates over the data to build the grids
function buildTables() {
// "domain" contains the dataset array
for (i = 0; i < domains.length; i++) {
var dataView;
dataView = new Slick.Data.DataView();
var d = domains[i];
grid = new Slick.Grid('#' + d.name, dataView, d.columns, grids.options);
var data = d.data;
// create a column filter collection for each grid - this works fine
var columnFilters = columnFilters[d.name];
// this seems to be working just fine
// Chrome console confirms it is is processed when rendering the filters
grid.onHeaderRowCellRendered.subscribe(function (e, args) {
$(args.node).empty();
$("<input type='text'>")
.data("columnId", args.column.id)
.val(columnFilters[args.column.id])
.appendTo(args.node);
});
// respond to changes in filter inputs
$(grid.getHeaderRow()).delegate(":input", "change keyup", function (e) {
var columnID = $(this).data("columnId");
if (columnID != null) {
// this works fine - when the user enters text into the input - it
// adds the filter term to the filter obj appropriately
// I have tested this extensively and it works appropriately on
// all grids (ie each grid has a distinct columnFilters object
var gridID = $(this).parents('.grid').attr('id');
columnFilters[gridID][columnID] = $.trim($(this).val());
dataView.refresh();
}
});
//##### FAIL #####
// this is where things seem to go wrong
// The row item always provides data from the LAST grid populated!!
// For example, if I have three grids, and I enter a filter term for
// grids 1 or 2 or 3 the row item below always belongs to grid 3!!
function filter(row) {
var gridID = $(this).parents('.grid').attr('id');
for (var columnId in grids.columnFilters[gridID]) {
if (columnId !== undefined && columnFilters[columnId] !== "") {
var header = grid.getColumns()[grid.getColumnIndex(columnId)];
//console.log(header.name);
}
}
return true;
}
grid.init();
dataView.beginUpdate();
dataView.setItems(data);
dataView.setFilter(filter); // does it matter than I only have one dataView instance?
dataView.endUpdate();
grid.invalidate();
grid.render();
In summary, each function seems to be binding appropriately to each grid except for the filter function. When I enter a filter term into ANY grid, it returns the rows from the last grid only.
I have spent several hours trying to find the fault but have to admit defeat. Any help would be most appreciated.
yes, it matters that you have only one instance of dataView. and also sooner or later you will come up to the fact that one variable for all grids is also a bad idea
so add a var dataView to your loop, it should solve the problem

facing performance issues with knockout mapping plugin

I have decent large data set of around 1100 records. This data set is mapped to an observable array which is then bound to a view. Since these records are updated frequently, the observable array is updated every time using the ko.mapping.fromJS helper.
This particular command takes around 40s to process all the rows. The user interface just locks for that period of time.
Here is the code -
var transactionList = ko.mapping.fromJS([]);
//Getting the latest transactions which are around 1100 in number;
var data = storage.transactions();
//Mapping the data to the observable array, which takes around 40s
ko.mapping.fromJS(data,transactionList)
Is there a workaround for this? Or should I just opt of web workers to improve performances?
Knockout.viewmodel is a replacement for knockout.mapping that is significantly faster at creating viewmodels for large object arrays like this. You should notice a significant performance increase.
http://coderenaissance.github.com/knockout.viewmodel/
I have also thought of a workaround as follows, this uses less amount of code-
var transactionList = ko.mapping.fromJS([]);
//Getting the latest transactions which are around 1100 in number;
var data = storage.transactions();
//Mapping the data to the observable array, which takes around 40s
// Instead of - ko.mapping.fromJS(data,transactionList)
var i = 0;
//clear the list completely first
transactionList.destroyAll();
//Set an interval of 0 and keep pushing the content to the list one by one.
var interval = setInterval(function () {if (i == data.length - 1 ) {
clearInterval(interval);}
transactionList.push(ko.mapping.fromJS(data[i++]));
}, 0);
I had the same problem with mapping plugin. Knockout team says that mapping plugin is not intended to work with large arrays. If you have to load such big data to the page then likely you have improper design of the system.
The best way to fix this is to use server pagination instead of loading all the data on page load. If you don't want to change design of your application there are some workarounds which maybe help you:
Map your array manually:
var data = storage.transactions();
var mappedData = ko.utils.arrayMap(data , function(item){
return ko.mapping.fromJS(item);
});
var transactionList = ko.observableArray(mappedData);
Map array asynchronously. I have written a function that processes array by portions in another thread and reports progress to the user:
function processArrayAsync(array, itemFunc, afterStepFunc, finishFunc) {
var itemsPerStep = 20;
var processor = new function () {
var self = this;
self.array = array;
self.processedCount = 0;
self.itemFunc = itemFunc;
self.afterStepFunc = afterStepFunc;
self.finishFunc = finishFunc;
self.step = function () {
var tillCount = Math.min(self.processedCount + itemsPerStep, self.array.length);
for (; self.processedCount < tillCount; self.processedCount++) {
self.itemFunc(self.array[self.processedCount], self.processedCount);
}
self.afterStepFunc(self.processedCount);
if (self.processedCount < self.array.length - 1)
setTimeout(self.step, 1);
else
self.finishFunc();
};
};
processor.step();
};
Your code:
var data = storage.transactions();
var transactionList = ko.observableArray([]);
processArrayAsync(data,
function (item) { // Step function
var transaction = ko.mapping.fromJS(item);
transactionList().push(transaction);
},
function (processedCount) {
var percent = Math.ceil(processedCount * 100 / data.length);
// Show progress to the user.
ShowMessage(percent);
},
function () { // Final function
// This function will fire when all data are mapped. Do some work (i.e. Apply bindings).
});
Also you can try alternative mapping library: knockout.wrap. It should be faster than mapping plugin.
I have chosen the second option.
Mapping is not magic. In most of the cases this simple recursive function can be sufficient:
function MyMapJS(a_what, a_path)
{
a_path = a_path || [];
if (a_what != null && a_what.constructor == Object)
{
var result = {};
for (var key in a_what)
result[key] = MyMapJS(a_what[key], a_path.concat(key));
return result;
}
if (a_what != null && a_what.constructor == Array)
{
var result = ko.observableArray();
for (var index in a_what)
result.push(MyMapJS(a_what[index], a_path.concat(index)));
return result;
}
// Write your condition here:
switch (a_path[a_path.length-1])
{
case 'mapThisProperty':
case 'andAlsoThisOne':
result = ko.observable(a_what);
break;
default:
result = a_what;
break;
}
return result;
}
The code above makes observables from the mapThisProperty and andAlsoThisOne properties at any level of the object hierarchy; other properties are left constant. You can express more complex conditions using a_path.length for the level (depth) the value is at, or using more elements of a_path. For example:
if (a_path.length >= 2
&& a_path[a_path.length-1] == 'mapThisProperty'
&& a_path[a_path.length-2] == 'insideThisProperty')
result = ko.observable(a_what);
You can use typeOf a_what in the condition, e.g. to make all strings observable.
You can ignore some properties, and insert new ones at certain levels.
Or, you can even omit a_path. Etc.
The advantages are:
Customizable (more easily than knockout.mapping).
Short enough to copy-paste it and write individual mappings for different objects if needed.
Smaller code, knockout.mapping-latest.js is not included into your page.
Should be faster as it does only what is absolutely necessary.

Resources