I am trying to create 2 charts one bar and one series which the bar will show total earnings per store and series will show multi line earnings per year.
Here is the jsfiddle https://jsfiddle.net/xc4bwgLj/
So when I click on Bar chart Store 1, I want in series chart to see for this store earning for 2017 and 2016 each on a new line. Currently the series chart show the total earnings for each store like bar chart.
Any idea how can I change series chart to show 2016 and 2017 earnings per store?
JsFiddle code:
// generate data
var data = [];
var n = 1000.;
for (var i = 0; i < n; i++) {
console.log(Math.floor(Math.random() * (1 - 0 + 1)) + 0);
data.push({
id: (Math.floor(Math.random() * (1 - 0 + 1)) + 0),
"i": i,
x: Math.random(),
"store_name": "Store"+(Math.floor(Math.random() * (1 - 0 + 1)) + 0),
"2017_earnings": Math.random()*110,
"2016_earnings": Math.random()*80
});
}
// do some crossfilter stuff
var cf = crossfilter(data),
series = cf.dimension(function(d) {
return [d.store_name, d.i];
}),
series_grouped = series
.group(function(d) {
console.log(d)
return [d[0], Math.floor(d[1] / 100.) * 100.];
})
.reduceSum(function(d) {
return d.x;
}),
id = cf.dimension(function(d) {
return d.store_name;
}),
id_grouped = id.group().reduceSum(function(d) {
return d.x;
});
// generate charts
var chart_width = 960,
chart_height = 200;
console.log(dc);
dc.seriesChart("#chart_a").height(chart_height).width(.74 * chart_width)
.chart(function(c) {
return dc.lineChart(c).renderArea(true)
.filterHandler(function(dimension, filter) {
if(filter[0]) {
dimension.filterFunction(function(d) {
return d[1] > filter[0][0] && d[1] < filter[0][1];
});
} else {
dimension.filterAll();
}
setTimeout(dc.redrawAll,0);
return filter;
});
})
.x(d3.scale.linear().domain([0, n]))
.dimension(series)
.group(series_grouped)
.seriesAccessor(function(d) {
return d.key[0];
})
.keyAccessor(function(d) {
return d.key[1];
})
.valueAccessor(function(d) {
return d.value;
}).legend(dc.legend().x(350).y(350).itemHeight(13).gap(5).horizontal(1).legendWidth(140).itemWidth(70));
dc.barChart("#chart_b").height(chart_height).width(.24 * chart_width)
.dimension(id)
.group(id_grouped)
.x(d3.scale.ordinal())
.xUnits(dc.units.ordinal)
.xAxis();
dc.renderAll();
Here's a different approach that keeps the shape of the source the same, but split the group for the use of the series chart. In my other answer I change the source data shape.
Whenever we need to preprocess data, e.g. to change the shape that crossfilter returns, we can use a fake group
We'll reduce both columns separately using an ordinary reduction of multiple fields:
series = cf.dimension(function(d) {
return d.i;
}),
series_grouped = series.group(function(k) {
return Math.floor(k / 100.) * 100.;
})
.reduce(
function(p, d) { // add
p[2016] += d['2016_earnings'];
p[2017] += d['2017_earnings'];
return p;
},
function(p, d) { // remove
p[2016] -= d['2016_earnings'];
p[2017] -= d['2017_earnings'];
return p;
},
function() {
return {2016: 0, 2017: 0};
}),
Then this split group will take the names of two fields, and will split each bin into two, using the field name as the first part of the multikey:
function split_group(group, field1, field2) {
return {
all: function() {
var ret = [];
group.all().forEach(function(kv) {
ret.push({
key: [field1, kv.key],
value: kv.value[field1]
});
ret.push({
key: [field2, kv.key],
value: kv.value[field2]
});
});
return ret;
}
}
}
Use it like this:
series_split = split_group(series_grouped, 2016, 2017)
// ...
chart
.group(series_split)
Hard to tell with the random number generation, but I think the result is identical to the other answer. Just a different approach.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With