I am trying to remove the empty bins or shall I say the rows with 0 values in a dc.js dataTable.
I have written my code based on the examples & snippets provided in the dc.js documentation's FAQs to remove empty bins but I am still not being able to achive the desired results.
I am trying to group my data using a fake group and then sorting the table by descending order of the total amount.
var data = [{salesman: "AB",name: "John",total: 190.1,type: "Tab"},
{salesman: "CD",name: "David",total: 190,type: "Tab"},
{salesman: "EF",name: "Elton",total: 300.5,type: "Visa"},
{salesman: "AB",name: "John",total: 90.0,type: "Tab"},
{salesman: "EF",name: "Elton",total: 90,type: "Tab"},
{salesman: "CD",name: "David",total: 90,type: "Tab"}];
var ndx = crossfilter(data);
var salesManDim = ndx.dimension(function(d) {
return d.salesman;
});
var salesmanSumGroup = salesManDim.group().reduceSum(function(d) {
return (Math.round((d.total) * 100) / 100);
});
var salesmanChart = dc.pieChart("#chart");
salesmanChart
.width(200)
.height(150)
.slicesCap(10)
.innerRadius(30)
.dimension(salesManDim)
.group(salesmanSumGroup);
var salesMan_TypeDim = ndx.dimension(function(d) {
return d.salesman + "/" + d.type;
});
var groupedDimension = salesMan_TypeDim.group().reduce(
function(p, v) {
p.TOTAL += +(Math.round((v.total) * 100) / 100);
p.SALESMAN = v.salesman + " - " + v.name;
p.TYPE = v.type;
return p;
},
function(p, v) {
p.TOTAL -= +(Math.round((v.total) * 100) / 100);
p.SALESMAN = v.salesman + " - " + v.name;;
p.TYPE = v.type;
return p;
},
function() {return { TOTAL: 0, SALESMAN: "",TYPE: ""};});
var rank = function(p) {
return p.key.substr(p.key.lastIndexOf("/") + 1);
};
function remove_empty_bins(source_group) {
function non_zero_pred(d) {
return d.value.TOTAL !== 0;
}
return {
top: function(n) {
return source_group.top(Infinity).filter(non_zero_pred).slice(0, n);
}
};
}
dc.dataTable(".dc-data-table")
.dimension(remove_empty_bins(groupedDimension))
.group(rank)
.size(Infinity)
.columns([
function(d) {
return d.value.SALESMAN;
},
function(d) {
return (Math.round((d.value.TOTAL) * 100) / 100);
}
])
.sortBy(function(d) {
return d.value.TOTAL;
})
.order(d3.descending);
dc.renderAll();
dc.redrawAll();
The remove empty bins function works fine when I check in the debugger but still somehow the 0 values are not filtered. Is it because of the way my keys are defined ? To best explain my scenario here is a jsFiddle for the above example
It's surprising that it comes up in such a simple example, but floating point addition and subtraction can often lead to values that are not zero when you would expect them to be.
It happens especially often with decimal numbers. 0.1 is unrepresentable in binary floating point numbers, and they are not necessarily associative or distributive.
We can add the following line to diagnose the problem:
.on('preRedraw', function(chart) {
console.log(chart.dimension().top(Infinity).map(x=>x.value.TOTAL));
})
And indeed, when we click on CD we see:
[200, 370.3, 2.842170943040401e-14]
So write non_zero_pred
like this:
function non_zero_pred(d) {
return Math.abs(d.value.TOTAL) > 0.0001;
}
And the zeros are gone: https://jsfiddle.net/3fovopxp/7/
I'll go fix the FAQ now...