Donut chart in d3 - javascript

i have donut chart with legend specification. I have 2 values in dataset. But here with this code i'm getting only the first value, "Unresolved".
var dataset = {
Unresolved: [3],
Resolved:[7]
};
var keyValue=[];
for(key in dataset){
keyValue.push(key);
}
var width = 260,
height = 300,
radius = Math.min(width, height) / 2;
var color = ["#9F134C", "#ccc"];
var pie = d3.layout.pie()
.sort(null);
var arc = d3.svg.arc()
.innerRadius(radius - 90)
.outerRadius(radius - 80);
var svg = d3.select("body").append("svg")
.attr("width", width)
.attr("height", height)
.append("g")
.attr("transform", "translate(" + width / 2 + "," + height / 2 + ")");
var gs = svg.selectAll("g").data(d3.values(dataset)).enter().append("g");
var path = gs.selectAll("path")
.data(function(d,i) { return pie(d); })
.enter().append("path")
.attr("fill", function(d, i) { console.log("log", keyValue[i]);return color[i]; }) //Here i'm getting only the 1st value "unresolved".
.attr("d", arc);
var legendCircle = d3.select("body").append("svg").selectAll("g").data(keyValue).enter().append("g")
.attr("class","legend")
.attr("width", radius)
.attr("height", radius * 2)
.attr("transform", function(d, i) { return "translate(0," + i * 20 + ")"; });
legendCircle.append("rect")
.attr("width", 18)
.attr("height", 10)
.style("fill", function(d, i) { return color[i];});
legendCircle.append("text")
.attr("x", 24)
.attr("y", 5)
.attr("dy", ".35em")
.text(function(d) { return d; });
The output i'm getting is,
Can anyone help on this? Thanks.

It looks like you're doing a nested selection in your code, which you would usually only need for nested data. Your data is not nested however -- there's a single level with 2 values. What's happening is that, by using a nested selection, you're descending into the value arrays, each of which contains only a single value.
It works fine if you do away with the nested selection and pass your original data to the pie layout.
var gs = svg.selectAll("g").data(pie(d3.values(dataset))).enter().append("g");
var path = gs.append("path")
.attr("fill", function(d, i) { return color[i]; })
.attr("d", arc);
Complete example here.

Related

d3 donut chart multy ring with text

I create a multi ring donut chart following some examples on the web and everything was ok till i try to display text into the ring and got stuck with different errors.
I think at this point i can access data but i when comes to create rings, i got NaN values for my path and text.
Here is my code:
var dataset = {
ringOne: [{"label":"70%", "value":70},
{"label":"10%", "value":10},
{"label":"20%", "value":20}],
ringTwo: [{"label":"70%", "value":70},
{"label":"10%", "value":10},
{"label":"20%", "value":20}],
};
var width = 460,
height = 300,
cwidth = 45,
outerR = 100,
color = d3.scale.ordinal().range(["#07e", "#00aced", "#e32"]);
var svgDonut = d3.select("#donut")
.append("svg")
.attr("width", width)
.attr("height", height)
.append("g")
.attr("transform", "translate(" + width / 2 + "," + height / 2 + ")" );
var arc = d3.svg.arc();
var pie = d3.layout.pie()
.sort(null)
.value(function(d) { return d.value; });
var rings = svgDonut.selectAll("g.slice")
.data(pie([dataset]))
.enter()
.append("g")
.attr("class", "slice");
rings.append("path")
.attr("fill", function(d, i){ return color(i); })
.attr("d", function(d, i, j){ return arc.innerRadius( 80 + cwidth * j )
.outerRadius( outerR * (j) )(d); });
rings.append("text")
.attr("transform", function(d) {
d.innerRadius = 0;
d.outerRadius = outerR;
return "translate(" + arc.centroid(d) + ")";
})
.attr("text-anchor", "middle")
.text(function(d, i) { return dataset.ringOne[i].label; });
The error i get is:
Error: Invalid value for <path> attribute d="M4.898587196589413e-15,-80A80,80 0 1,1 NaN,NaNL0,0Z"
Error: Invalid value for <text> attribute transform="translate(NaN,NaN)"
fiddle here: https://jsfiddle.net/anaketa/8u7gejjc/
Any ideas?

