Dc.js charts interactivity fails when charts are dynamically added by users.Pls see description for more details - dc.js

I am looking for a way to get Dc.js charts to be added to a dashboard by user choice of chart in a palette.
End of the day I want the user to be able to do 2 things.
Part 1: User should be able to select and add various charts from charts palette into a dashboard by selecting respective target variable(x,y fields) which are nothing but fields in csv, one by one for each chart.
Part 2 : These charts which form a kind of dashboard should be reactive or interactive(change in one chart reflects change in all other charts) which is not the case now.
I have already achieved the 1st part by below code , but the main challenge is 2nd part. Charts are no more coordinated visualizations(by change of a pie in one chart , other charts do not reflect the change)
I have added all charts in 1 function with a switch case statement. Everything works fine up to adding the charts to the dashboard or respective div but this does not make the charts reactive(click on one chart does not affect other charts).
Little note about the below code and its intentions.
-I am dynamically creating div’s and assigning ids.
-here num in my function is a random number assigned in calling function to make sure there are no duplicate divs with same id.
-Targetvar is a field chosen by the user.
-arrayofobject is the actual data.
-ctype is the charttype chosen by user.I have declared 2 charts(pie and row) for example sake.Their are more charts in the palette for user to choose.
Please let me know what change I need to make to get charts be reactive.
function charts_all(targetvar1,arrayOfObject,num,ctype){
var selname="#divid"+num;
switch(ctype){
case "pie":
var SevPieChart=dc.pieChart(selname);
data=arrayOfObject;
var ndx = crossfilter(data);
var all = ndx.groupAll();
var targetvariable1=targetvar1;
var SevDim = ndx.dimension(function(d){ return d[targetvariable1]; })
;
var Sevgroup=SevDim.group();
SevPieChart
.height(400)
.width(1200)//1200
.radius(150)
.innerRadius(20)
.dimension(SevDim)
.group(Sevgroup)
.transitionDuration(1000)
.legend(dc.legend().x(950).y(40)) //LEGEND CODE x=950
.externalLabels(0)
.renderLabel(true)
.label(function(d) { return d.key +" (" + Math.floor(d.value / all.value() * 100) + "%)"; })
.title(function(d){return d.Sevgroup;});
//dc.renderAll();
alldefault();
break;
case "row":
var Rowchart = dc.rowChart(selname);
data=arrayOfObject;
var ndx = crossfilter(data);
var all = ndx.groupAll();
var targetvariable1=targetvar1;
var SaDim = ndx.dimension(function(d){ return d[targetvariable1]; })
;
var SAgroup=SaDim.group();
Rowchart
.width(1000)
.height(1000)
.dimension(SaDim)
.group(SAgroup)
.renderLabel(true)
.label(function(d) { return d.key+" (" + Math.floor(d.value / all.value() * 100) + "%)"; })
.data(function(group){ return group.top(40);}) ;
//Rowchart.filter = function() {};
//dc.renderAll();
alldefault();
break;
default:
}
dc.renderAll();
}

Related

How can AMCharts 4 be scrolled horizontally from javascript code?

I am trying to get the chart scrolled horizontally from externally triggered event calls like when a button is clicked elsewhere on the page. I am equally able to get the start and indexes of where i want the chart horizontally scrolled to. The chart's DateAxis is defined as below:
this.categoryAxis = chart.xAxes.push(new am4charts.DateAxis());
this.categoryAxis.dataFields.date = "value_date";
this.categoryAxis.cursorTooltipEnabled = false;
this.categoryAxis.start = 0.7;
this.categoryAxis.keepSelection = true;
And the function that is called by an external event is defined as below:
function(res){
let cpre = res.tree.split(',');
cpre.forEach((el, i) => {
cpre[i] = Number(el);
});
let visibleEl = this.data.findIndex((el, i) => {
return isEqual(cpre, el.graphIndex);
});
console.log(visibleEl);
//Trying to display 6 date points at any given time, not working.
let startIndex = visibleEl - 6 < 0 ? 0 : visibleEl - 6;
this.categoryAxis.zoomToIndexes(visibleEl, startIndex);
}
This line this.categoryAxis.zoomToIndexes(visibleEl, startIndex); currently zooms to the target indexes, but also zooms in the map which is not the desired effect. I only want to have it scroll to the desired position without zooming, thanks.
You can try to use valueAxis.strictMinMax (docs).
valueAxis.strictMinMax = true;
If that doesn't work try to use dateAxis.zoomToDates() instead of zoomToIndexes (docs).

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-

ordinal axis not updating on data filtering in 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

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.

How do I prevent selection of data outside my selected range from highlighting when using crossfilter.js?

I'm new to crossfilter.js. Whenever I used the range selector I get the strange blocky highlighted area on my bar chart outside of my filter range. I can't figure out what I'm doing wrong.
How I can prevent this blocky section from highlighting outside my range?
d3.csv("mydata.csv", function(data) {
data.forEach(function(d) {
d.conf = d3.round(+d.Conf,1);
console.log(d.conf);
});
var facts = crossfilter(data); // Put data into crossfilter
var confDim = facts.dimension(function (d) { return d.conf;}); // conf # filter
var confTotal = confDim.group().reduceCount(function (d) { return d.conf;});
var confChart = dc.barChart("#conf-chart");
confChart
.width(500).height(200)
.dimension(confDim)
.group(confTotal,"Confidence Number")
.x(d3.scale.linear().domain([0,5]).range([10,400]))
.yAxisLabel("Number of data points")
.brushOn(true);
dc.renderAll();
});
This line that you've commented as being a filter is actually just setting up the dimension in its entirety:
var confDim = facts.dimension(function (d) { return d.conf;}); // conf # filter
You need to define a filter using one of the filter functions. See the documentation here.
Be careful to read the documentation for dimension.group carefully as it contains a gotcha where the filter on the dimension being grouped won't be applied. To work around this you may need to actually define 2 dimensions that are identical and use one for filtering and the other for grouping.

Resources