ordinal axis not updating on data filtering in dc.js - dc.js

I have 4 charts in dc.js. One of the charts has ordinal data displayed. I want the chart to display the data selected by the user which is working fine. But I want the axis to display ordinal values of only the selected data. Is there any way to do so. I am using
.x(d3.scale.ordinal().domain(group.all()
.map(function(d){
return d.key; })))
and setting
.elasticX(true);

You don't need to set the ordinal domain yourself in dc.js 2.0 and greater.
What is probably happening here is that the bins are still there but they have value zero - crossfilter doesn't remove empty bins.
You might try wrapping your group with this:
function remove_empty_bins(source_group) {
return {
all:function () {
return source_group.all().filter(function(d) {
return d.value != 0;
});
}
};
}
https://github.com/dc-js/dc.js/wiki/FAQ#filter-the-data-before-its-charted

Related

dc.js Grouping for Bubble Chart Removing from wrong groups

I'm trying to create a bubble chart with dc.js that will have a bubble for each data row and will be filtered by other charts on the same page. The initial bubble chart is created correctly, but when items are filtered from another chart and added or removed from the group it looks like they are being applied to the wrong group. I'm not sure what I'm messing up on the grouping or dimensions. I've created an example fiddle here
There's simple pie chart to filter on filterColumn, a bubble chart that uses identifer1, a unique field, as the dimension and xVal, yVal, and rVal to display the data, and a dataTable to display the current records.
I've tried other custom groups functions, but switched to the example from the FAQ and still had problems.
var
filterPieChart=dc.pieChart("#filterPieChart"),
bubbleChart = dc.bubbleChart('#bubbleChart'),
dataTable = dc.dataTable('#data-table');
var
bubbleChartDim=ndx.dimension(dc.pluck("identifier1")),
filterPieChartDim=ndx.dimension(dc.pluck("filterColumn")),
allDim = ndx.dimension(function(d) {return d;});
var filterPieChartGroup=filterPieChartDim.group().reduceCount();
function reduceFieldsAdd(fields) {
return function(p, v) {
fields.forEach(function(f) {
p[f] += 1*v[f];
});
return p;
};
}
function reduceFieldsRemove(fields) {
return function(p, v) {
fields.forEach(function(f) {
p[f] -= 1*v[f];
});
return p;
};
}
function reduceFieldsInitial(fields) {
return function() {
var ret = {};
fields.forEach(function(f) {
ret[f] = 0;
});
return ret;
};
}
var fieldsToReduce=['xVal', 'yVal', 'rVal'];
var bubbleChartGroup = bubbleChartDim.group().reduce(
reduceFieldsAdd(fieldsToReduce),
reduceFieldsRemove(fieldsToReduce),
reduceFieldsInitial(fieldsToReduce)
);
filterPieChart
.dimension(filterPieChartDim)
.group(filterPieChartGroup)
...
;
bubbleChart
.dimension(bubbleChartDim)
.group(bubbleChartGroup)
.keyAccessor(function (p) { return p.value.xVal; })
.valueAccessor(function (p) { return p.value.yVal; })
.radiusValueAccessor(function (p) { return p.value.rVal; })
...
;
This was a frustrating one to debug. Your groups and reductions are fine, and that's the best way to plot one bubble for each row, using a unique identifier like that.
[It's annoying that you have to specify a complicated reduction, when the values will be either the original value or 0, but the alternatives aren't much better.]
The reductions are going crazy. Definitely not just original values and zero, some are going to other values, bigger or negative, and sometimes clicking a pie slice twice does not even return to the original state.
I put breakpoints in the reduce functions and noticed, as you did, that the values were being removed from the wrong groups. How could this be? Finally, by logging bubbleChartGroup.all() in a filtered handler for the pie chart, I noticed that the groups were out of order after the first rendering!
Your code is fine. But you've unearthed a new bug in dc.js, which I filed here.
In order to implement the sortBubbleSize feature, we sort the bubbles. Unfortunately we are also sorting crossfilter's internal array of groups, which it trusted us with. (group.all() returns an internal data structure which must never be modified.)
The fix will be easy; we just need to copy the array before sorting it. You can test it out in your code by commenting out sortBubbleSize and instead supplying the data function, which is what it does internally:
bubbleChart.data(function (group) {
var data = group.all().slice(0);
if (true) { // (_sortBubbleSize) {
// sort descending so smaller bubbles are on top
var radiusAccessor = bubbleChart.radiusValueAccessor();
data.sort(function (a, b) { return d3.descending(radiusAccessor(a), radiusAccessor(b)); });
}
return data;
});
Notice the .slice(0) at the top.
Hope to fix this in the next release, but this workaround is pretty solid in case it takes longer.
Here is a fiddle demonstrating the workaround.

dc.js ignore results later than today

