dc.js pie-chart combine 2 columns - dc.js

I have a table.
year,continent,state,export_value,import_value,all
2013,Африка,Angola,94061754,220518.54,94282272.54
2013,Африка,Burundi,4136818,1239175.91,5375993.91
2013,Африка,Benin,3251643.49,65421.6,3317065.09
I need to make a pie chart using dc.js:
Here is what I tried:
impexDim = ndx.dimension(function (d) {return ((d.export_value > 0 ? 'Export' : 'Import')
&& (d.import_value > 0 ? 'Import' : 'Export')) ;}),
groupeimpexDim = impexDim.group().reduceSum(function(d) {return +d.all}),
How do I display the columns export_value and import_value as slices of the pie chart?

Related

D3.js dynamically switching between number and dollar formats on tick marks

I have data that show both the number of items sold as well as the value of those items. I have a dynamic and interactive line chart that switches between the two data sets. I'd like to format the tick marks on the axis so that dollar signs appear on the axis where appropriate.
var y = d3.scaleLinear().range([height, 0]);
var yAxis = d3.axisLeft().scale(y)
svg.append("g")
.attr("class","Yaxis")
y.domain([0, d3.max(data, function(d) { return d.number }) ]);
svg.selectAll(".Yaxis")
.transition()
.duration(3000)
.call(yAxis);`
The entire pen is available here: https://codepen.io/cyrusobrien/full/NWdPRLp
Replace D3 tickFormat with your custom one:
const d3Format = d3.format(".2s");
const customFormat = value => `$${d3Format(value)}`;
const yAxis = d3.axisLeft().scale(y)
.tickFormat(customFormat)

dc.js Adding count to the xAxis label

This is related to the dc.js boxPlot example. Is there an easy way to add the number of data values to each xAxis label?
exp-1 [10]
I know I can configure the xAxis label by:
chart.xAxis().tickFormat(function(k) {return k + ' []';});
But I only have access to the key and not the value count.
I would do this by keeping a map from keys to counts, refreshing it before the chart is rendered or redrawn:
var counts = {};
function get_counts() {
speedArrayGroup.all().forEach(function(kv) {
counts[kv.key] = kv.value.length;
});
}
chart.on('preRender', get_counts)
.on('preRedraw', get_counts);
Now that we're sure counts is initialized whenever the axis is drawn, we can use it in the tickFormat:
chart.xAxis().tickFormat(function(k) {return k + ' [' + counts[k] + ']';});
Not so interesting with the standard example, but it works:

D3 stacked bar chart: unique bar for each row (stack only one row)

I'm quite new to D3 and I'm trying to make a stacked bar chart (or column chart) with unique bars for each row (each observation) in the dataset.
The problem I have encountered is: if there's more than one row with the same value used for the y axes (in my case, in data.csv, in the column "seq", "3" and "4" appear twice), then all data with the same name (from different rows) will be stacked together like this:
data.csv
seq,Idle Time,Answer Time
2,95,4
1,0,3
3,22,3
4,0,4
6,43,3
5,0,2
8,30,1
7,0,3
4,20,5
3,0,8
But what I'm trying to do is to make one bar for each row, despite the identical values of d.seq (so that there will be two bars for d.seq=3 and d.seq=4)
The full code I am working on now is here
Any suggestions will be appreciated, thanks!
You can plot based on the index of your data array, rather than on the "seq" value. The index will be unique. http://plnkr.co/edit/vtsfWSB7etegM9VfI6mM?p=preview
I've just updated two lines
line 86:
y.domain(data.map(function(d, i) { return i; }));
line 112:
.attr("transform", function(d, i) { return "translate(0," + y(i) + ")"; });
If you still want the y-tick label to show the value in "seq", have a look here where you can find out more about axes and how to apply custom labels:
https://github.com/mbostock/d3/wiki/SVG-Axes
EDIT
http://plnkr.co/edit/6sNaLwiSSm7aU66qzIOK?p=preview
You need to move the yAxis definition within the d3.csv success function, and then reference the data array like so to label the axis how you would like:
var yAxis = d3.svg.axis()
.scale(y)
.tickFormat(function(i) {return data[i].seq; })
.orient("left");

dc.js apply some, but not all chart selections to numberDisplay, while maintaining the interactions between charts in place

I have a dataset (data) with the following row/column structure:
Date Category1 Category2 Revenue
30/12/2014 a x 10
30/12/2014 b x 15
31/12/2014 a x 11
1/1/2015 a x 13
2/1/2015 a x 14
2/1/2015 b x 9
2/1/2015 c z 4
...
Based on data I create a couple of dimensions and groups:
var ndx = crossfilter(data);
var cat1Dim = ndx.dimension(function(d) {return d.Category1;});
var revenuePerCat1 = cat1Dim.group().reduceSum(function(d) { return d.Revenue; });
var cat2Dim = ndx.dimension(function(d) {return d.Category2;});
var revenuePerCat2 = cat2Dim.group().reduceSum(function(d) { return d.Revenue; });
var dateDim = ndx.dimension(function(d) { return d.Date; });
var revenuePerDate = dateDim.group().reduceSum(function(d) { return d.Revenue; });
Next, I create the following charts:
a line chart; dimension = dateDim, group = revenuePerDate
a pie-chart; dimension = cat1Dim, group = revenuePerCat1
a pie-chart; dimension = cat2Dim, group = revenuePerCat2
Besides the charts I would also like to show the year-to-date value of the revenues via a numberDisplay. Initially I thought to achieve this by adding a simple if condition to the reduceSum function where I reduce the data to contain only items of the current year, like so:
var ytdRev = ndx.groupAll().reduceSum(function(d) { if(d.Date.getFullYear() == curYear) {return d.Revenue;} else{return 0;}});
A box containing a numberDisplay item is then called by:
box_ytd
.formatNumber("$,.4s")
.valueAccessor(function(d) {return Math.round(d * 1000) / 1000; })
.group(ytdRev);
This works perfectly fine if one selects one of the categories displayed in the pie-charts, but is incorrect when one also starts to filter date ranges in the line chart. Namely, instead of a year-to-date value, actually a 'date-to-date' value for the specific selection will be returned. Although this behaviour is correct from a technical perspective, I would like to know how I can instruct dc.js such that it will only take into account chart selections from a certain set of charts when rendering a numberDisplay. The selections made in the pie-charts should, however, both update the displayed selection in the line chart and the numberDisplay.
Ideally, I would like to use one crossfilter instance only, but I am open to any suggestions that involve a second crossfilter as well.
EDIT:
Based on Gordon's comment I played around with a custom reduce function. Instead of ndx.groupAll() I applied the following reduce function with a .groupAll() on the dimension level:
function reduceAdd(p,v) {
if(v.Date.getFullYear() == curYear)
p.revenue += +v.Revenue;
return p;}
function reduceRemove(p,v) {
if v.Date.getFullYear() == curYear)
p.revenue -= +v.Revenue;
return p;}
function reduceInitial() {
return {revenue:0 };}
var ytdRev = dateDim.groupAll().reduce(reduceAdd, reduceRemove, reduceInitial);
The .valueAccessor in the numberDisplay is changed from d.value.revenue to d.revenue:
box_ytd
.formatNumber("$,.4s")
.valueAccessor(function(d) {return Math.round(d.revenue * 1000) / 1000; })
.group(ytdRev);
The numberDisplay will now reflect the total value for the current year for each of the selections made in the pie-charts. Date selections will only affect the pie-charts' values; the numberDisplay shares the same dimension with the line chart and hence the numberDisplay is unaffected by any selections on that dimension.
Based on Gordon's comment I played around with a custom reduce function. Instead of ndx.groupAll() I applied the following reduce function with a .groupAll() on the dimension level:
function reduceAdd(p,v) {
if(v.Date.getFullYear() == curYear)
p.revenue += +v.Revenue;
return p;}
function reduceRemove(p,v) {
if v.Date.getFullYear() == curYear)
p.revenue -= +v.Revenue;
return p;}
function reduceInitial() {
return {revenue:0 };}
var ytdRev = dateDim.groupAll().reduce(reduceAdd, reduceRemove, reduceInitial);
The .valueAccessor in the numberDisplay is changed from d.value.revenue to d.revenue:
box_ytd
.formatNumber("$,.4s")
.valueAccessor(function(d) {return Math.round(d.revenue * 1000) / 1000; })
.group(ytdRev);
The numberDisplay will now reflect the total value for the current year for each of the selections made in the pie-charts. Date selections will only affect the pie-charts' values; the numberDisplay shares the same dimension with the line chart and hence the numberDisplay is unaffected by any selections on that dimension.

How to add labels to c3.js scatter plot graph?

Is it possible to add labels to scatter plot points in c3.js like in this google charts example?
https://google-developers.appspot.com/chart/interactive/docs/gallery/bubblechart#javascript
c3 doesn't support this currently - https://github.com/masayuki0812/c3/issues/481. But you can easily add the functionality - just loop through the chart series and points and add the labels as necessary.
var labels = [
['AA', 'BB', 'CC', 'DD', 'EE', 'FF', 'GG', 'HH'],
['ZA', 'ZB', 'ZC', 'ZD', 'ZE', 'ZF', 'ZG', 'ZH']
];
// series
var series = chart.internal.main
.selectAll('.' + c3.chart.internal.fn.CLASS.circles)[0];
// text layers
var texts = chart.internal.main
.selectAll('.' + c3.chart.internal.fn.CLASS.chartTexts)
.selectAll('.' + c3.chart.internal.fn.CLASS.chartText)[0]
series.forEach(function (series, i) {
var points = d3.select(series).selectAll('.' + c3.chart.internal.fn.CLASS.circle)[0]
points.forEach(function (point, j) {
d3.select(texts[i])
.append('text')
.attr('text-anchor', 'middle')
.attr('dy', '0.3em')
.attr('x', d3.select(point).attr('cx'))
.attr('y', d3.select(point).attr('cy'))
.text(labels[i][j])
})
});
Fiddle - http://jsfiddle.net/6phuuans/
Currently C3.js doesnt provide us with the option to add labels to a scatter plot chart. But the following method can be used to add responsive data labels:
After the chart is rendered (in the "onrendered" property of the chart), identify the data points ( tags) and add tags with the x and y coodinates picked from the relevant circle, as the tags sibling.
Code Snippet:
onrendered: function(){
// get the parent of the the <circles> to add <text as siblings>
var g = d3.selectAll('.c3-circles');
//Get all circle tags
var circles = d3.selectAll('circle')[0];
//go to each circle and add a text label for it
for(var i = 0; i < circles.length; i++){
//fetch x-coordinate
var x = $(circles[i])[0].cx;
//fetch y-coordinate
var y = $(circles[i])[0].cy;
//create and append the text tag
g.append('text')
.attr('y', y.baseVal.value - 15) // (-15) places the tag above the circle, adjust it according to your need
.attr('x', x.baseVal.value)
.attr('text-anchor', 'middle')
.attr('class', 'c3-text c3-text-'+i)
.text(data[i].<data point key>) // the text that needs to be added can be hard coded or fetched for the original data.
//Since I am using a JSON to plot the data, I am referencing it and using the key of the value to be shown.
}
}
This will add the label, but on resize , multiple data labels will be plotted. To handle that, on the charts resize, we must remove the previous data labels (in the "onresize" property of the chart).
Code Snippet:
onresize: function () {
$('.c3-shapes.c3-circles text').remove();
}

Resources