How to use forEach in d3 using Javascript

I'm trying to make a pie chart using d3. To do this I am send my data in a JSON format to my view and then I'd like to try and total up three of the parameters and then use these in the pie chart. So far I have managed to get the data to the view but now I need to use a foreach to total up the parameters. I'm only working on one at the moment.
Script:
var width = 960,
height = 500,
radius = Math.min(width, height) / 2;
var color = d3.scale.ordinal()
.range(["#98abc5", "#8a89a6", "#7b6888", "#6b486b", "#a05d56", "#d0743c"]);
var arc = d3.svg.arc()
.outerRadius(radius - 10)
.innerRadius(0);
var pie = d3.layout.pie()
.sort(null)
.value(function (d) { return d.Query; });
var svg = d3.select("body").append("svg")
.attr("width", width)
.attr("height", height)
.append("g")
.attr("transform", "translate(" + width / 2 + "," + height / 2 + ")");
var data = #Html.Raw(#Model.output);
var QueryTotal = data.forEach(function(d) {
d.Query = +d.Query;
});
console.log(QueryTotal);
var g = svg.selectAll(".arc")
.data(pie(data))
.enter().append("g")
.attr("class", "arc");
g.append("path")
.attr("d", arc)
.style("fill", function (d) { return color("Query"); });
g.append("text")
.attr("transform", function (d) { return "translate(" + arc.centroid(d) + ")"; })
.attr("dy", ".35em")
.style("text-anchor", "middle")
.text(function (d) { return "Query"; });
How do I use a for each to total up the values, I've given it an attempt which you can see above. It just returns undefined.
To sum the values, use reduce()
var QueryTotal = data.reduce(function(prev, d) {
return prev + d.Query;
}, 0);
Or, using your existing structure:
var QueryTotal = 0;
data.forEach(function(d) {
QueryTotal += d.Query;
});
The D3 way to do this is to use d3.sum():
var QueryTotal = d3.sum(data, function(d) { return d.Query; });

D3 - How to loop through an object with keys for a bar chart

I am trying to create a bar chart with the dataset below. I am stuck on the part where the height[score] of the bar[country] is determined. How do I loop through the dataset to pull each score for a different country?
Any help would be greatly appreciated :)
var w = 500;
var h = 100;
var barPadding = 1;
var dataset = [
{"country":"Hong Kong","score":8.98},
{"country":"Singapore","score":8.54},
{"country":"New Zealand","score":8.19},
{"country":"Switzerland","score":8.09},
{"country":"Mauritius","score":8.98},
{"country":"United Arab Emirates","score":8.05},
{"country":"Canada","score":8.00},
{"country":"Australia","score":7.87},
{"country":"Jordan","score":7.86},
{"country":"Chile","score":7.84},
];
//Create SVG element
var svg = d3.select("body")
.append("svg")
.attr("width", w)
.attr("height", h);
svg.selectAll("rect")
.data(dataset)
.enter()
.append("rect")
.attr("x", function(d, i) {
return i * (w / dataset.length);
})
.attr("y", function(d) {
return h - (d * 4);
})
.attr("width", w / dataset.length - barPadding)
.attr("height", function(d) {
return d * 4;
});
In D3, once you load the data through the .data(dataset) command, you can now access each record of the data by inserting the anonymous function function(d, i) { } as you have done in a few of your attributes.
Since your dataset is:
var dataset = [
{"country":"Hong Kong","score":8.98},
{"country":"Singapore","score":8.54},
{"country":"New Zealand","score":8.19},
{"country":"Switzerland","score":8.09},
{"country":"Mauritius","score":8.98},
{"country":"United Arab Emirates","score":8.05},
{"country":"Canada","score":8.00},
{"country":"Australia","score":7.87},
{"country":"Jordan","score":7.86},
{"country":"Chile","score":7.84},
];
each d is a object record e.g. {"country":"Singapore","score":8.54}, while i refers to the index of the object d returned e.g. 1 for our example of d used above.
To access the score of the object record d, this becomes simple Javscript object notation i.e. d.score.
Hence your .attr call should look like:
.attr("height", function(d) {
return d.score * 4;
});
Similarly, you can extract the other fields e.g. country with d.country if you intend to use it in .attr("text", function(d) { return d.country; });
This is the real beauty and power of D3. If you ever want to expand your visualization with more features that is obtained through your data, then all you have to make sure is that your dataset data contains more data attributes, and you can call them later as you iterate through the anonymous functions. And D3 is in the spirit of its name, truly being "data-driven"! :)
You will need to fix d to d.score.
If you want to show country text, write svg.selectAll("text") after svg.selectAll("rect").
Like this:
var w = 500;
var h = 100;
var barPadding = 1;
var dataset = [
{"country":"Hong Kong","score":8.98},
{"country":"Singapore","score":8.54},
{"country":"New Zealand","score":8.19},
{"country":"Switzerland","score":8.09},
{"country":"Mauritius","score":8.98},
{"country":"United Arab Emirates","score":8.05},
{"country":"Canada","score":8.00},
{"country":"Australia","score":7.87},
{"country":"Jordan","score":7.86},
{"country":"Chile","score":7.84},
];
//Create SVG element
var svg = d3.select("body")
.append("svg")
.attr("width", w)
.attr("height", h);
svg.selectAll("rect")
.data(dataset)
.enter()
.append("rect")
.attr("x", function(d, i) {
return i * (w / dataset.length);
})
.attr("y", function(d) {
return h - (d.score * 4);
})
.attr("width", w / dataset.length - barPadding)
.attr("height", function(d) {
return d.score * 4;
});
svg.selectAll("text")
.data(dataset)
.enter()
.append("text")
.text(function(d) {
return d.country;
})
.attr("transform", function(d, i) {
var barW = w / dataset.length;
return "translate(" +
( barW * i + barW / 2 + barPadding ) + "," +
( h - 5 ) +
")rotate(-90)";
})
.attr("font-size", "8pt")
.attr("fill", "white");
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/3.4.11/d3.min.js"></script>
Something like
For( var i =0; i<dataset.length; i++){
// Dataset[i].country
// dataset[i].score
}
You have an array of objects