In DC.JS I have a rowChart of Top 10 variance of A minus B with the following dim/group:
.dimension(dateDim)
.group(grp)
Relevant Variables are:
var dateDim = ndx.dimension(function (d) { return d.DATE; });
var dateFormat = d3.time.format("%d/%m/%Y");
For the group, this works fine:
var grp= dateDim.group().reduceSum(function(d) {return Math.abs(d.A - d.B);});
However, what I'd like to do is only show items that are up to today only.
I tried the function below but this is not working. It may just be syntax.
Any Ideas?
var grp2= dateDim.group().reduceSum(function(d) {
if (dateDim > dateFormat(today))return dateDim; else return Math.abs(d.A- B.plan);
Thanks, stutray
This is addressed in the dc.js FAQ: https://github.com/dc-js/dc.js/wiki/FAQ#how-do-i-filter-the-data-before-its-charted
So ... originally I said the above. But what you actually want to do is filter out records that don't match a particular pre-defined filter so that they are not aggregated into your groups for this chart? In that case you need to use custom filter functions and I would recommend using a helper library like Reductio, which provides a filter function.
var dAct = reductio().filter(function(d) {
// Here `d` is the actual data record. Return a boolean by testing
// d against your filter criteria. Return true to include the record and
// false to exclude it.
return d.STARTDATE !== "10\/Sep\/2016";
})
.sum(function(d) {return Math.abs(d.A - d.B);})(dateDim.group());
If you use Reductio, you'll also need to use a valueAccessor function on your chart in dc.js:
rChart
...
.valueAccessor(function(d) { return d.value.sum; })
Updated example on Codepen: http://codepen.io/anon/pen/oxrPJv
Documentation: https://github.com/crossfilter/reductio#aggregations-standard-aggregations-reductio-b-filter-b-i-filterfn-i-

Motion chart using nvd3.js

I want to implement motion charts similar to http://bost.ocks.org/mike/nations/ but using nvd3.js. I took the example scatter/bubble chart from nvd3.org and added one additional key to the data. I use this key as a frame number for the animation. you can find it here: http://jsfiddle.net/bartiosze/ny6vnznn/.
My idea was to change the data for the chart in a function called from .tween:
function drawFrame(idx) {
var data = _.where(dataset, {
index: idx
});
d3.select('#chart svg').datum(data)
.transition().duration(100).call(window.chart.update);
}
function tweener() {
var idx = d3.interpolateNumber(0, 9);
return function (t) {
drawFrame(Math.round(idx(t)));
};
}
//should this work?
d3.select('#chart svg')
.transition().duration(10000).ease("linear").tween(".nv-clip-points", tweener);
It might be a general d3 question, but am I missing something?

nvd3.js line chart -- add vertical lines

I need to add several vertical lines (say 10 or 20) to an nvd3 line chart.
The question here suggests adding a series for this, but I would need to add 20 series, overcrowding the legend and the interactive tooltip.
From what I understand this can't be done out-of-the-box (please correct me if I'm wrong), so my question is what is the easiest way of doing this:
Add D3 lines to the DOM (how would I go about scale, positioning them horizontally, etc?)
Add generic support for this in nvd3
Add support for hiding specific series from the legend and from the tooltip, and add 20 series
Any other idea?
Well, it turns out that it wasn't that hard. I chose option #3, and the following code changes to nv.d3.js got the job done:
In the legend model, change
function chart(selection) {
selection.each(function(data) {
... to
function chart(selection) {
selection.each(function(dataUnfiltered) {
var data = dataUnfiltered.filter(function (d) {
return !d.disableLegend;
});
and in the lineChart model, change:
interactiveLayer.dispatch.on('elementMousemove', function(e) {
lines.clearHighlights();
var singlePoint, pointIndex, pointXLocation, allData = [];
data
.filter(function(series, i) {
series.seriesIndex = i;
return !series.disabled;
})
... to
interactiveLayer.dispatch.on('elementMousemove', function(e) {
lines.clearHighlights();
var singlePoint, pointIndex, pointXLocation, allData = [];
data
.filter(function(series, i) {
series.seriesIndex = i;
return !series.disabled && !series.disableTooltip;
})
(Obviously this second change would have to be done to each chart model you want to support, say also cumulativeLineChart and `stackedAreaChart).
This will enable you to specify, in addition to color, key, values, etc. also disableTooltip: true and/or disableLegend: true.
Hope this helps someone.

Filtering crossfilter dimension using dropdown

I am trying to filter my row chart by selecting values in a dropdown.
I do this by creating a dimension and applying a filter to that dimension based on selected values in the dropdown.
The row chart has to display average values, to do this I created ReduceAdd/Remove/Initial functions.
The average values are working as intended.
However, when I filter my dimension used for the dropdown, it seems to add duplicate values to my row chart.
Example code: http://jsfiddle.net/sy9xA/4/
var dropDownFilterDimension;
function ReduceAdd(p, v) {
var value = v.value;
++p.countAvg;
p.totalAvg += value;
return p;
}
function ReduceRemove(p, v) {
var value = v.value
--p.countAvg;
p.totalAvg -= value;
return p;
}
function ReduceInitial() {
return {countAvg: 0, totalAvg: 0};
}
function filterDropdown(dropDownID){
dropDown = document.getElementById(dropDownID);
dropDownFilterDimension.filterAll();
values = $(dropDown).val();
if( values != null ){
dropDownFilterDimension.filter(function(d) { if (values.indexOf(d) > -1) {return d;} });
}
dc.redrawAll();
}
Here for example, when "banana" is selected for filtering. ReduceRemove is called for all non selected values, but in addition ReduceAdd is called for banana (even though it is already in it).
So now when after selecting banana, apple is selected. Banana still has some value.
Can someone explain why this is happening? Or how i can avoid this from happening?
Thanks in advance!
Robert
I don't have fully understand what you are doing also because the jsfddle doesn't show nothing more than the js.
I can tell how I do:
you have a dimension associated to a dc chart (ex chartDim)
you have a group that calculate the average associated to a dc chart (ex averageGroup)
you have a dropdown with a set of values to be use as filter (#myDropDown)
d3.select('#myDropDown')
.on('change', function(){
chartDim.filter(this.value)
dc.redrawAll();
})
the call of dc.redrawAll() will update not only the chart but also the averageGroup and everything should go fine. If the dropDown has is own dimension just filter this one instead of the chartDim

Resources