d3 line graph using csv - d3.js
I have a data in csv like this:
date,partner,units
2012-05-01,team1,34.12
2012-04-30,team1,45.56
2012-04-27,team2,67.89
2012-04-26,team1,78.54
2012-04-25,team2,89.23
2012-04-24,team2,99.23
2012-04-23,team2,101.34
I want to plot two lines (one for team1, one for team2 using this data), but I am just getting a scatterplot using the following complete d3 code, is my filtering wrong?
<!DOCTYPE html>
<meta charset="utf-8">
<style> /* set the CSS */
#line1 {
fill: none;
stroke: steelblue;
stroke-width: 2px;
}
#line2 {
fill: none;
stroke: red;
stroke-width: 2px;
}
</style>
<input type="button" onclick="hideLine()">
<input type="button" onclick="showLine()">
<!-- load the d3.js library -->
<script src="https://d3js.org/d3.v4.min.js"></script>
<script>
// set the dimensions and margins of the graph
var margin = {top: 20, right: 20, bottom: 30, left: 50},
width = 960 - margin.left - margin.right,
height = 500 - margin.top - margin.bottom;
// parse the date / time
var parseTime = d3.timeParse("%Y-%m-%d");
// set the ranges
var x = d3.scaleTime().range([0, width]);
var y = d3.scaleLinear().range([height, 0]);
// define the 1st line
var valueline = d3.line()
.x(function(d) { return x(d.date); })
.y(function(d) { return y(d.units); });
// define the 2nd line
var valueline2 = d3.line()
.x(function(d) { return x(d.date); })
.y(function(d) { return y(d.units); });
var svg = d3.select("body").append("svg")
.attr("width", width + margin.left + margin.right)
.attr("height", height + margin.top + margin.bottom)
.append("g")
.attr("transform",
"translate(" + margin.left + "," + margin.top + ")");
// Get the data
d3.csv("data1.csv", function(error, data) {
if (error) throw error;
// format the data
data.forEach(function(d) {
d.date = parseTime(d.date);
d.units = +d.units;
});
// Scale the range of the data
x.domain(d3.extent(data, function(d) { return d.date; }));
m = d3.max(data, function(d) {
var m = d.units;
return m;
});
console.log("Max:", m);
y.domain([0,m]);
// Add the valueline path.
svg.append("path")
.data([data])
.filter(function(d) { return d.partner == 'team1'; })
.attr("id", "line1")
.attr("d", valueline);
console.log("DATA", data)
svg.selectAll(".point")
.data(data)
.enter()
.append("circle")
.attr("class", "point")
.attr("cx", function (d, i) {
return x(d.date);
})
.attr("cy", function(d) {
return y(d.units);
})
.attr("r", 4)
.on("mouseover", function(d) { console.log(d.units) });
// Add the valueline2 path.
svg.append("path")
.data([data])
.filter(function(d) { return d.partner == 'team2'; })
.attr("id", "line2")
.style("stroke", "red")
.attr("d", valueline2)
.on("mouseover", function(d) {console.log(d)});
// Add the X Axis
svg.append("g")
.attr("transform", "translate(0," + height + ")")
.call(d3.axisBottom(x));
// Add the Y Axis
svg.append("g")
.call(d3.axisLeft(y));
});
function hideLine() {
console.log("Hideline");
d3.select("#line2").attr("style", "opacity:0");
}
function showLine() {
console.log("ShowLine");
d3.select("#line2").attr("style", "opacity:1");
}
</script>
</body>
Yes, your filter is wrong. You are wrapping your data in another [] which means the filter is only operating on the outer array.
Do this instead:
svg.append("path")
.datum(data.filter(function(d) { return d.partner == 'team1'; }))
.attr("id", "line1")
.attr("d", valueline);
Working example here.
Related
How to synchronize animation of path and area?
I'm getting stuck with the D3.js v4's animation of both line & area: It's ok to do the animation separately for line & area When 2 animations are combined, even at the same transition duration, they do not occur together. For the reason of styling, I cannot drop the line away. See the illustration below: To make thing like above, I do 2 big steps: Do animation for line via setting the properties stroke-dasharrow and stroke-dashoffset. (Inspired from Visual Cinnamon) Do animation for area via tweaking parameters for d3.area() function (Inspired from other Stackoverlfow post) The result is rather disappointing because line and area underneath do not appear in parallel. My target is to mimic the Highchart library, see an example here, and its illustration below: It seems the Highchart library uses a different animation technique, because during DOM inspection, there is no sign of any change for the DOM paths along the animation. Appreciated if anyone could suggest me some ideas to experiment with. My code sample is below: let animationDuration = 5000; // set the dimensions and margins of the graph var margin = { top: 20, right: 20, bottom: 30, left: 50 }, width = 480 - margin.left - margin.right, height = 250 - margin.top - margin.bottom; // parse the date / time var parseTime = d3.timeParse("%d-%b-%y"); // set the ranges var x = d3.scaleTime().range([0, width]); var y = d3.scaleLinear().range([height, 0]); // define the area var area = function (datum, boolean) { return d3.area() .y0(height) .y1(function (d) { return boolean ? y(d.close) : y(d.close); }) .x(function (d) { return boolean ? x(d.date) : 0; }) (datum); } // define the line var valueline = d3.line() .x(function (d) { return x(d.date); }) .y(function (d) { return y(d.close); }); // append the svg obgect to the body of the page // appends a 'group' element to 'svg' // moves the 'group' element to the top left margin var svg = d3.select("body").append("svg") .attr("width", width + margin.left + margin.right) .attr("height", height + margin.top + margin.bottom) .append("g") .attr("transform", "translate(" + margin.left + "," + margin.top + ")"); var data = d3.csvParse(d3.select("pre#data").text()); data.reverse(); // format the data data.forEach(function (d) { d.date = parseTime(d.date); d.close = +d.close; }); // scale the range of the data x.domain(d3.extent(data, function (d) { return d.date; })); y.domain([0, d3.max(data, function (d) { return d.close; })]); // add the area svg.append("path") .data([data]) .attr("class", "area") .attr("d", d => area(d, false)) .attr("fill", "lightsteelblue") .transition() .duration(animationDuration) .attr("d", d => area(d, true)); // add the valueline path. svg.append("path") .data([data]) .attr("class", "line") .attr("d", valueline) .style("stroke-dasharray", d => { let path = document.querySelector(".line"); const totalLength = path.getTotalLength(); return `${totalLength} ${totalLength}`; }) .style("stroke-dashoffset", d => { let path = document.querySelector(".line"); const totalLength = path.getTotalLength(); return `${totalLength}`; }) .transition() .duration(animationDuration) .style("stroke-dashoffset", 0); // add the X Axis svg.append("g") .attr("transform", "translate(0," + height + ")") .call(d3.axisBottom(x)); // add the Y Axis svg.append("g") .call(d3.axisLeft(y)); .line { fill: none; stroke: steelblue; stroke-width: 2px; } pre#data {display:none;} <script src="https://d3js.org/d3.v4.min.js"></script> <pre id="data"> date,close 1-May-12,58.13 30-Apr-12,53.98 27-Apr-12,67.00 26-Apr-12,89.70 25-Apr-12,99.00 24-Apr-12,130.28 23-Apr-12,166.70 20-Apr-12,234.98 19-Apr-12,345.44 18-Apr-12,443.34 17-Apr-12,543.70 16-Apr-12,580.13 13-Apr-12,605.23 12-Apr-12,622.77 11-Apr-12,626.20 10-Apr-12,628.44 9-Apr-12,636.23 5-Apr-12,633.68 4-Apr-12,624.31 3-Apr-12,629.32 2-Apr-12,618.63 30-Mar-12,599.55 29-Mar-12,609.86 28-Mar-12,617.62 27-Mar-12,614.48 26-Mar-12,606.98 </pre>
There is a way to animate both the line and the area, using a custom interpolator. However, since your goal is to mimic that Highcharts animation you linked, there is a way easier alternative: use a <clipPath>. In my proposed solution we create the area and the line the regular way. However, we reference a clipping path... .attr("clip-path", "url(#clip)"); ...in both area and line. The clipping path is created with 0 width: var clip = svg.append("clipPath") .attr("id", "clip"); var clipRect = clip.append("rect") .attr("width", 0) Then, after that, it's just a matter of applying the transition to the clipping path: clipRect.transition() .duration(5000) .ease(d3.easeLinear) .attr("width", someValue) Here is a demo: var svg = d3.select("svg"); var data = d3.range(30).map(d => Math.random() * 150); var clip = svg.append("clipPath") .attr("id", "clip"); var clipRect = clip.append("rect") .attr("width", 0) .attr("height", 150) var lineGenerator = d3.line() .x((_, i) => i * 10) .y(d => d) .curve(d3.curveMonotoneX) var areaGenerator = d3.area() .x((_, i) => i * 10) .y1(d => d) .y0(150) .curve(d3.curveMonotoneX) svg.append("path") .attr("d", areaGenerator(data)) .attr("class", "area") .attr("clip-path", "url(#clip)"); svg.append("path") .attr("d", lineGenerator(data)) .attr("class", "line") .attr("clip-path", "url(#clip)"); clipRect.transition() .duration(5000) .ease(d3.easeLinear) .attr("width", 300) .line { fill: none; stroke: #222; stroke-width: 2px; } .area { fill: limegreen; stroke: none; } <script src="https://d3js.org/d3.v4.min.js"></script> <svg></svg> And here is your code with those changes: let animationDuration = 5000; // set the dimensions and margins of the graph var margin = { top: 20, right: 20, bottom: 30, left: 50 }, width = 480 - margin.left - margin.right, height = 250 - margin.top - margin.bottom; // parse the date / time var parseTime = d3.timeParse("%d-%b-%y"); // set the ranges var x = d3.scaleTime().range([0, width]); var y = d3.scaleLinear().range([height, 0]); // define the area var area = function(datum, boolean) { return d3.area() .y0(height) .y1(function(d) { return boolean ? y(d.close) : y(d.close); }) .x(function(d) { return boolean ? x(d.date) : 0; }) (datum); } // define the line var valueline = d3.line() .x(function(d) { return x(d.date); }) .y(function(d) { return y(d.close); }); // append the svg obgect to the body of the page // appends a 'group' element to 'svg' // moves the 'group' element to the top left margin var svg = d3.select("body").append("svg") .attr("width", width + margin.left + margin.right) .attr("height", height + margin.top + margin.bottom) .append("g") .attr("transform", "translate(" + margin.left + "," + margin.top + ")"); var clip = svg.append("clipPath") .attr("id", "clip"); var clipRect = clip.append("rect") .attr("width", 0) .attr("height", height); var data = d3.csvParse(d3.select("pre#data").text()); data.reverse(); // format the data data.forEach(function(d) { d.date = parseTime(d.date); d.close = +d.close; }); // scale the range of the data x.domain(d3.extent(data, function(d) { return d.date; })); y.domain([0, d3.max(data, function(d) { return d.close; })]); // add the area svg.append("path") .data([data]) .attr("class", "area") .attr("d", d => area(d, true)) .attr("fill", "lightsteelblue") .attr("clip-path", "url(#clip)"); // add the valueline path. svg.append("path") .data([data]) .attr("class", "line") .attr("d", valueline) .attr("clip-path", "url(#clip)"); // add the X Axis svg.append("g") .attr("transform", "translate(0," + height + ")") .call(d3.axisBottom(x)); // add the Y Axis svg.append("g") .call(d3.axisLeft(y)); clipRect.transition() .duration(5000) .ease(d3.easeLinear) .attr("width", width) .line { fill: none; stroke: steelblue; stroke-width: 2px; } pre#data { display: none; } <script src="https://d3js.org/d3.v4.min.js"></script> <pre id="data"> date,close 1-May-12,58.13 30-Apr-12,53.98 27-Apr-12,67.00 26-Apr-12,89.70 25-Apr-12,99.00 24-Apr-12,130.28 23-Apr-12,166.70 20-Apr-12,234.98 19-Apr-12,345.44 18-Apr-12,443.34 17-Apr-12,543.70 16-Apr-12,580.13 13-Apr-12,605.23 12-Apr-12,622.77 11-Apr-12,626.20 10-Apr-12,628.44 9-Apr-12,636.23 5-Apr-12,633.68 4-Apr-12,624.31 3-Apr-12,629.32 2-Apr-12,618.63 30-Mar-12,599.55 29-Mar-12,609.86 28-Mar-12,617.62 27-Mar-12,614.48 26-Mar-12,606.98 </pre>
D3.js How to hide/show line when click select options?
I tried to use D3.js to draw lines when you click on different checkbox. It will get data of that option. Here is an example I used D3 multi-series line chart with tooltips and legend. I have got the specific data object that is corresponding the checkbox I choose. Howevery, I don't know how to draw it on svg. Please help me and I will appreciate you so much. I also find a website "www.cotrino.com/starpaths/" that shows the final effect I want to implement. My D3 effect <!DOCTYPE html> <meta charset="utf-8"> <style> .axis--x path { display: none; } .line { fill: none; stroke: steelblue; stroke-width: 1.5px; } </style> <script src="http://d3js.org/d3.v4.js"></script> <body> <svg width="1000" height="500"></svg> <div id="disease_list"></div> </body> <script> var svg = d3.select("svg"), margin = {top: 20, right: 80, bottom: 30, left: 50}, width = svg.attr("width") - margin.left - margin.right, height = svg.attr("height") - margin.top - margin.bottom, g = svg.append("g").attr("transform", "translate(" + margin.left + "," + margin.top + ")"); //make a clip path for the graph var clip = svg.append("svg:clipPath") .attr("id", "clip") .append("svg:rect") .attr("x", 0) .attr("y", 0) .attr("width", width) .attr("height", height); var parseTime = d3.timeParse("%Y-%m"); var x = d3.scaleTime().range([0, width]), y = d3.scaleLinear().range([height, 0]); var line = d3.line() .curve(d3.curveBasis) .x(function(d) { console.log(d.date); return x(d.date); }) .y(function(d) { console.log(d.date); return y(d.count); }); var color = d3.scaleOrdinal(d3.schemeCategory20); d3.csv("./top10highestNormalize.csv", type, function(error, data) { if (error) throw error; var diseases = data.columns.slice(1).map(function(id) { return { id: id, values: data.map(function(d) { return {date: d.date, count: d[id]}; }) }; }); console.log(diseases); x.domain(d3.extent(data, function(d) { return d.date; })); y.domain([ d3.min(diseases, function(c) { return d3.min(c.values, function(d) { return d.count; }); }), d3.max(diseases, function(c) { return d3.max(c.values, function(d) { return d.count; }); }) ]); g.append("g") .attr("class", "axis axis--x") .attr("transform", "translate(0," + height + ")") .call(d3.axisBottom(x)); g.append("g") .attr("class", "axis axis--y") .call(d3.axisLeft(y)) .append("text") .attr("transform", "rotate(-90)") .attr("y", 6) .attr("dy", "0.71em") .attr("fill", "#000") .text("Count"); /* var disease = g.selectAll(".disease") .data(diseases) .enter().append("g") .attr("class", "disease"); */ // Create the shape selectors var selector = d3.select("#disease_list").append("select"); labels = selector.selectAll("option") .data(diseases) .enter() .append("option") .attr("value",function(d,i) {return i;}) .text(function(d) {return d.id;}); var menu = d3.select("#disease_list select") .on("change", redraw); // var series = menu.property("value"); //console.log(series); // all the meat goes in the redraw function function redraw() { console.log("redraw start"); // get value from menu selection // the option values are set in HTML and correspond //to the [type] value we used to nest the data var series = menu.property("value"); console.log(series); // only retrieve data from the selected series, using the nest we just created var adata = diseases[series]; console.log(adata); } }); function type(d, _, columns) { d.date = parseTime(d.date); for (var i = 1, n = columns.length, c; i < n; ++i) d[c = columns[i]] = +d[c]; return d; } </script> top10highestNormalize.csv date,disseminated sclerosis,sclerosis,gestural tics,venereal disease,bite,cot death,venereal disease,cardiovascular disease,diseases vascular,pruritis,pus,cystic fibrosis,fibroses 2010-04,0,0,0,0,0,0,0,0,0,0,0,0,0 2010-05,0,0,0.06898023,0.068783069,0.085790885,0.065761258,0.068783069,0,0,0.001204094,0.023051592,0,0 2010-06,0.076923077,0.076923077,0.190584554,0.199972867,0.201072386,0.171789373,0.199972867,0.071428571,0.071428571,0.004816376,0.031284303,0.2,0.2 2010-07,0.230769231,0.230769231,0.221590101,0.224664225,0.225201072,0.235167977,0.224664225,0.214285714,0.285714286,0.00602047,0.038419319,0,0 2010-08,0.538461538,0.538461538,0.174797326,0.182471849,0.174262735,0.192041935,0.182471849,0.071428571,0.071428571,0.003612282,0.023051592,0,0 2010-09,0.230769231,0.230769231,0.287725786,0.277845611,0.252010724,0.259471051,0.277845611,0,0,0.004214329,0.046652031,0,0 2010-10,0.076923077,0.076923077,0.295406059,0.299416633,0.285969616,0.265665952,0.299416633,0,0.071428571,0.007224564,0.03402854,0.066666667,0.066666667 2010-11,0.153846154,0.153846154,0.284027877,0.279337946,0.261840929,0.276149631,0.279337946,0,0,0.006622517,0.050493963,0,0 2010-12,0.153846154,0.153846154,0.271511876,0.237552571,0.213583557,0.237312366,0.237552571,0.142857143,0.142857143,0.004214329,0.035126235,0,0 2011-01,0.076923077,0.076923077,0.306642014,0.312440646,0.28150134,0.305694544,0.312440646,0.142857143,0.142857143,0.006622517,0.046103183,0,0.066666667 2011-02,0.076923077,0.076923077,0.288721377,0.262243929,0.219839142,0.25899452,0.262243929,0.142857143,0.142857143,0.007224564,0.038968167,0,0.066666667 2011-03,0.076923077,0.076923077,0.271654103,0.255324922,0.253798034,0.266857279,0.255324922,0.071428571,0.071428571,0.007224564,0.051591658,0,0 2011-04,0.461538462,0.461538462,0.291423695,0.252068919,0.235031278,0.284250655,0.252068919,0,0,0.009030704,0.045005488,0,0 2011-05,0.153846154,0.153846154,0.448158157,0.380681047,0.351206434,0.439123183,0.380681047,0,0,0.011438892,0.079582876,0.333333333,0.4 2011-06,0.153846154,0.153846154,0.498079932,0.437661104,0.391420912,0.424827258,0.437661104,0.142857143,0.142857143,0.009632751,0.063117453,0,0.066666667 2011-07,0,0,0.410467928,0.424094424,0.419124218,0.379080295,0.424094424,0,0.071428571,0.009030704,0.061470911,1,1 2011-08,0.076923077,0.076923077,0.268382876,0.262922263,0.238605898,0.267810341,0.262922263,0.214285714,0.214285714,0.002408188,0.038968167,0,0 2011-09,0.230769231,0.230769231,0.510027023,0.469949803,0.470956211,0.444841553,0.469949803,0,0,0.014449127,0.075740944,0.133333333,0.2 2011-10,0.076923077,0.076923077,0.462380885,0.434540768,0.431635389,0.417679295,0.434540768,0.142857143,0.142857143,0.006622517,0.073545554,0,0.066666667 2011-11,0.153846154,0.153846154,0.519698478,0.457061457,0.415549598,0.443888492,0.457061457,0.142857143,0.142857143,0.01384708,0.06805708,0.2,0.2 2011-12,1,1,0.382449154,0.35002035,0.319928508,0.315701692,0.35002035,0,0,0.002408188,0.060373216,0,0 2012-01,0.461538462,0.461538462,0.492390841,0.45312712,0.409294013,0.45389564,0.45312712,0.571428571,0.571428571,0.007224564,0.060373216,0,0 2012-02,0.076923077,0.076923077,0.382875836,0.375932709,0.350312779,0.369073147,0.375932709,0.071428571,0.071428571,0.003612282,0.049945115,0.066666667,0.066666667 2012-03,0.923076923,1,1,0.922127255,1,0.871098404,0.922127255,0.5,0.5,0.01384708,0.171789243,0,0.066666667 2012-04,0.230769231,0.307692308,0.699331532,0.676977344,0.63360143,0.645699309,0.676977344,0.142857143,0.142857143,0.012040939,0.092206367,0.133333333,0.133333333 2012-05,0.846153846,0.846153846,0.801735173,0.752408086,0.776586238,0.7436264,0.752408086,0.785714286,0.785714286,0.016857315,0.131723381,0.466666667,0.466666667 2012-06,0.384615385,0.461538462,0.730479306,0.732193732,0.625558534,0.657850846,0.732193732,0,0,0.011438892,0.118002195,0.6,0.666666667 2012-07,0.384615385,0.384615385,0.751386716,0.738434405,0.71849866,0.714081487,0.738434405,0.285714286,0.285714286,0.009030704,0.126783754,0.2,0.2 2012-08,0.384615385,0.461538462,0.700327123,0.643467643,0.619302949,0.646890636,0.643467643,0.285714286,0.285714286,0.012642986,0.150933041,0.2,0.266666667 2012-09,0.076923077,0.230769231,0.72137676,0.701804368,0.63538874,0.70455087,0.701804368,0.214285714,0.214285714,0.011438892,0.130076839,0.066666667,0.066666667 2012-10,0.230769231,0.230769231,0.846252311,0.863112196,0.796246649,0.825827972,0.863112196,0.071428571,0.071428571,0.036724865,0.127881449,0.333333333,0.333333333 2012-11,0.692307692,0.692307692,0.895605177,1,0.798927614,0.909935668,1,0.214285714,0.357142857,0.012642986,0.143798024,0,0.133333333 2012-12,0.923076923,1,0.795903854,0.803283137,0.683646113,0.827257565,0.803283137,0.142857143,0.142857143,0.008428657,0.104829857,0.6,0.6 2013-01,0.230769231,0.384615385,0.92106386,0.964862298,0.848078642,0.944007624,0.964862298,0.285714286,0.357142857,0.015653221,0.146542261,0.533333333,0.733333333 2013-02,0.153846154,0.307692308,0.830322856,0.872880206,0.798927614,0.755777937,0.872880206,0.142857143,0.142857143,0.010234798,0.110318332,0,0.066666667 2013-03,0.230769231,0.230769231,0.927037406,0.944105277,0.885612154,0.953061711,0.944105277,0.142857143,0.142857143,0.009632751,0.131174533,0,0.133333333 2013-04,0.384615385,0.384615385,0.796046082,0.775471442,0.671134942,0.715749345,0.775471442,0,0,0.012040939,0.12349067,0.133333333,0.133333333 2013-05,0.923076923,1,0.824633765,0.844254511,0.742627346,0.843697879,0.844254511,0.142857143,0.142857143,0.015653221,0.149286498,0,0 2013-06,0.307692308,0.307692308,0.884369222,0.949667616,0.865951743,1,0.949667616,0.071428571,0.071428571,0.020469597,0.135016465,0.466666667,0.466666667 2013-07,0.461538462,0.461538462,0.864172948,0.935829602,0.843610366,0.939480581,0.935829602,0.071428571,0.071428571,0.015051174,0.128979144,0.066666667,0.2 2013-08,0.153846154,0.153846154,0.670886076,0.738163071,0.753351206,0.821300929,0.738163071,0.071428571,0.214285714,0.012642986,0.098243688,0,0 2013-09,0.230769231,0.230769231,0.876262267,0.861484195,0.744414656,0.996426019,0.861484195,0,0,0.024081878,0.144895719,0.066666667,0.066666667 2013-10,0.615384615,0.615384615,0.917508178,0.885361552,0.806970509,0.841315225,0.885361552,0.642857143,0.642857143,0.030704395,0.115806806,0.2,0.4 2013-11,0,0.076923077,0.857061584,0.903540904,0.791778374,0.845127472,0.903540904,0.5,0.5,0.012642986,0.093852909,0,0 2013-12,0.230769231,0.230769231,0.704878396,0.719169719,0.584450402,0.81915654,0.719169719,0.285714286,0.5,0.015653221,0.108122942,0,0 2014-01,0.461538462,0.461538462,0.900014223,0.856328856,0.717605004,0.98903979,0.856328856,0.357142857,0.5,0.030102348,0.137211855,0,0.066666667 2014-02,0,0,0.707865169,0.703296703,0.63717605,0.796997856,0.703296703,1,1,0.012642986,0.097145993,0,0 2014-03,0.230769231,0.230769231,0.815531219,0.800434134,0.7256479,0.786275911,0.800434134,0.714285714,0.714285714,0.009632751,0.099341383,0.533333333,0.6 2014-04,0.153846154,0.153846154,0.756506898,0.790259124,0.615728329,0.778174887,0.790259124,0,0,0.011438892,0.12349067,0,0 2014-05,0.461538462,0.461538462,0.85990613,0.767331434,0.705987489,0.78008101,0.767331434,0.142857143,0.285714286,0.014449127,0.13611416,0.066666667,0.133333333 2014-06,0.076923077,0.153846154,0.670886076,0.713064713,0.615728329,0.735763641,0.713064713,0.285714286,0.285714286,0.010836845,0.102634468,0,0 2014-07,0.076923077,0.076923077,0.672592803,0.801655135,0.621090259,0.680009531,0.801655135,0.071428571,0.071428571,0.007224564,0.103183315,0,0 2014-08,0.384615385,0.461538462,0.487270659,0.58377425,0.486148347,0.575887539,0.58377425,0.071428571,0.071428571,0.005418423,0.079582876,0,0.133333333 2014-09,0,0.076923077,0.715545442,0.678062678,0.669347632,0.705980462,0.678062678,0,0,0.01384708,0.103183315,0,0.066666667 2014-10,0.230769231,0.307692308,0.742995306,0.723511057,0.630920465,0.679294734,0.723511057,0,0,0.016857315,0.1064764,0,0 2014-11,0,0,0.672735031,0.623388957,0.583556747,0.64927329,0.623388957,0,0,0.004816376,0.115806806,0.066666667,0.066666667 2014-12,0.307692308,0.384615385,0.591096572,0.55704789,0.478999106,0.491303312,0.55704789,0.285714286,0.428571429,0.003010235,0.074643249,0,0 2015-01,0.076923077,0.153846154,0.659223439,0.561117894,0.531724754,0.605432452,0.561117894,0.071428571,0.071428571,0.007224564,0.094401756,0.133333333,0.133333333 2015-02,0.230769231,0.307692308,0.61840421,0.564780898,0.512064343,0.585656421,0.564780898,0.071428571,0.071428571,0.007224564,0.096597146,0,0 2015-03,0,0,0.770302944,0.677927011,0.599642538,0.675482487,0.677927011,0.071428571,0.071428571,0.009632751,0.111964874,0.066666667,0.2 2015-04,0.076923077,0.076923077,0.706016214,0.61687695,0.731903485,0.563497736,0.61687695,0.071428571,0.071428571,0.008428657,0.097145993,0,0 2015-05,0,0.076923077,0.655383303,0.614027947,0.55406613,0.6154396,0.614027947,0.071428571,0.071428571,0.012642986,0.099341383,0,0 2015-06,0,0.076923077,0.564357844,0.540632207,0.527256479,0.598284489,0.540632207,0.142857143,0.142857143,0.00602047,0.091657519,0,0 2015-07,0.076923077,0.076923077,0.486417295,0.525301859,0.511170688,0.566356922,0.525301859,0,0,0.015653221,0.08726674,0.066666667,0.066666667 2015-08,0.230769231,0.230769231,0.408476746,0.386379053,0.320822163,0.465094115,0.386379053,0,0,0.003010235,0.056531284,0,0 2015-09,0.538461538,0.538461538,0.870999858,0.792701126,0.747095621,0.883964737,0.792701126,0,0,0.013245033,0.156421515,0,0 2015-10,0.153846154,0.153846154,0.469492249,0.435490435,0.320822163,0.51227067,0.435490435,0,0,0.174593618,0.221734358,0,0 2015-11,0.153846154,0.153846154,0.322998151,0.309455976,0.273458445,0.346676197,0.309455976,0,0,0.462974112,0.481888035,0.133333333,0.133333333 2015-12,0.076923077,0.076923077,0.342767743,0.309320309,0.27971403,0.384798666,0.309320309,0,0,0.464780253,0.482436883,0.066666667,0.066666667 2016-01,0.307692308,0.384615385,0.415872564,0.349477683,0.358355675,0.442458899,0.349477683,0,0,0.559903672,0.581229418,0.066666667,0.066666667 2016-02,0,0,0.445455838,0.403744404,0.316353887,0.457469621,0.403744404,0,0,0.54846478,0.568605928,0.066666667,0.066666667 2016-03,0,0,0.471198976,0.400352734,0.317247542,0.508220157,0.400352734,0.142857143,0.142857143,0.604455148,0.628430296,0,0 2016-04,0,0,0.582989617,0.570343237,0.575513852,0.603764594,0.570343237,0.214285714,0.214285714,1,1,0,0
You need to create your line variable: var myLine = svg.append("path"); And then, inside redraw(), changing it according to the option selected: myLine.datum(adata.values) .attr("d", line); Here is a demo plunker: https://plnkr.co/edit/YjGO9TLDBXj13JQuO5bm?p=preview PS: I changed your x-scale range: var x = d3.scaleTime().range([margin.left, width]); And also added a call to redraw() when the code runs for the first time.
Making a grouped bar chart when my groups are varied sizes?
I am trying to make a bar chart out of some grouped data. This is dummy data, but the structure is basically the same. The data: election results includes a bunch of candidates, organized into the districts they were running in, and the total vote count: district,candidate,votes Dist 1,Leticia Putte,3580 Dist 2,David Barron,1620 Dist 2,John Higginson,339 Dist 2,Walter Bannister,2866 [...] I'd like to create a bar or column chart (either, honestly, though my end goal is horizontal) that groups the candidates by district. Mike Bostock has an excellent demo but I'm having trouble translating it intelligently for my purposes. I started to tease it out at https://jsfiddle.net/97ur6cwt/6/ but my data is organized somewhat differently -- instead of rows, by group, I have a column that sets the category. And there might be just one candidate or there might be a few candidates. Can I group items if the groups aren't the same size?
My answer is similar to #GerardoFurtado but instead I use a d3.nest to build a domain per district. This removes the need for hardcoding values and cleans it up a bit: y0.domain(data.map(function(d) { return d.district; })); var districtD = d3.nest() .key(function(d) { return d.district; }) .rollup(function(d){ return d3.scale.ordinal() .domain(d.map(function(c){return c.candidate})) .rangeRoundBands([0, y0.rangeBand()], pad); }).map(data); districtD becomes a map of domains for your y-axis which you use when placing the rects: svg.selectAll("bar") .data(data) .enter().append("rect") .style("fill", function(d,i) { return color(d.district); }) .attr("x", 0) .attr("y", function(d) { return y0(d.district) + districtD[d.district](d.candidate); }) .attr("height", function(d){ return districtD[d.district].rangeBand(); }) .attr("width", function(d) { return x(d.votes); }); I'm off to a meeting but the next step is to clean up the axis and get the candidate names on there. Full running code: var url = "https://gist.githubusercontent.com/amandabee/edf73bc0bbe131435c952f5ed47524a6/raw/99febb9971f76e36af06f1b99913fcaa645ecb3e/election.csv" var m = {top: 10, right: 10, bottom: 50, left: 110}, w = 800 - m.left - m.right, h = 500 - m.top - m.bottom, pad = .1; var x = d3.scale.linear().range([0, w]); y0 = d3.scale.ordinal().rangeRoundBands([0, h], pad); var color = d3.scale.category20c(); var yAxis = d3.svg.axis() .scale(y0) .orient("left"); var xAxis = d3.svg.axis() .scale(x) .orient("bottom") .ticks(5) .tickFormat(d3.format("$,.0f")); var svg = d3.select("#chart").append("svg") .attr("width", w + m.right + m.left + 100) .attr("height", h + m.top + m.bottom) .append("g") .attr("transform", "translate(" + m.left + "," + m.top + ")"); // This moves the SVG over by m.left(110) // and down by m.top (10) d3.csv(url, function(error, data) { data.forEach(function(d) { d.votes = +d.votes; }); y0.domain(data.map(function(d) { return d.district; })); districtD = d3.nest() .key(function(d) { return d.district; }) .rollup(function(d){ console.log(d); return d3.scale.ordinal() .domain(d.map(function(c){return c.candidate})) .rangeRoundBands([0, y0.rangeBand()], pad); }) .map(data); x.domain([0, d3.max(data, function(d) { return d.votes; })]); svg.append("g") .attr("class", "x axis") .attr("transform", "translate(0," + h + ")") .call(xAxis) .selectAll("text") .style("text-anchor", "middle"); svg.append("g") .attr("class", "y axis") .call(yAxis) .append("text"); svg.selectAll("bar") .data(data) .enter().append("rect") .style("fill", function(d,i) { return color(d.district); }) .attr("x", 0) .attr("y", function(d) { return y0(d.district) + districtD[d.district](d.candidate); }) .attr("height", function(d){ return districtD[d.district].rangeBand(); }) .attr("width", function(d) { return x(d.votes); }); svg.selectAll(".label") .data(data) .enter().append("text") .text(function(d) { return (d.votes); }) .attr("text-anchor", "start") .attr("x", function(d) { return x(d.votes)}) .attr("y", function(d) { return y0(d.district) + districtD[d.district](d.candidate) + districtD[d.district].rangeBand()/2;}) .attr("class", "axis"); }); .axis { font: 10px sans-serif; } .axis path, .axis line { fill: none; stroke: black; shape-rendering: crispEdges; } <script src="https://cdnjs.cloudflare.com/ajax/libs/d3/3.4.11/d3.min.js"></script> <div id="chart"></div> An alternate version which sizes the bars the same and scales the outer domain appropriately: <!DOCTYPE html> <html> <head> <script src="https://cdnjs.cloudflare.com/ajax/libs/d3/3.4.11/d3.min.js"></script> <style> .label { font: 10px sans-serif; } .axis { font: 11px sans-serif; font-weight: bold; } .axis path, .axis line { fill: none; stroke: black; shape-rendering: crispEdges; } </style> </head> <body> <div id="chart"></div> <script> var url = "https://gist.githubusercontent.com/amandabee/edf73bc0bbe131435c952f5ed47524a6/raw/99febb9971f76e36af06f1b99913fcaa645ecb3e/election.csv" var m = { top: 10, right: 10, bottom: 50, left: 110 }, w = 800 - m.left - m.right, h = 500 - m.top - m.bottom, pad = .1, padPixel = 5; var x = d3.scale.linear().range([0, w]); var y0 = d3.scale.ordinal(); var color = d3.scale.category20c(); var yAxis = d3.svg.axis() .scale(y0) .orient("left"); var xAxis = d3.svg.axis() .scale(x) .orient("bottom") .ticks(5) .tickFormat(d3.format("$,.0f")); var svg = d3.select("#chart").append("svg") .attr("width", w + m.right + m.left + 100) .attr("height", h + m.top + m.bottom) .append("g") .attr("transform", "translate(" + m.left + "," + m.top + ")"); // This moves the SVG over by m.left(110) // and down by m.top (10) d3.csv(url, function(error, data) { data.forEach(function(d) { d.votes = +d.votes; }); var barHeight = h / data.length; y0.domain(data.map(function(d) { return d.district; })); var y0Range = [0]; districtD = d3.nest() .key(function(d) { return d.district; }) .rollup(function(d) { var barSpace = (barHeight * d.length); y0Range.push(y0Range[y0Range.length - 1] + barSpace); return d3.scale.ordinal() .domain(d.map(function(c) { return c.candidate })) .rangeRoundBands([0, barSpace], pad); }) .map(data); y0.range(y0Range); x.domain([0, d3.max(data, function(d) { return d.votes; })]); svg.append("g") .attr("class", "x axis") .attr("transform", "translate(0," + h + ")") .call(xAxis) .selectAll("text") .style("text-anchor", "middle"); svg.append("g") .attr("class", "y axis") .call(yAxis) .append("text"); svg.selectAll("bar") .data(data) .enter().append("rect") .style("fill", function(d, i) { return color(d.district); }) .attr("x", 0) .attr("y", function(d) { return y0(d.district) + districtD[d.district](d.candidate); }) .attr("height", function(d) { return districtD[d.district].rangeBand(); }) .attr("width", function(d) { return x(d.votes); }); var ls = svg.selectAll(".labels") .data(data) .enter().append("g"); ls.append("text") .text(function(d) { return (d.votes); }) .attr("text-anchor", "start") .attr("x", function(d) { return x(d.votes) }) .attr("y", function(d) { return y0(d.district) + districtD[d.district](d.candidate) + districtD[d.district].rangeBand() / 2; }) .attr("class", "label"); ls.append("text") .text(function(d) { return (d.candidate); }) .attr("text-anchor", "end") .attr("x", -2) .attr("y", function(d) { return y0(d.district) + districtD[d.district](d.candidate) + districtD[d.district].rangeBand() / 2; }) .style("alignment-baseline", "middle") .attr("class", "label"); }); </script> </body> </html>
This is a partial solution: https://jsfiddle.net/hb13oe4v/ The main problem here is creating a scale for each group with a variable domain. Unlike Bostock's example, you don't have the same amount of bars(candidates) for each group(districts). So, I had to do a workaround. First, I nested the data in the most trivial way: var nested = d3.nest() .key(function(d) { return d.district; }) .entries(data); And then created the groups accordingly: var district = svg.selectAll(".district") .data(nested) .enter() .append("g") .attr("transform", function(d) { return "translate(0," + y(d.key) + ")"; }); As I couldn't create an y1 (x1 in Bostock's example) scale, I had to hardcode the height of the bars (which is inherently bad). Also, for centring the bars in each group, I created this crazy math, that puts one bar in the center, the next under, the next above, the next under and so on: .attr("y", function(d, i) { if( i % 2 == 0){ return (y.rangeBand()/2 - 10) + (i/2 + 0.5) * 10} else { return (y.rangeBand()/2 - 10) - (i/2) * 10} }) Of course, all this can be avoided and coded way more elegantly if we could set a variable scale for each group.
d3js stack chart: how to add text on each layer?
In the example of Stacked Area via Nest, I would like to add text on each layer. Like The position of the text doesn't really matters, just being center-right.
You can do something like this: 0) Calculte the center of the path using the function below: function getMyCentroid(element) { var bbox = element.getBBox(); return [bbox.x + bbox.width / 2, bbox.y + bbox.height / 2]; } 1) Next select all path and calculate its center. 2) Append a text and position it at the center svg.append("text") 3) Set the text associated with the path like this .text(d3.select(d).data()[0].key); d3.selectAll(".layer")[0].forEach(function(d) { var centroid = getMyCentroid(d); svg.append("text").attr("x", centroid[0]).attr("y", centroid[1]).text(d3.select(d).data()[0].key); }) var format = d3.time.format("%m/%d/%y"); var margin = {top: 20, right: 30, bottom: 30, left: 40}, width = 960 - margin.left - margin.right, height = 500 - margin.top - margin.bottom; var x = d3.time.scale() .range([0, width]); var y = d3.scale.linear() .range([height, 0]); var z = d3.scale.category20c(); var xAxis = d3.svg.axis() .scale(x) .orient("bottom") .ticks(d3.time.days); var yAxis = d3.svg.axis() .scale(y) .orient("left"); var stack = d3.layout.stack() .offset("zero") .values(function(d) { return d.values; }) .x(function(d) { return d.date; }) .y(function(d) { return d.value; }); var nest = d3.nest() .key(function(d) { return d.key; }); var area = d3.svg.area() .interpolate("cardinal") .x(function(d) { return x(d.date); }) .y0(function(d) { return y(d.y0); }) .y1(function(d) { return y(d.y0 + d.y); }); var svg = d3.select("body").append("svg") .attr("width", width + margin.left + margin.right) .attr("height", height + margin.top + margin.bottom) .append("g") .attr("transform", "translate(" + margin.left + "," + margin.top + ")"); d3.csv("https://gist.githubusercontent.com/cyrilcherian/e6f56b1b9337142c0bde/raw/8b1728849e193db0e8b960ecb750062f4e0cb487/data.csv", function(error, data) { if (error) throw error; data.forEach(function(d) { d.date = format.parse(d.date); d.value = +d.value; }); var layers = stack(nest.entries(data)); x.domain(d3.extent(data, function(d) { return d.date; })); y.domain([0, d3.max(data, function(d) { return d.y0 + d.y; })]); svg.selectAll(".layer") .data(layers) .enter().append("path") .attr("class", "layer") .attr("d", function(d) { return area(d.values); }) .style("fill", function(d, i) { return z(i); }); svg.append("g") .attr("class", "x axis") .attr("transform", "translate(0," + height + ")") .call(xAxis); svg.append("g") .attr("class", "y axis") .call(yAxis); function getMyCentroid(element) { var bbox = element.getBBox(); return [bbox.x + bbox.width / 2, bbox.y + bbox.height / 2]; } d3.selectAll(".layer")[0].forEach(function(d) { var centroid = getMyCentroid(d); svg.append("text").attr("x", centroid[0]).attr("y", centroid[1]).text(d3.select(d).data()[0].key).style("fill", "red"); }) }); body { font: 10px sans-serif; } .axis path, .axis line { fill: none; stroke: #000; shape-rendering: crispEdges; } <script src="https://cdnjs.cloudflare.com/ajax/libs/d3/3.4.11/d3.min.js"></script> Hope this helps!
d3 show labels only for ticks with data in a bar chart
First, I'm new with D3. :) I have a stacked bar chart. My y axis is a ordinal scale (state). In my data, I could have total=0 for some ticks. So, I just want to see the labels when the total > 0 but maintain all the ticks of the data. var data = [{ "state":"A", "total":"10"}, { "state":"B", "total":"0"}, { "state":"C", "total":"0"}, { "state":"D", "total":"20"}, { "state":"E", "total":"0"}, { "state":"F", "total":"50"}] I've tried this code, but this remove all the ticks with total = 0. I just want to remove the label of that tick. yAxis.tickValues(dataSet.map( function(d,i) { if (d.total > 0) return d.state; else if(i % 4 === 0 ) return d.state; }) .filter(function (d) { return !!d; } )); Thanks, Filipe UPDATE Here is the code: <!DOCTYPE html> <html xmlns="http://www.w3.org/1999/xhtml" class="ocks-org do-not-copy"> <head> <link rel="stylesheet" type="text/css" href="style.css" /> </head> <body> <div id="timeLine"></div> <!--style> svg { font: 10px sans-serif; } path { fill: steelblue; } .axis path, .axis line { fill: none; stroke: #000; shape-rendering: crispEdges; } </style--> <script src="http://d3js.org/d3.v3.min.js"></script> <script> var timeline; var labels; var margin = { top : 20, right : 10, bottom : 60, left : 80 }, width = 500 - margin.left - margin.right, height = 500 - margin.top - margin.bottom; var x = d3.scale.linear() .rangeRound([0, width]); var y = d3.scale.ordinal() .rangeBands([height, 0], 0.1); var color = d3.scale.ordinal() .range(["#1f77b4", "#2ca02c", "#E53524"]); //var color = d3.scale.category10() var xAxis = d3.svg.axis() .scale(x) .orient("bottom") .tickFormat(d3.format(".2s")) .ticks(10); var yAxis = d3.svg.axis() .scale(y) .orient("left") .ticks(5); timeline = d3.select("#timeLine").append("svg") .attr("width", width + margin.left + margin.right) .attr("height", height + margin.top + margin.bottom) .append("g") .attr("transform", "translate(" + margin.left + "," + margin.top + ")") var dataSet; var all_nodes_t; var all_updates_t; var test; var entity = "ALL"; dataSet = [ { "Date":"2014-01", "insert":"27", "remove":"17","updates":"427"}, { "Date":"2014-02", "insert":"27", "remove":"17","updates":"427"}, { "Date":"2014-03", "insert":"27", "remove":"17","updates":"427"}, { "Date":"2014-04", "insert":"0", "remove":"0","updates":"0"}, { "Date":"2014-05", "insert":"27", "remove":"17","updates":"427"}, ]; color.domain(d3.keys(dataSet[0]).filter(function (key) { return key !== "Date"; })); dataSet.forEach(function (d) { var x0 = 0; d.ages = color.domain().map(function (name) { return { name : name, x0 : x0, x1 : x0 += +d[name] }; }); d.total = d.ages[d.ages.length - 1].x1; }); //HERE yAxis.tickFormat(dataSet.map(function(d) { d.total == 0 ? "" : d.Date; })); y.domain(dataSet.map(function (d) { return d.Date; })); x.domain([0, d3.max(dataSet, function (d) { return (d.total + 5); })]); timeline.append("g") .attr("class", "x axis") .attr("transform", "translate(0," + height + ")") .call(xAxis) .append("text") .attr("x", 1) .attr("dx", "42em") .attr("dy", "3em") .style("text-anchor", "end") .text("Operations"); timeline.append("g") .attr("class", "y axis") .call(yAxis); var layer = timeline.selectAll(".state") .data(dataSet) .enter().append("g") .attr("class", "rect") .attr("transform", function (d) { return "translate(0," + y(d.Date) + ")"; }); var rect = layer.selectAll("rect") .data(function (d) { return d.ages; }) .enter().append("rect") .attr("class", "rect") .attr("width", 0) .attr("x", width) .attr('y', function (d, i) { return y(d.Date); }) .attr("height", y.rangeBand()) .style("fill", function (d) { return color(d.name); }); rect.transition() .duration(600) .delay(function (d, i) { return i * 300; }) .attr("width", function (d) { return x(d.x1) - x(d.x0); }) .attr("x", function (d) { return x(d.x0); }); </script> </body> </html>
You can use .tickFormat() to suppress the labels for those particular values. As the thing you want to check isn't part of the data that's available to the scale, you'll need to find it in your entire data: yAxis.tickFormat(function(d) { var val = 0; dataSet.forEach(function(item) { if(item.Date == d) val = item.total; }); return val == 0 ? "" : d; }); This will suppress the label (return "") if the total is 0 or the value can't be found in the data set. Complete demo here.