Showing the percentage value in pie chart - dc.js

console.log(group1.top(Infinity));
gives me:
0:
key: "M"
value: {Recovered: 122, Hospitalized: 38922, Deceased: 641, Migrated_Other: 1}
__proto__: Object
1:
key: "F"
value: {Recovered: 82, Hospitalized: 19215, Deceased:.....
In this code:
.label(function(d) {
return d.key+": " + (d.value[type]/<?> * 100).toFixed(2) + "%";
})
if the type is Recovered then I want the sum of recovered values (122+82) in place of <?> i.e d.value["Recovered"]/(122+82)
I am just stuck at the syntax how to take the sum of values of the matched type in place of <?>.
I can only think of
group1.all()[0].value['Recovered']+group1.all()[1].value['Recovered']
Is there any better way?
Working Code:
https://blockbuilder.org/ninjakx/61d405cb0aaeda3be960e836f803cdd5

There are a couple of ways to do this. One is to compute the sum, manually as you show above, or by using a groupAll object.
The other way, which the pie chart example uses, is to use the angle data saved in the pie slices:
// workaround for #703: not enough data is accessible through .label() to display percentages
.on('pretransition', function(chart) {
chart.selectAll('text.pie-slice').text(function(d) {
return d.data.key + ' ' + dc.utils.printSingleValue((d.endAngle - d.startAngle) / (2*Math.PI) * 100) + '%';
})
});
As noted, there is not enough data supplied to .label() to use this approach, so this is applied after drawing, using the full data objects.

Related

PieChart with all values joined

I'm newbie and I'm working on a dashboard. I want to show with a pie chart the total value of one dimension (100% when all the registers all selected, and change it with the other filters). I've tried it with groupAll() but it doesn't work. This code works but it shows the groups separate. How can I do this? Thanks a lot!!!
CSV
CausaRaiz,probabilidad,costeReparacion,costePerdidaProduccion,impacto,noDetectabilidad,criticidad,codigo,coste,duracion,recursosRequeridos
PR.CR01,2,1.3,1,1,1,2,AM.PR.01,1,2,Operarios
PR.CR02,4,2.3,3,2.5,2,20,AM.PR.02,2,3,Ingenieria
PR.CR03,4,3.3,4,3.5,4,25,AM.PR.03,3,4,Externos
PR.CR04,2,2.7,2,2,2,8,AM.PR.04,3,4,Externos
FR.CR01,3,2.9,3,2.5,3,22,AM.FR.01,4,5,Ingenieria
FR.CR02,2,2.1,2,2,2,8,AM.FR.02,4,3,Operarios
FR.CR03,1,1.7,1,1,1,1,AM.FR.03,3,5,Operarios
RF.CR01,1,1.9,2,2,3,6,AM.RF.01,3,5,Externos
RF.CR02,3,3.5,4,3.5,4,20,AM.RF.02,4,4,Ingenieria
RF.CR03,4,3.9,4,3.5,4,25,AM.RF.03,4,5,Operarios
Code working
var pieCri = dc.pieChart("#criPie")
var criDimension = ndx.dimension(function(d) { return +d.criticidad; });
var criGroup =criDimension.group().reduceCount();
pieCri
.width(270)
.height(270)
.innerRadius(20)
.dimension(criDimension)
.group(criGroup)
.on('pretransition', function(chart) {
chart.selectAll('text.pie-slice').text(function(d) {
return d.data.key + ' ' + dc.utils.printSingleValue((d.endAngle - d.startAngle) / (2*Math.PI) * 100) + '%';
})
});
pieCri.render();
I can show the total percentage with a number:
var critTotal = ndx.groupAll().reduceSum(function(d) { return +d.criticidad; });
var numbCriPerc = dc.numberDisplay("#criPerc");
numbCriPerc
.group(critTotal)
.formatNumber(d3.format(".3s"))
.valueAccessor( function(d) { return d/critTotalValue*100; } );
But I prefer in a pie chart to show the difference between all the registers and the selection.
If I understand your question correctly, you want to show a pie chart with exactly two slices: the count of items included, and the count of items excluded.
You're on the right track with using groupAll, which is great for taking a count of rows (or sum of a field) based on the current filters. There are just two parts missing:
finding the full total with no filters applied
putting the data in the right format for the pie chart to read it
This kind of preprocessing is really easy to do with a fake group, which will adapt as the filters change.
Here is one way to do it:
// takes a groupAll and produces a fake group with two key/value pairs:
// included: the total value currently filtered
// excluded: the total value currently excluded from the filter
// "includeKey" and "excludeKey" are the key names to give to the two pairs
// note: this must be constructed before any filters are applied!
function portion_group(groupAll, includeKey, excludeKey) {
includeKey = includeKey || "included";
excludeKey = excludeKey || "excluded";
var total = groupAll.value();
return {
all: function() {
var current = groupAll.value();
return [
{
key: includeKey,
value: current
},
{
key: excludeKey,
value: total - current
}
]
}
}
}
You'll construct a groupAll to find the total under the current filters:
var criGroupAll = criDimension.groupAll().reduceCount();
And you can construct the fake group when passing it to the chart:
.group(portion_group(criGroupAll))
Note: you must have no filters active when constructing the fake group this way, since it will grab the unfiltered total at that point.
Finally, I noticed that the way you were customizing pie chart labels, they would be shown even if the slice is empty. That looked especially bad in this example, so I fixed it like this:
.on('pretransition', function(chart) {
chart.selectAll('text.pie-slice').text(function(d) {
return d3.select(this).text() && (d.data.key + ' ' + dc.utils.printSingleValue((d.endAngle - d.startAngle) / (2*Math.PI) * 100) + '%');
})
});
This detects whether the label text is empty because of minAngleForLabel, and doesn't try to replace it in that case.
Example fiddle based on your code.

D3v4 - update / cycle through data

I have a set of datapoints, each point has multiple y values
x: number;
y: Array<{key:string, value:number}>;
I use the following function to get the correct data points for a key:
function dataForKey(data, key)
{
return data.map(function(d)
{
return {
x: d.x,
y: findCorrespondingDataPoint(d.y, key)
};
});
}
I am struggling however to make D3 walk through all the available datapoints (by key) and transition from 1 to the next in a given sequence.
I have seen a few examples using transition().tween(..) and data interpolation, but I don't think that's what I want, since I just want to transition from one value to the next.
Something along these lines:
// pseudo code
for(key in keys)
{
d3.selectAll(".dot")
.transition(t)
.data(dataForKey(key))
.waitForTransitionToFinish()
}
Any advice on how to accomplish this using D3v4?

How to choose a different property instead of `key` in nvd3

All nvd3 examples (which I've found) look like this:
return [
{
values: sin, //values - represents the array of {x,y} data points
key: 'Sine Wave', //key - the name of the series.
color: '#ff7f0e' //color - optional: choose your own line color.
}, ...]
I want to use a function which would use different keys based on the size of the chart / drawing area.
So if I have a large drawing area I have space for the whole name Sine Wave and in small areas I'd just display sin.
Yes, I could go through the series and update the key property, but it would be easier to put all the necessary data into the object and choose on render time, which key should be used.
You can use chart.legend.key()
chart.legend.key(function(d){
// Return the key you want for series d here based on screen realestate
// FYI: The default would return d.key
});
So it could look something like this:
function setLegendKeys(d){
var width = document.body.clientWidth;
var abbreviations = {
'Sine Wave': 'sin',
'Cosine Wave': 'cos'
};
if (width < 500){
return abbreviations[d.key];
}
return d.key;
}
chart.legend.key(setLegendKeys)
See this Plunk for a live example:
http://plnkr.co/edit/lisKuZ5ivj25QhyjlQUW?p=preview

d3 selectAll().data() just returns data in first element. why

I am trying to get the data in all the matching element using d3. I have following code
d3.selectAll('svg').selectAll('.line').data()
what i expect is that it should return data in all the matching element. but it just return data in first matching element.
if i just do
d3.selectAll('svg').selectAll('.line')
this shows that it has 2 group element and its data property contains the data.
if i just do var line = d3.selectAll('svg').selectAll('.line'); line[0].data()it gives me error. as line[0] become a DOM element without any property
how to get data in all matching selection or am i not clear on how to use it.
This is the expected behaviour as the spec on selection.data(values) reads:
If values is not specified, then this method returns the array of data
for the first group in the selection.
That explains why you only get the data bound to the first group.
To access data bound to all groups returned by your selection you could use:
d3.selectAll('svg').selectAll('.line').each(function(d) {
// Within this function d is this group's data.
// Iterate, accumulate, do whatever you like at this point.
});
I can not see your code, therefore I will show you a working one:
// scene setup
// we generate 3 SVG tags inside the page <body>
var $svg;
for (var svg = 0; svg < 3; svg++) {
$svg = d3.select("body").append("svg:svg").attr({
width: 200,
height: 200,
id: "svg_" + svg
});
}
// multiple selection + data
// consider the array of colors as data
var array_of_colors = ["red", "yellow", "blue", "khaki", "gray", "green"];
d3.selectAll("svg").selectAll("line.dash").data(array_of_colors).enter()
.append("svg:line").attr({
x1: function(d){return(50 + Math.random() * 50);},// random coordinates
y1: function(d){return(50 + Math.random() * 50);},// random coordinates
x2: 150,
y2: 140,
"stroke-width": 2,
stroke: function(d) {
console.log(d);
return (d);
}
}).classed({
"dash": true
});
The code produces 6 lines (as size of data array) in each SVG element:
Looks like:

cal-heatmap - legendColors as array of color values?

I am trying out cal-heatmap, and I want to supply a simple array of the legend colors to use, rather than having to define CSS classes for each color.
So far in my testing this doesn't seem possible?
for example, using:
legendColors : ['rgb(103,0,31)','rgb(178,24,43)','rgb(214,96,77)','rgb(244,165,130)','rgb(253,219,199)','rgb(224,224,224)','rgb(186,186,186)','rgb(135,135,135)','rgb(77,77,77)','rgb(26,26,26)'],
Gives me a scale with 10 steps from the first to the second color, ignoring the rest.
Am I missing something simple in the documentation, or is this not possible?
I know that I can create CSS classes, but I don't want to do that - I need to have a dynamic, variable number of categories, with dynamically created colors.
I looked through the API and source code and it doesn't seem possible. Two thoughts come to mind:
One, fix it after the fact:
cal.init({
range: 10,
start: new Date(2000, 0, 1, 1),
data: "datas-hours.json",
onComplete: function() {
setTimeout(function(){
['rgb(103,0,31)','rgb(178,24,43)','rgb(214,96,77)','rgb(244,165,130)','rgb(253,219,199)','rgb(224,224,224)','rgb(186,186,186)','rgb(135,135,135)','rgb(77,77,77)','rgb(26,26,26)']
.forEach(function(d,i){
d3.selectAll("rect.r" + i)
.style("fill", d);
});
}, 10);
}
I'm not sure why the setTimeout is necessary and this produces an odd "flash" affect as the colors are swapped. Example here.
Another idea is to write the styles on the fly:
var style = document.createElement('style');
style.type = 'text/css';
['rgb(103,0,31)','rgb(178,24,43)','rgb(214,96,77)','rgb(244,165,130)','rgb(253,219,199)','rgb(224,224,224)','rgb(186,186,186)','rgb(135,135,135)','rgb(77,77,77)','rgb(26,26,26)']
.forEach(function(d,i){
style.innerHTML += ".q" + i + " {fill:" + d + "; background-color: " + d + "}";
});
document.getElementsByTagName('head')[0].appendChild(style);
var cal = new CalHeatMap();
cal.init({
range: 10,
start: new Date(2000, 0, 1, 1),
data: "datas-hours.json"
});
Example here.
Not sure I like either of these options, though. You'd probably be better forking the repository, adding what you want and submitting a pull request. Look down at line 3278 and swap out that color scale with your ordinal one.

Resources