Incomplete line chart with missing points and missing lines - d3.js
var maindata=[
{"date":"21-APR-16 04:19 AM","Delhi":30,"Mumbai":28},
{"date":"21-APR-16 05:19 AM","Delhi":32,"Mumbai":30},
{"date":"21-APR-16 06:19 AM","Delhi":34,"Mumbai":34},
{"date":"21-APR-16 10:19 AM","Kolkata":34},
{"date":"21-APR-16 11:19 AM","Delhi":48,"Chennai":40,"Kolkata":36}
];
var that = this;
var deepClonedCopy = jQuery.extend(true, {}, maindata);
var data;
data = $.map(deepClonedCopy, function(el) { return el });
// var passedheight = this.getHeight();
//var containerWidth = jQuery.sap.byId(this.oParent.sId).width() || 800; // gets super parent width
var margin = {top: 15, right: 30, bottom: 20, left: 30},
width = 960- margin.left - margin.right,
height = 500 - margin.top - margin.bottom;
if(data.length >= 1){
//alert(JSON.stringify(data));
var parseDate = d3.time.format("%d-%b-%y %H:%M %p").parse;
var maxDate = d3.time.hour.offset(parseDate(d3.max(data, function(d) { return d.date; })),+1);
var minDate = d3.time.hour.offset(parseDate(d3.min(data, function(d) { return d.date; })),-1);
var div = d3.select("#toolTip");
var x = d3.time.scale()
.domain([minDate, maxDate])
.range([0, width]);
var y = d3.scale.linear()
.range([Math.ceil(height), 0]);
var color = d3.scale.category10();
var xAxis = d3.svg.axis()
.scale(x)
.orient("bottom").ticks(4);
var yAxis = d3.svg.axis()
.scale(y)
.orient("left").ticks(4).tickSize(-width, 0, 0);
var line = d3.svg.line()
.x(function(d) { return x(d.date); })
.y(function(d) { return y(d.tonneValue); });
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 + ")");
color.domain(d3.keys(data[0]).filter(function(key) { return key !== "date" && key !== "maxLength"; }));
data.forEach(function(d) {
d.date = parseDate(d.date);
});
var wsfs = color.domain().map(function(name) {
return {
name: name,
values: data.map(function(d) {
return {date: d.date, tonneValue: +d[name]};
})
};
});
//x.domain(d3.extent(data, function(d) { return d.date; }));
y.domain([
//d3.min(wsfs, function(c) { return d3.min(c.values, function(v) { return v.tonneValue; }); }),
0,
Math.ceil(d3.max(wsfs, function(c) { return d3.max(c.values, function(v) { return v.tonneValue; }); }))
]);
svg.append("g")
.attr("class", "x axis")
.attr("transform", "translate(0," + height + ")")
.call(xAxis);
svg.append("g")
.attr("class", "y axis")
.call(yAxis);
var wsf = svg.selectAll(".wsf")
.data(wsfs)
.enter().append("g")
.attr("class", "wsf");
wsf.append("path")
.attr("class", "line")
.attr("d", function(d) { return line(d.values); })
.style("stroke", function(d) { return color(d.name); });
wsf.selectAll("dot")
.data(function(d) { return d.values;})
.enter().append("circle")
.attr("class", "dot")
.attr("r", 3)
.attr("cx", function(d) { return x(d.date); })
.attr("cy", function(d) { return y(d.tonneValue); })
.attr("stroke", function (d) {
return color(this.parentNode.__data__.name) })
.attr("fill", function (d) {
return color(this.parentNode.__data__.name) })
//.attr("fill-opacity", .5)
//.attr("stroke-width", 2)
.on("mouseover", function (d) {
formatDate = d3.time.format("%d-%m-%Y %H:%M %p");
div.transition().duration(100).style("opacity", .9);
div.html(/*this.parentNode.__data__.name + "<br/>" +*/ d.tonneValue /*+ "<br/>" + "<br/>"*/ +" Tonne handled at "+ formatDate(d.date))
.style("left", (d3.event.pageX) + "px").style("top", (d3.event.pageY - 28) + "px").attr('r', 8);
d3.select(this).attr('r', 8)
}).on("mouseout", function (d) {
div.transition().duration(600).style("opacity", 0)
d3.select(this).attr('r', 3);
});
var legendNames = d3.keys(data[0]).filter(function(key) { return key !== "date" });
data.forEach(function(d) {
d.ages = legendNames.map(function(name) { return {name: name, value: +d[name]}; });
});
var legend = svg.selectAll(".legend")
.data(legendNames.slice())
.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", 4)
.style("fill", function(d) {return color(d); });
legend.append("text")
.attr("x", width - 24)
.attr("y", 6)
.attr("dy", ".35em")
.style("text-anchor", "end")
.text(function(d) { return d; });
}
The above code gives 2 lines, until first 3 json elements till the data format is similar.
but from 4th element there is no Delhi / Mumbai and there is just Kolkata, but I don't find a line for Kolkata 34, Kolkata 36 neither Delhi's 48 point, where Delhi's line should extend from 34 to 48 and Chennai a simple dot is expected as there is only one point of Chennai i.e. 40.
Can any one tell where am I doing wrong. Here is a fiddle.
The main problem in the code (missing lines and points and incomplete legend) is that your data needs some structuring.
You dataset some times has cities which may or may not be present.
So The first task will be to make a proper data set
From this:
var maindata = [{
"date": "21-APR-16 04:19 AM",
"Delhi": 30,
"Mumbai": 28
}, {
"date": "21-APR-16 05:19 AM",
"Delhi": 32,
"Mumbai": 30
}, {
"date": "21-APR-16 06:19 AM",
"Delhi": 34,
"Mumbai": 34
}, {
"date": "21-APR-16 10:19 AM",
"Kolkata": 34
}, {
"date": "21-APR-16 11:19 AM",
"Delhi": 48,
"Chennai": 40,
"Kolkata": 36
}];
To this:
[
{
"name":"Delhi",
"values":[
{
"date":"2016-04-20T22:49:00.000Z",
"value":30
},
{
"date":"2016-04-20T23:49:00.000Z",
"value":32
},
{
"date":"2016-04-21T00:49:00.000Z",
"value":34
},
{
"date":"2016-04-21T05:49:00.000Z",
"value":48
}
]
},
{
"name":"Mumbai",
"values":[
{
"date":"2016-04-20T22:49:00.000Z",
"value":28
},
{
"date":"2016-04-20T23:49:00.000Z",
"value":30
},
{
"date":"2016-04-21T00:49:00.000Z",
"value":34
}
]
},
{
"name":"Kolkata",
"values":[
{
"date":"2016-04-21T04:49:00.000Z",
"value":34
},
{
"date":"2016-04-21T05:49:00.000Z",
"value":36
}
]
},
{
"name":"Chennai",
"values":[
{
"date":"2016-04-21T05:49:00.000Z",
"value":40
}
]
}
]
So now all cities have their respective data.
Number of cities x so number of lines will be x.
To make the dataset do this:
var cities = []
var parseDate = d3.time.format("%d-%b-%y %H:%M %p").parse;
maindata.forEach(function(d) {
cities.push(d3.keys(d).filter(function(key) {
return key !== "date" && key !== "maxLength";
}));
});
cities = d3.set(d3.merge(cities)).values();//get all cites and make it unique
console.log(cities)
var myData = [];
var allValues = [];
var allDates =[];
//generate cities and its values as shown in my dataset above.
cities.forEach(function(city){
var cityData = {};
cityData.name = city;
cityData.values = [];
maindata.forEach(function(md){
if (md[city]){
allValues.push(md[city])
allDates.push(parseDate(md.date))
cityData.values.push({date: parseDate(md.date), value: md[city]})
}
})
myData.push(cityData)
});
Next make lines like this:
var wsf = svg.selectAll(".wsf")
.data(myData)
.enter().append("g")
.attr("class", "wsf");
wsf.append("path")
.attr("class", "line")
.attr("d", function(d) {
return line(d.values);
})
.style("stroke", function(d) {
return color(d.name);
});
And Legends like this:
var legend = svg.selectAll(".legend")
.data(cities)
.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", 4)
.style("fill", function(d) {
return color(d);
});
legend.append("text")
.attr("x", width - 24)
.attr("y", 6)
.attr("dy", ".35em")
.style("text-anchor", "end")
.text(function(d) {
return d;
});
}
working code here
Here is the updated fiddle
some keys(i.e. Kolkata)aren't included in binding data
Original code:
color.domain(d3.keys(data[0]).filter(function(key) { return key !== "date" && key !== "maxLength"; }));
data.forEach(function(d) {
d.date = parseDate(d.date);
});
var wsfs = color.domain().map(function(name) {
return {
name: name,
values: data.map(function(d) {
return {date: d.date, tonneValue: +d[name]};
})
};
});
To(you need change ['Delhi', 'Mumbai', 'Kolkata', 'Chennai'] to code more formally):
var wsfs = ['Delhi', 'Mumbai', 'Kolkata', 'Chennai'].map(function(name) {
return {
name: name,
values: data.map(function(d) {
return {date: d.date, tonneValue: +d[name]};
}).filter(function(d) {
return !isNaN(d.tonneValue)
})
};
});
Filter data
Original code:
var wsfs = color.domain().map(function(name) {
return {
name: name,
values: data.map(function(d) {
return {date: d.date, tonneValue: +d[name]};
})
};
});
To:
var wsfs = ['Delhi', 'Mumbai', 'Kolkata', 'Chennai'].map(function(name) {
return {
name: name,
values: data.map(function(d) {
return {date: d.date, tonneValue: +d[name]};
}).filter(function(d) {
return !isNaN(d.tonneValue)
})};
});
Related
trouble accessing max of nested object data for use in a domain in D3
I have csv data with a column of years and then columns of age groups with rates: year,15-19,20-24,25-29,... 1999,0,1.3,8.1,... 2000,0,1.3,8.3,... . . I've mapped the data as shown below. Once mapped the data is in the form of: >Array[Object, Object, ...] Each Object is of the form: >id:"age group" values:Array[] >Object >rate: "" >year: "" I'm having trouble with the max value for the y.domain. It seems to be pulling the max value from my 4th object and stopping there. Any help figuring out what's wrong with the max call would be appreciated. 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 + ")"); // define scales var x = d3.scaleLinear().range([0, width]), y = d3.scaleLinear().range([height, 0]), //define color scale z = d3.scaleOrdinal(d3.schemeCategory10); //define line generator var line = d3.line() .curve(d3.curveBasis) .x(function(d) { return x(d.year); }) .y(function(d) { return y(d.rate); }); //load data d3.csv("breaseByageSpread.csv", function(error, data) { if(error) throw error; var ages = data.columns.slice(1).map(function(id) { return { id: id, values: data.map(function(d) { return {year: d.year, rate: d[id]}; }) }; }); //define x axis x.domain(d3.extent(data, function(d) { return d.year; })); //define y axis y.domain([ d3.min(ages, function(c) { return d3.min(c.values, function(d) { return d.rate; }); }), d3.max(ages, function(c) { return d3.max(c.values, function(d) { return d.rate; }); }) ]); // define color scale z.domain(ages.map(function(c) { return c.id; })); //append x axis g.append("g") .attr("class", "axis axis--x") .attr("transform", "translate(0," + height + ")") .call(d3.axisBottom(x) .ticks(15) .tickFormat(d3.format('d'))); //append y axis g.append("g") .attr("class", "axis axis--y") .call(d3.axisLeft(y)) .append("text") .attr("transform", "rotate(-90)") .attr("y", -50) .attr('x', -125) .attr("dy", "0.71em") .attr("fill", "#000") .text("Rate per 100,000 ppl"); //append rate data to svg var rate = g.selectAll(".rate") .data(ages) .enter().append("g") .attr("class", "age"); //append rate path to svg rate.append("path") .attr("class", "line") .attr("d", function(d) { return line(d.values); }) .style("stroke", function(d) { return z(d.id); }); rate.append("text") .datum(function(d) { return {id: d.id, value: d.values[d.values.length - 1]}; }) .attr("transform", function(d) { return "translate(" + x(d.value.year) + "," + y(d.value.rate) + ")"; }) .attr("x", 3) .attr("dy", "0.35em") .style("font", "10px sans-serif") .text(function(d) { return d.id; }); 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; }; });
I found the problem. The rates data was coming in as strings. To convert it to a numeric I changed the following code when loading the data: Old: values: data.map(function(d) { return {year: d.year, rate: d[id]}; New: values: data.map(function(d) { return {year: d.year, rate: +d[id]};
Cannot read property 'values' of null
I am following this example, but I am using d3 v4. My d.date have no values. Can't figure out why. The csv file has correct time format. Can you help me? <script type="text/javascript"> var margin = {top: 20, right: 80, bottom: 30, left: 50}, w = 800 - margin.left - margin.right, h = 600 - margin.top - margin.bottom; var x = d3.scaleTime() .domain([new Date("January 1, 2012"), new Date("May 31, 2017")]) .range([0, w]); var y = d3.scaleLinear() .range([h, 0]); var xAxis = d3.axisBottom() .scale(x) .tickFormat(d3.timeFormat("%m/%Y")); var yAxis = d3.axisLeft() .scale(y) var parseDate = d3.timeParse("%d%m%Y"); console.log(parseDate); var color = d3.scaleOrdinal(d3.schemeCategory10); var line = d3.line() .x(function(d) { return x(d.date); }) .y(function(d) { return y(d.stat); }); var svg = d3.select("body").append("svg") .attr("width", w + margin.left + margin.right) .attr("height", h + margin.top + margin.bottom) .append("g") .attr("transform", "translate(" + margin.left + "," + margin.top + ")"); svg.append("svg:g") .attr("class", "x_axis") .attr("transform", "translate(0," + h + ")") .call(xAxis); svg.append("svg:g") .attr("class", "y_axis") .call(yAxis) .append("text") .attr("transform", "rotate(-90)") .attr("y", 6) .attr("dy", "0.71em") .attr("fill", "#000") .text("Numbers"); var menu = d3.select("#menu select") .on("change", change); d3.csv("Data4.csv", function(csv) { console.log(csv); medias = csv redraw(); }); d3.select(window) .on("keydown", function() { altKey = d3.event.altKey; }) .on("keyup", function() { altKey = false; }); var altKey; function change() { clearTimeout(timeout); d3.transition() .duration(altKey ? 7500 : 1500) .each(redraw); } function redraw() { var nested = d3.nest() .key(function(d) { return d.indicatorCode;}) .object(medias) var series = menu.property("value"); var data = nested[series]; console.log(data); var keyring = d3.keys(data[0]).filter(function(key) { return (key !== "Sel_name" && key !== "monthCode" && key !== "indicatorCode" && key !== "date"); // console.log(keyring); }); var transpose = keyring.map(function(name) { return { name: name, values: data.map(function(d) { return {date: new Date(parseDate(d.date)), stat: d[name]}; }) }; }); console.log(transpose); x.domain([ d3.min(transpose, function(c) { return d3.min(c.values, function(d) { return d.date; }); }), d3.max(transpose, function(c) { return d3.max(c.values, function(d) { return d.date; }); }) ]); y.domain([ d3.min(transpose, function(c) { return d3.min(c.values, function(d) { return d.stat; }); }), d3.max(transpose, function(c) { return d3.max(c.values, function(d) { return d.stat; }); }) ]); var media = svg.selectAll(".media") .data(transpose) .enter().append("g") .attr("class", "media") .attr("id", function(d) { return d.name; }); media.append("path") .attr("class", "line") .attr("d", function(d) { return line(d.values); }) .style("stroke", function(d) { return color(d.name); }); media.append("text") .attr("class", "names") .datum(function(d) { return {name: d.name, value: d.values[d.values.length - 1]}; }) .attr("transform", function(d) { return "translate(" + x(d.value.date) + "," + y(d.value.stat) + ")"; }) .attr("x", 4) .attr("dy", ".35em") .text(function(d) { return d.name; }); var mediaUpdate = d3.transition(media); mediaUpdate.select("path") .attr("d", function(d) { return line(d.values); }); mediaUpdate.select("text") .attr("transform", function(d) { return "translate(" + x(d.values[d.values.length - 1].date) + "," + y(d.values[d.values.length - 1].stat) + ")"; }); d3.transition(svg).select("y_axis") .call(yAxis); d3.transition(svg).select("x_axis") .attr("transform", "translate(0," + h + ")") .call(xAxis); } var timeout = setTimeout(function() { menu.property("value", "NSPM").node().focus(); change(); }, 7000); function smedia() { var chkbox = document.getElementById("statmedia"); if (chkbox.checked) { document.getElementById("media5").style.cssText = "opacity:1;", document.getElementById("media6").style.cssText = "opacity:1;", document.getElementById("media7").style.cssText = "opacity:1;", document.getElementById("media8").style.cssText = "opacity:1;" } else { document.getElementById("media5").style.cssText = "", document.getElementById("media6").style.cssText = "", document.getElementById("media7").style.cssText = "", document.getElementById("media8").style.cssText = ""; }}; function nstmedia() { var chkbox = document.getElementById("nonstmedia") if (chkbox.checked) { document.getElementById("media1").style.cssText = "opacity:1;", document.getElementById("media2").style.cssText = "opacity:1;", document.getElementById("media3").style.cssText = "opacity:1;", document.getElementById("media4").style.cssText = "opacity:1;", document.getElementById("media5").style.cssText = "opacity:1;", document.getElementById("media6").style.cssText = "opacity:1;", document.getElementById("media7").style.cssText = "opacity:1;", document.getElementById("media8").style.cssText = "opacity:1;", document.getElementById("media9").style.cssText = "opacity:1;" } else { document.getElementById("media1").style.cssText = "", document.getElementById("media2").style.cssText = "", document.getElementById("media3").style.cssText = "", document.getElementById("media4").style.cssText = "", document.getElementById("media5").style.cssText = "", document.getElementById("media6").style.cssText = "", document.getElementById("media7").style.cssText = "", document.getElementById("media8").style.cssText = "", document.getElementById("media9").style.cssText = ""; }}; </script> My cvs file looks like following: Sel_name,indicatorCode,date,monthCode,media1,media2,media3,media4,media5,media6,media7,media8,media9,media10,media11,media12,media13 Num_posts_pmon,NPPM,31/01/2012,m1201,217,146,0,1114,0,0,0,0,0,0,0,0,54 Num_posts_pmon,NPPM,29/02/2012,m1202,159,161,0,1402,0,0,0,0,0,0,0,0,31 Num_posts_pmon,NPPM,31/03/2012,m1203,8,7,0,1212,0,0,0,24,0,0,0,28,17 Num_posts_pmon,NPPM,30/04/2012,m1204,10,14,0,1004,0,0,0,111,0,0,26,28,6 Num_posts_pmon,NPPM,31/05/2012,m1205,15,73,0,1070,0,0,0,76,0,0,127,557,6 Num_posts_pmon,NPPM,30/06/2012,m1206,102,396,0,834,0,0,0,97,0,0,23,893,1 Num_posts_pmon,NPPM,31/07/2012,m1207,148,276,0,993,0,0,0,63,0,0,67,602,0 Num_posts_pmon,NPPM,31/08/2012,m1208,167,189,0,909,34,0,0,54,0,0,31,606,3 Num_posts_pmon,NPPM,30/09/2012,m1209,176,196,0,991,254,0,0,80,0,0,81,608,8 Num_posts_pmon,NPPM,31/10/2012,m1210,121,184,0,1064,190,0,0,41,0,0,93,600,100 Num_posts_pmon,NPPM,30/11/2012,m1211,107,137,0,1301,78,0,0,68,0,0,110,416,116 Num_posts_pmon,NPPM,31/12/2012,m1212,73,108,0,1190,40,0,0,41,0,0,163,306,158 Num_posts_pmon,NPPM,31/01/2013,m1301,98,122,0,1266,121,0,0,53,0,0,137,329,201 Num_posts_pmon,NPPM,28/02/2013,m1302,92,108,0,1248,175,0,0,32,0,0,86,221,205 Num_posts_pmon,NPPM,31/03/2013,m1303,95,101,0,1330,88,0,0,59,0,0,112,160,243 Num_posts_pmon,NPPM,30/04/2013,m1304,122,127,0,1764,280,0,0,61,0,0,151,264,189 Num_posts_pmon,NPPM,31/05/2013,m1305,89,90,0,1645,630,0,785,123,715,0,144,275,203 Num_posts_pmon,NPPM,30/06/2013,m1306,83,103,0,1541,830,0,1105,130,1425,0,99,260,268 Num_posts_pmon,NPPM,31/07/2013,m1307,89,111,0,1468,895,0,1515,133,1443,0,40,145,318 Num_posts_pmon,NPPM,31/08/2013,m1308,82,86,0,1508,1105,0,1720,125,1488,0,65,227,250 Num_posts_pmon,NPPM,30/09/2013,m1309,264,117,0,1690,1900,14,1507,195,2515,0,105,226,251
Your specifier is wrong, you're forgetting the forward slashes. Since your dates are like this: 31/01/2012 Your specifier should be: var parseDate = d3.timeParse("%d/%m/%Y"); Besides that, you don't need the new Date in the map function: var date = "31/01/2012"; var parseDate = d3.timeParse("%d/%m/%Y"); console.log(parseDate(date)) console.log(new Date(parseDate(date))) <script src="https://d3js.org/d3.v4.min.js"></script>
How can I refactor a d3 pie to accept more or less data points?
I have a project that almost works the way I want. When a smaller dataset is added, slices are removed. It fails when a larger dataset is added. The space for the arc is added but no label or color is added for it. This is my enter() code: newArcs.enter() .append("path") .attr("stroke", "white") .attr("stroke-width", 0.8) .attr("fill", function(d, i) { return color(i); }) .attr("d", arc); What am I doing wrong?
I've fixed the code such that it works now: // Tween Function var arcTween = function(a) { var i = d3.interpolate(this.current || {}, a); this.current = i(0); return function(t) { return arc(i(t)); }; }; // Setup all the constants var duration = 500; var width = 500 var height = 300 var radius = Math.floor(Math.min(width / 2, height / 2) * 0.9); var colors = ["#d62728", "#ff9900", "#004963", "#3497D3"]; // Test Data var d2 = [{ label: 'apples', value: 20 }, { label: 'oranges', value: 50 }, { label: 'pears', value: 100 }]; var d1 = [{ label: 'apples', value: 100 }, { label: 'oranges', value: 20 }, { label: 'pears', value: 20 }, { label: 'grapes', value: 20 }]; // Set the initial data var data = d1 var updateChart = function(dataset) { arcs = arcs.data(donut(dataset), function(d) { return d.data.label }); arcs.exit().remove(); arcs.enter() .append("path") .attr("stroke", "white") .attr("stroke-width", 0.8) .attr("fill", function(d, i) { return color(i); }) .attr("d", arc); arcs.transition() .duration(duration) .attrTween("d", arcTween); sliceLabel = sliceLabel.data(donut(dataset), function(d) { return d.data.label }); sliceLabel.exit().remove(); sliceLabel.enter() .append("text") .attr("class", "arcLabel") .attr("transform", function(d) { return "translate(" + (arc.centroid(d)) + ")"; }) .attr("text-anchor", "middle") .style("fill-opacity", function(d) { if (d.value === 0) { return 1e-6; } else { return 1; } }) .text(function(d) { return d.data.label; }); sliceLabel.transition() .duration(duration) .attr("transform", function(d) { return "translate(" + (arc.centroid(d)) + ")"; }) .style("fill-opacity", function(d) { if (d.value === 0) { return 1e-6; } else { return 1; } }); }; var color = d3.scale.category20(); var donut = d3.layout.pie() .sort(null) .value(function(d) { return d.value; }); var arc = d3.svg.arc() .innerRadius(radius * .4) .outerRadius(radius); var svg = d3.select("body") .append("svg") .attr("width", width) .attr("height", height); var arc_grp = svg.append("g") .attr("class", "arcGrp") .attr("transform", "translate(" + (width / 2) + "," + (height / 2) + ")"); var label_group = svg.append("g") .attr("class", "lblGroup") .attr("transform", "translate(" + (width / 2) + "," + (height / 2) + ")"); var arcs = arc_grp.selectAll("path"); var sliceLabel = label_group.selectAll("text"); updateChart(data); // returns random integer between min and max number function getRand() { var min = 1, max = 2; var res = Math.floor(Math.random() * (max - min + 1) + min); //console.log(res); return res; } // Update the data setInterval(function(model) { var r = getRand(); return updateChart(r == 1 ? d1 : d2); }, 2000); <script src="https://cdnjs.cloudflare.com/ajax/libs/d3/3.4.11/d3.min.js"></script>
Removing segments from a d3 animated pie
I have prototype code I am working with here: jsfiddle The example shows how to add a segment when new data is added but not how to remove it again when the data changes [back]. I am fairly new to d3 and don't quite get the exit() function yet... if you reverse the initial and second dataset you will see that the grapes segment is never removed. Thanks in advance! any help would be great! The update code: (my final chart needs to update on a timer when data changes) var arcs = arc_grp.selectAll("path") .data(donut(data)); arcs.enter() .append("path") .attr("stroke", "white") .attr("stroke-width", 0.8) .attr("fill", function(d, i) { return color(i); }) .attr("d", arc) .each(function(d) { return this.current = d; }); var sliceLabel = label_group.selectAll("text") .data(donut(data)); sliceLabel.enter() .append("text") .attr("class", "arcLabel") .attr("transform", function(d) { return "translate(" + (arc.centroid(d)) + ")"; }) .attr("text-anchor", "middle") .style("fill-opacity", function(d) { if (d.value === 0) { return 1e-6; } else { return 1; } }) .text(function(d) { return d.data.label; }); };
To remove elements without data, you have to use exit(), which... Returns the exit selection: existing DOM elements in the selection for which no new datum was found. So, inside your updateChart function, you need an exit selection for both paths and texts: var newArcs = arcs.data(donut(dataset)); newArcs.exit().remove(); var newSlices = sliceLabel.data(donut(dataset)); newSlices.exit().remove(); Here is your updated code: // Setup all the constants var duration = 500; var width = 500 var height = 300 var radius = Math.floor(Math.min(width / 2, height / 2) * 0.9); var colors = ["#d62728", "#ff9900", "#004963", "#3497D3"]; // Test Data var d2 = [{ label: 'apples', value: 20 }, { label: 'oranges', value: 50 }, { label: 'pears', value: 100 }]; var d1 = [{ label: 'apples', value: 100 }, { label: 'oranges', value: 20 }, { label: 'pears', value: 20 }, { label: 'grapes', value: 20 }]; // Set the initial data var data = d1 var updateChart = function(dataset) { var newArcs = arcs.data(donut(dataset)); newArcs.exit().remove(); newArcs.enter() .append("path") .attr("stroke", "white") .attr("stroke-width", 0.8) .attr("fill", function(d, i) { return color(i); }) .attr("d", arc); newArcs.transition() .duration(duration) .attrTween("d", arcTween); var newSlices = sliceLabel.data(donut(dataset)); newSlices.exit().remove(); newSlices.transition() .duration(duration) .attr("transform", function(d) { return "translate(" + (arc.centroid(d)) + ")"; }) .style("fill-opacity", function(d) { if (d.value === 0) { return 1e-6; } else { return 1; } }); sliceLabel.data(donut(dataset)).enter() .append("text") .attr("class", "arcLabel") .attr("transform", function(d) { return "translate(" + (arc.centroid(d)) + ")"; }) .attr("text-anchor", "middle") .style("fill-opacity", function(d) { if (d.value === 0) { return 1e-6; } else { return 1; } }) .text(function(d) { return d.data.label; }); }; var color = d3.scale.category20(); var donut = d3.layout.pie() .sort(null) .value(function(d) { return d.value; }); var arc = d3.svg.arc() .innerRadius(radius * .4) .outerRadius(radius); var svg = d3.select("body") .append("svg") .attr("width", width) .attr("height", height); var arc_grp = svg.append("g") .attr("class", "arcGrp") .attr("transform", "translate(" + (width / 2) + "," + (height / 2) + ")"); var label_group = svg.append("g") .attr("class", "lblGroup") .attr("transform", "translate(" + (width / 2) + "," + (height / 2) + ")"); var arcs = arc_grp.selectAll("path") .data(donut(data)); arcs.enter() .append("path") .attr("stroke", "white") .attr("stroke-width", 0.8) .attr("fill", function(d, i) { return color(i); }) .attr("d", arc) .each(function(d) { return this.current = d; }); var sliceLabel = label_group.selectAll("text") .data(donut(data)); sliceLabel.enter() .append("text") .attr("class", "arcLabel") .attr("transform", function(d) { return "translate(" + (arc.centroid(d)) + ")"; }) .attr("text-anchor", "middle") .style("fill-opacity", function(d) { if (d.value === 0) { return 1e-6; } else { return 1; } }) .text(function(d) { return d.data.label; }); // Update the data setInterval(function(model) { return updateChart(d2); }, 2000); // Tween Function var arcTween = function(a) { var i = d3.interpolate(this.current, a); this.current = i(0); return function(t) { return arc(i(t)); }; }; .arcLabel { font: 10px sans-serif; fill: #fff; } <script src="https://cdnjs.cloudflare.com/ajax/libs/d3/3.4.11/d3.min.js"></script>
d3js - TypeError: string is undefined
I don't understand this error: TypeError: string is undefined function d3_time_parse(date, template, string, j) { var c, p, i = 0, n = template.length, m = string.length; ... Here my code: var margin = {top: 20, right: 80, bottom: 30, left: 50}, width = 960 - margin.left - margin.right, height = 500 - margin.top - margin.bottom; var parseDate = d3.time.format("%Y%m%d").parse; var x = d3.time.scale() .range([0, width]); var y = d3.scale.linear() .range([height, 0]); var color = d3.scale.category10(); var xAxis = d3.svg.axis() .scale(x) .orient("bottom"); var yAxis = d3.svg.axis() .scale(y) .orient("left"); var line = d3.svg.line() .interpolate("basis") .x(function(d) { return x(d.date); }) .y(function(d) { return y(d.index); }); 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("data.csv", function(error, data) { color.domain(d3.keys(data[0]).filter(function(key) { return key !== "date"; })); data.forEach(function(d) { d.date = parseDate(d.date); }); var indexes = color.domain().map(function(name) { return { name: name, values: data.map(function(d) { return {date: d.date, index: +d[name]}; }) }; }); x.domain(d3.extent(data, function(d) { return d.date; })); y.domain([ d3.min(indexes, function(c) { return d3.min(c.values, function(v) { return v.index; }); }), d3.max(indexes, function(c) { return d3.max(c.values, function(v) { return v.index; }); }) ]); svg.append("g") .attr("class", "x axis") .attr("transform", "translate(0," + height + ")") .call(xAxis); svg.append("g") .attr("class", "y axis") .call(yAxis) .append("text") .attr("transform", "rotate(-90)") .attr("y", 6) .attr("dy", ".71em") .style("text-anchor", "end") .text("Variation (%)"); var index = svg.selectAll(".index") .data(indexes) .enter().append("g") .attr("class", "index"); index.append("path") .attr("class", "line") .attr("d", function(d) { return line(d.values); }) .style("stroke", function(d) { return color(d.name); }); index.append("text") .datum(function(d) { return {name: d.name, value: d.values[d.values.length - 1]}; }) .attr("transform", function(d) { return "translate(" + x(d.value.date) + "," + y(d.value.index) + ")"; }) .attr("x", 3) .attr("dy", ".35em") .text(function(d) { return d.name; }); }); and my csv file look like this: Date,DAX,CAC40,FTSE,NASDAQ,Other 20060407,-0.000712859,-0.009013212,0.010819606,0.009846526,0.003082604 20060413,-0.007765968,-0.024263398,0.011238971,0.004128621,0.005952774 20060421,0.02261859,0.00330204,0.028734861,0.001688981,0.003459211 20060428,0.007170521,-0.007531405,0.010534438,-0.002416181,0.004012361 ... Could someone please help me? thx skeut
Date,DAX,CAC40,FTSE,NASDAQ,Other JavaScript variable names are case sensitive, so when parsing CSV file with headers as above you need to refer to fields with: d.Date // note the uppercase D d.DAX // note all uppercase Here's an example from d3 wiki https://github.com/mbostock/d3/wiki/CSV#wiki-parse Year,Make,Model,Length 1997,Ford,E350,2.34 2000,Mercury,Cougar,2.38 When parsed with d3's CSV parser, the resulting data will be represented by the following array: [ {"Year": "1997", "Make": "Ford", "Model": "E350", "Length": "2.34"}, {"Year": "2000", "Make": "Mercury", "Model": "Cougar", "Length": "2.38"} ] Note the uppercase object keys. To access the year of the first entry, a "Year" with an uppercase Y in data[0].Year would be necessary. Thus in your forEach function you'll need: data.forEach(function (d) { d.Date = parseDate(d.Date); }); ... and later on: return {date: d.Date, index: +d[name]};