Donut chart d3.js

I try to modify this example. I would like to create two data arrays and merge them using special mergingAr() function instead of data.csv. But it does not work. There is one-colour chart without any data. I can`t find the problem place in the code. So, here it is:
var width = 250,
height = 250,
radius = 230;
var arr1 = [44, 64]; //age
var arr2 = [14106543, 8819342]; //population
function type(d) {
d[1] = +d[1];
return d;
}
function mergingAr(array1, array2)
{
var i, out = [];
for(i=0;i<array1.length;i++)
{
out.push([array1[i],array2[i]]);
}
return out;
}
var data = mergingAr(arr1, arr2);
var color = d3.scale.ordinal()
.range(["#EB7221", "#8a89a6", "#7b6888", "#6b486b", "#a05d56", "#d0743c", "#ff8c00"]);
var arc = d3.svg.arc()
.outerRadius(radius - 100)
.innerRadius(radius - 180);
var pie = d3.layout.pie()
.sort(null)
.value(function(d) { return d[1]; });
var svg = d3.select("#pie").append("svg")
.attr("width", width)
.attr("height", height)
.append("g")
.attr("transform", "translate(" + width / 2 + "," + height / 2 + ")");
var g = svg.selectAll(".arc")
.data(pie(data))
.enter().append("g")
.attr("class", "arc");
g.append("path")
.attr("d", arc)
.style("fill", function(d) { return color(d[0]); });
g.append("text")
.attr("transform", function(d) { return "translate(" + arc.centroid(d) + ")"; })
.attr("dy", ".35em")
.style("text-anchor", "middle")
.text(function(d) { return d[0]; });
var legend = svg.selectAll(".legend")
.data(color.domain())
.enter().append("g")
.attr("class", "legend")
.attr("transform", function(d, i) { return "translate(0," + i * 20 + ")"; });
legend.append("rect")
.attr("x", width - 18)
.attr("width", 18)
.attr("height", 18)
.style("fill", color);
legend.append("text")
.attr("x", width - 24)
.attr("y", 9)
.attr("dy", ".35em")
.style("text-anchor", "end")
.text(function(d) { return d; });
Thanks all for help!
Your data is there, but your color is wrong. Right now, you have this line setting path color:
.style("fill", function(d) { return color(d[0]); });
The problem is that your data is an object, not an array. (It's transformed by the pie function), so you need to reference the data attribute on the object, and then get the zero index of that array, like so:
.style("fill", function(d) { return color(d.data[0]); });

D3 barplot only using half my data

I have a d3.js barplot using some json data containing 12 elements. The data value I'm using for bar height is fpkm. I'm able to return that value as a callback to d3's data function- but only for half the elements.
My problem is that only the first half of the values are appearing in my barplot. I only get 6 rows corresponding to my first 6 values.
I made a fiddle here: http://jsfiddle.net/z9Mvt/
I can't seem to figure out why it's only using half the elements in my json.
Any help = appreciated.
html:
<div align='center' id="GECGplot" style='width:98%;text-align:center;'></plot>
and the js:
var gecgData= {"nodeName":"GECG","children":[{"nodeName":0,"nodeData":{"id":"643139","library_id":"SI_5589","gene_id":"ENSG00000157554","gene_short_name":"ERG","fpkm":"1.1241","fpkm_conf_lo":"0.898502","fpkm_conf_hi":"1.34969","fpkm_status":"OK","fpkm_percentile_compendium":"8.33","chr_id":"21","start":"39751948","end":"40033704","locus":"21:39751948-40033704","report":"0","tracking_id":null,"class_code":null,"nearest_ref":null,"tss_id":null,"length":null,"coverage":null,"fpkm_percentile_origin_tissue":null,"fpkm_percentile_collection_tissue":null,"fpkm_percentile_sample_cancer":null,"fpkm_fold_change_benign":null}},
{"nodeName":1,"nodeData":{"id":"872561","library_id":"SI_5596","gene_id":"ENSG00000157554","gene_short_name":"ERG","fpkm":"1.12666","fpkm_conf_lo":"0.871059","fpkm_conf_hi":"1.38226","fpkm_status":"OK","fpkm_percentile_compendium":"16.67","chr_id":"21","start":"39751948","end":"40033704","locus":"21:39751948-40033704","report":"0","tracking_id":null,"class_code":null,"nearest_ref":null,"tss_id":null,"length":null,"coverage":null,"fpkm_percentile_origin_tissue":null,"fpkm_percentile_collection_tissue":null,"fpkm_percentile_sample_cancer":null,"fpkm_fold_change_benign":null}},
{"nodeName":2,"nodeData":{"id":"1031623","library_id":"SI_5553","gene_id":"ENSG00000157554","gene_short_name":"ERG","fpkm":"1.21305","fpkm_conf_lo":"0.949369","fpkm_conf_hi":"1.47674","fpkm_status":"OK","fpkm_percentile_compendium":"25.00","chr_id":"21","start":"39751948","end":"40033704","locus":"21:39751948-40033704","report":"0","tracking_id":null,"class_code":null,"nearest_ref":null,"tss_id":null,"length":null,"coverage":null,"fpkm_percentile_origin_tissue":null,"fpkm_percentile_collection_tissue":null,"fpkm_percentile_sample_cancer":null,"fpkm_fold_change_benign":null}},
{"nodeName":3,"nodeData":{"id":"248423","library_id":"SI_5486","gene_id":"ENSG00000157554","gene_short_name":"ERG","fpkm":"1.98203","fpkm_conf_lo":"1.64888","fpkm_conf_hi":"2.31519","fpkm_status":"OK","fpkm_percentile_compendium":"33.33","chr_id":"21","start":"39751948","end":"40033704","locus":"21:39751948-40033704","report":"0","tracking_id":null,"class_code":null,"nearest_ref":null,"tss_id":null,"length":null,"coverage":null,"fpkm_percentile_origin_tissue":null,"fpkm_percentile_collection_tissue":null,"fpkm_percentile_sample_cancer":null,"fpkm_fold_change_benign":null}},
{"nodeName":4,"nodeData":{"id":"1039674","library_id":"SI_5554","gene_id":"ENSG00000157554","gene_short_name":"ERG","fpkm":"2.24514","fpkm_conf_lo":"1.83333","fpkm_conf_hi":"2.65696","fpkm_status":"OK","fpkm_percentile_compendium":"41.67","chr_id":"21","start":"39751948","end":"40033704","locus":"21:39751948-40033704","report":"0","tracking_id":null,"class_code":null,"nearest_ref":null,"tss_id":null,"length":null,"coverage":null,"fpkm_percentile_origin_tissue":null,"fpkm_percentile_collection_tissue":null,"fpkm_percentile_sample_cancer":null,"fpkm_fold_change_benign":null}},
{"nodeName":5,"nodeData":{"id":"304849","library_id":"SI_5485","gene_id":"ENSG00000157554","gene_short_name":"ERG","fpkm":"2.29868","fpkm_conf_lo":"2.02514","fpkm_conf_hi":"2.57221","fpkm_status":"OK","fpkm_percentile_compendium":"50.00","chr_id":"21","start":"39751948","end":"40033704","locus":"21:39751948-40033704","report":"0","tracking_id":null,"class_code":null,"nearest_ref":null,"tss_id":null,"length":null,"coverage":null,"fpkm_percentile_origin_tissue":null,"fpkm_percentile_collection_tissue":null,"fpkm_percentile_sample_cancer":null,"fpkm_fold_change_benign":null}},
{"nodeName":6,"nodeData":{"id":"417495","library_id":"SI_5484","gene_id":"ENSG00000157554","gene_short_name":"ERG","fpkm":"2.61196","fpkm_conf_lo":"2.28949","fpkm_conf_hi":"2.93442","fpkm_status":"OK","fpkm_percentile_compendium":"58.33","chr_id":"21","start":"39751948","end":"40033704","locus":"21:39751948-40033704","report":"0","tracking_id":null,"class_code":null,"nearest_ref":null,"tss_id":null,"length":null,"coverage":null,"fpkm_percentile_origin_tissue":null,"fpkm_percentile_collection_tissue":null,"fpkm_percentile_sample_cancer":null,"fpkm_fold_change_benign":null}},
{"nodeName":7,"nodeData":{"id":"928522","library_id":"SI_5595","gene_id":"ENSG00000157554","gene_short_name":"ERG","fpkm":"2.94397","fpkm_conf_lo":"2.61962","fpkm_conf_hi":"3.26832","fpkm_status":"OK","fpkm_percentile_compendium":"66.67","chr_id":"21","start":"39751948","end":"40033704","locus":"21:39751948-40033704","report":"0","tracking_id":null,"class_code":null,"nearest_ref":null,"tss_id":null,"length":null,"coverage":null,"fpkm_percentile_origin_tissue":null,"fpkm_percentile_collection_tissue":null,"fpkm_percentile_sample_cancer":null,"fpkm_fold_change_benign":null}},
{"nodeName":8,"nodeData":{"id":"622876","library_id":"SI_5552","gene_id":"ENSG00000157554","gene_short_name":"ERG","fpkm":"3.27303","fpkm_conf_lo":"2.79509","fpkm_conf_hi":"3.75097","fpkm_status":"OK","fpkm_percentile_compendium":"75.00","chr_id":"21","start":"39751948","end":"40033704","locus":"21:39751948-40033704","report":"0","tracking_id":null,"class_code":null,"nearest_ref":null,"tss_id":null,"length":null,"coverage":null,"fpkm_percentile_origin_tissue":null,"fpkm_percentile_collection_tissue":null,"fpkm_percentile_sample_cancer":null,"fpkm_fold_change_benign":null}},
{"nodeName":9,"nodeData":{"id":"50230","library_id":"SI_5487","gene_id":"ENSG00000157554","gene_short_name":"ERG","fpkm":"9.88611","fpkm_conf_lo":"8.6495","fpkm_conf_hi":"11.1227","fpkm_status":"OK","fpkm_percentile_compendium":"83.33","chr_id":"21","start":"39751948","end":"40033704","locus":"21:39751948-40033704","report":"0","tracking_id":null,"class_code":null,"nearest_ref":null,"tss_id":null,"length":null,"coverage":null,"fpkm_percentile_origin_tissue":null,"fpkm_percentile_collection_tissue":null,"fpkm_percentile_sample_cancer":null,"fpkm_fold_change_benign":null}},
{"nodeName":10,"nodeData":{"id":"816444","library_id":"SI_5594","gene_id":"ENSG00000157554","gene_short_name":"ERG","fpkm":"15.1868","fpkm_conf_lo":"13.8218","fpkm_conf_hi":"16.5519","fpkm_status":"OK","fpkm_percentile_compendium":"91.67","chr_id":"21","start":"39751948","end":"40033704","locus":"21:39751948-40033704","report":"0","tracking_id":null,"class_code":null,"nearest_ref":null,"tss_id":null,"length":null,"coverage":null,"fpkm_percentile_origin_tissue":null,"fpkm_percentile_collection_tissue":null,"fpkm_percentile_sample_cancer":null,"fpkm_fold_change_benign":null}},
{"nodeName":11,"nodeData":{"id":"496931","library_id":"SI_5551","gene_id":"ENSG00000157554","gene_short_name":"ERG","fpkm":"52.249","fpkm_conf_lo":"50.8217","fpkm_conf_hi":"53.6763","fpkm_status":"OK","fpkm_percentile_compendium":"100.00","chr_id":"21","start":"39751948","end":"40033704","locus":"21:39751948-40033704","report":"0","tracking_id":null,"class_code":null,"nearest_ref":null,"tss_id":null,"length":null,"coverage":null,"fpkm_percentile_origin_tissue":null,"fpkm_percentile_collection_tissue":null,"fpkm_percentile_sample_cancer":null,"fpkm_fold_change_benign":null}}]}
;
//Width and height
// var w = $('#GECGplot').width();
var w = 700;
var h = 300;
var barPadding = 1;
var margin = {top: 40, right: 10, bottom: 20, left: 10};
var xScale = d3.scale.linear().
domain([0, 20]). // your data minimum and maximum
range([0, h]); // the pixels to map to, e.g., the width of the diagram.
//Create SVG element
var svg = d3.select("#GECGplot")
.append("svg")
.attr("width", w)
.attr("height", h);
svg.selectAll("rect")
// .data(dataset)
.data(function(d, i) {
return plotData[i].nodeData.fpkm;
})
.enter()
.append("rect")
.attr("x", function(d, i) {
// alert(plotData.length);
return i * (w / plotData.length);
})
.attr("y", function(d, i) {
alert(plotData[i].nodeData.fpkm);
return h - (plotData[i].nodeData.fpkm * 50); //Height minus data value
})
.attr("width", w / plotData.length - barPadding)
.attr("height", function(d, i) {
return plotData[i].nodeData.fpkm * 50; //Just the data value
})
.attr("fill", function(d, i) {
return "rgb(0, 0, " + (plotData[i].nodeData.fpkm * 50) + ")";
})
svg.selectAll("text")
.data(function(d, i) {
return plotData[i].nodeData.fpkm;
})
.enter()
.append("text")
.text(function(d, i) {
return plotData[i].nodeData.fpkm;
})
.attr("font-family", "sans-serif")
.attr("font-size", "11px")
.attr("fill", "white")
.attr("text-anchor", "middle")
.attr("x", function(d, i) {
return i * (w / plotData.length) + (w / plotData.length - barPadding) / 2;
})
.attr("y", function(d, i) {
return h - (plotData[i].nodeData.fpkm * 50) + 14;
})
// alert(tableSchema);
Here you go. You bind the array "children" to the rectangle elements so you dont need the argument 'i' to access the value you need.
Also, I would recommend using the d3.scale.ordinal() for your x axis as opposed to calculating it explicitly from the data. Litte more flexible.
http://jsfiddle.net/Cef4D/
svg.selectAll("rect")
.data(plotData)
.enter().append("rect")
.attr("x", function(d, i) {return i * (w / plotData.length);})
.attr("y", function(d) {
return h - (d.nodeData.fpkm * 50); //Height minus data value
})
.attr("width", w / plotData.length - barPadding)
.attr("height", function(d, i) {
return d.nodeData.fpkm * 50; //Just the data value
})
.attr("fill", function(d, i) {
return "rgb(0, 0, " + (d.nodeData.fpkm * 50) + ")";
})

Categories