I'm trying to generate a D3 Stream Graph similar to this beautiful example using some activity tracker data.
I've referenced this example - http://jsfiddle.net/NTJPB/1/light
My JSFiddle - http://jsfiddle.net/Nyquist212/00war1o6/ is telling me I have something wrong with my json format (despite trying to model it on the examples).
The core of my code looks something like this -
data.forEach(function(d){
parseDate = d3.time.format("%m/%d/%Y").parse;
d.date = parseDate(d.date);
d.value = Math.round(+d.value);
});
var maxval = d3.max(data, function(d){ return d.value; });
// Nest the json by key
var nest = d3.nest()
.key(function(d) { return d.key; })
.entries(data);
var margin = {top: 50, right: 50, bottom: 50, left: 50 },
width = 960 - margin.left - margin.right,
height = 500 - margin.top - margin.bottom;
var color = d3.scale.linear()
.range(["#0A3430", "#1E5846", "#3E7E56", "#6BA55F", "#A4CA64", "#E8ED69"]);
var x = d3.scale.linear()
.range([0, width])
.domain([0, data[0].length]);
var y = d3.scale.linear()
.range([height, 0])
.domain([0, maxval]);
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 stack = d3.layout.stack()
.offset("wiggle");
var layers = stack([data]);
var area = d3.svg.area()
.interpolate('cardinal')
.x(function (d, i) { return x(i); })
.y0(function (d) { return y(d.y0);})
.y1(function (d) { return y(d.y0 + d.y);});
svg.selectAll(".layer")
.data(layers)
.enter().append("path")
.attr("class", "layer")
.attr("d", function (d) {return area(d);})
.style("fill", function (d, i) {
return color(i);
});
var layers = stack([nest]);
var area = d3.svg.area()
.interpolate('cardinal')
.x(function (d, i) { return x(i); })
.y0(function (d) { return y(d.y0);})
.y1(function (d) { return y(d.y0 + d.y);});
svg.selectAll(".layer")
.data(layers)
.enter().append("path")
.attr("class", "layer")
.attr("d", function (d) {return area(d);})
.style("fill", function (d, i) {
return color(i);
});
What is the preferred/desired json format I need to massage my data into? Is this even my problem?
Thanks
I refactored things and managed to get it working... amongst other things I think I was polluting my global variable namespace.
http://jsfiddle.net/Nyquist212/00war1o6/
margin = {top: 20, right: 20, bottom: 20, left: 30};
width = 950 - margin.left - margin.right;
height = 250 - margin.top - margin.bottom;
colorrange = ["#B30000", "#E34A33", "#FC8D59", "#FDBB84", "#FDD49E", "#FEF0D9"];
parseDate = d3.time.format("%m/%d/%Y").parse;
x = d3.time.scale().range([margin.left, width]);
y = d3.scale.linear().range([height, 0]);
z = d3.scale.ordinal().range(colorrange);
xAxis = d3.svg.axis()
.scale(x)
.orient("bottom")
.ticks(d3.time.years);
yAxis = d3.svg.axis().scale(y);
nest = d3.nest()
.key(function(d) { return d.key; });
data.forEach(function(d) {
d.date = parseDate(d.date);
d.value= +d.value;
});
stack = d3.layout.stack()
.offset("silhouette")
.values(function(d) { return d.values; })
.x(function(d) { return d.date; })
.y(function(d) { return d.value; });
layers = stack(nest.entries(data));
area = d3.svg.area()
//.interpolate("cardinal")
.interpolate("basis")
.x(function(d) { return x(d.date); })
.y0(function(d) { return y(d.y0); })
.y1(function(d) { return y(d.y0 + d.y); });
svg = d3.select("#streamgraph").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 + ")");
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")
.attr("transform", "translate(" + width + ", 0)")
.call(yAxis.orient("right"));
svg.append("g")
.attr("class", "y axis")
.call(yAxis.orient("left"));
svg.selectAll(".layer")
.attr("opacity", 1)
.on("mouseover", function(d, i) {
svg.selectAll(".layer").transition()
.duration(250)
.attr("opacity", function(d, j) {
return j != i ? 0.6 : 1;
})
})
Related
The lines on my chart are displaying with a weird black filled-in shape instead of an actual line. Any idea what i'm doing wrong? The data is parsed in from a csv. It's a multiline chart where each series is a column in the csv(columns are: date, cat1, cat2, cat3, cat4).
Aside - I got this to work in a different fashion by defining each valueline and appending to the path. But I ran into issues with scaling the axis. I eventually need to have four plots on my html page, each with different axis scales (for example log scale on the y axis). Therefore I'll need y = d3.scaleLog().domain(my domain).range(range) and then in d3.line.y return y(my value).
var margin = {top: 20, right: 20, bottom: 30, left: 50},
w = 960 - margin.left - margin.right,
h = 500 - margin.top - margin.bottom;
var padding =20;
var xScale = d3.scaleTime()
.domain([d3.min(dataset,function (d) { return d.date }),d3.max(dataset,function (d) { return d.date }) ]).range([padding, w - padding * 2])
var yScale = d3.scaleLinear()
.domain([0, d3.max(dataset,function (d) { return d.cat1 }) ]).range([h- padding, padding])
var xAxis = d3.axisBottom().scale(xScale);
var yAxis = d3.axisLeft().scale(yScale);
var svg1 = 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 + ")");
var line = d3.line()
.x(function(d) { return xScale(d['date']); })
.y(function(d) { return yScale(d['cat1']); });
var line2 = d3.line()
.x(function(d) { return xScale(d['date']); })
.y(function(d) { return yScale(d['cat2']); });
var line3 = d3.line()
.x(function(d) { return xScale(d['date']); })
.y(function(d) { return yScale(d['cat3']); });
var line4 = d3.line()
.x(function(d) { return xScale(d['date']); })
.y(function(d) { return yScale(d['cat4']); });
var path = svg1.append('path').attr('d', line(dataset));
var path2 = svg1.append('path').attr('d', line2(dataset));
var path3 = svg1.append('path').attr('d', line3(dataset));
var path4 = svg1.append('path').attr('d', line4(dataset));
//draw points
var selectCircle = svg1.selectAll(".circle")
.data(dataset)
selectCircle.enter().append("circle")
.attr("class", "circle")
.attr("r", 10)
.attr("cx", function(d) {
return xScale(d.date)
})
.attr("cy", function(d) {
return yScale(d.cat1)
})
.attr("fill", "#FFC300")
selectCircle.enter().append("circle")
.attr("class", "circle")
.attr("r", 10)
.attr("cx", function(d) {
return xScale(d.date)
})
.attr("cy", function(d) {
return yScale(d.cat2)
})
.attr("fill", "#FF5733")
selectCircle.enter().append("circle")
.attr("class", "circle")
.attr("r", 10)
.attr("cx", function(d) {
return xScale(d.date)
})
.attr("cy", function(d) {
return yScale(d.cat3)
})
.attr("fill", "#C70039")
selectCircle.enter().append("circle")
.attr("class", "circle")
.attr("r", 10)
.attr("cx", function(d) {
return xScale(d.date)
})
.attr("cy", function(d) {
return yScale(d.cat4)
})
.attr("fill", "#900C3F")
svg1.append("g").attr("class", "axis").attr("transform", "translate(0," + (h - padding) + ")").call(xAxis);
//draw Y axis
svg1.append("g").attr("class", "axis").attr("transform", "translate(" + padding + ",0)").call(yAxis);
// add label
svg1.append("text").attr("x", (w/2)).attr("y", h+30).attr("text-anchor", "middle").text("Year");
svg1.append("text").attr("x", padding).attr("y", padding-20).attr("text-anchor", "middle").text("# of Events");
//add title
svg1.append("text").attr("x", (w/2)).attr("y", padding).attr("text-anchor", "middle").text("Events per Year by Category");
I have the following data structure as on screenshot. Could someone help me to display a multiline chart with it. I am stuck at setting the range and domain. As I am not able to find the minimum and maximum values. Looping at object, might be bad Idea, as there potentially going to be a lot of data.
Could someone suggest the correct way to do this?
Image with data structure
I am trying to use this as an example, and it has a similar task. But due to some circumstances I can not send data through AJAX in the same format as it is in this example.
http://jsfiddle.net/JYS8n/1/
var margin = {
top: 20,
right: 80,
bottom: 30,
left: 50
},
width = 360 - margin.left - margin.right,
height = 300 - 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.Value);
});
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(data.map(function (d) { return d.City; }));
data.forEach(function (kv) {
kv.Data.forEach(function (d) {
d.Date = parseDate(d.Date);
});
});
var cities = data;
var minX = d3.min(data, function (kv) { return d3.min(kv.Data, function (d) { return d.Date; }) });
var maxX = d3.max(data, function (kv) { return d3.max(kv.Data, function (d) { return d.Date; }) });
var minY = d3.min(data, function (kv) { return d3.min(kv.Data, function (d) { return d.Value; }) });
var maxY = d3.max(data, function (kv) { return d3.max(kv.Data, function (d) { return d.Value; }) });
x.domain([minX, maxX]);
y.domain([minY, maxY]);
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("Temperature (ºF)");
var city = svg.selectAll(".city")
.data(cities)
.enter().append("g")
.attr("class", "city");
city.append("path")
.attr("class", "line")
.attr("d", function (d) {
return line(d.Data);
})
.style("stroke", function (d) {
return color(d.City);
});
city.append("text")
.datum(function (d) {
return {
name: d.City,
date: d.Data[d.Data.length - 1].Date,
value: d.Data[d.Data.length - 1].Value
};
})
.attr("transform", function (d) {
return "translate(" + x(d.date) + "," + y(d.value) + ")";
})
.attr("x", 3)
.attr("dy", ".35em")
.text(function (d) {
return d.name;
});
I have to implement a bar chart in D3, but my values on the x axis are of type Date, data type which the D3 library should accept, but it seems to give me an error like this: attribute x: Expected length, "NaN".
This is the code for my bar chart:
//this holds the data that will be drawn
var data = [ {"yy":12,"mm":01,ppm_value:90000}, {"yy":11,"mm":02,ppm_value:50000}];;
//formats the date
var format = d3.time.format("%Y-%m-%d");
//define margins, height and width
var margin = {
top: 20,
right: 30,
bottom: 30,
left: 40
},
w = 1000 - margin.left - margin.right,
h = 500 - margin.top - margin.bottom;
/*var x = d3.time.scale()
//.domain([0, d3.max(data, function(d) { return d.date; })])
.range([0, w], 0.6);*/
var x = d3.time.scale()
.range([0, w]);
var y = d3.scale.linear()
//.domain([0, d3.max(data, function(d) { return d.ppm_value;})])
.range([h, 0]);
var xAxis = d3.svg.axis()
.scale(x)
.orient("bottom")
//.tickFormat(d3.time.format("%Y/%m"));
var yAxis = d3.svg.axis()
.scale(y)
.orient("left");
var zoom = d3.behavior.zoom()
.scaleExtent([1, 1])
.x(x)
.on("zoom", zoomed);
//create the svg
var chart = d3.select("#testChart").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 + ")")
.call(zoom);
var rect = chart.append("rect")
.attr("width", w)
.attr("height", h)
.style("fill", "none")
.style("pointer-events", "all");
//loops through data
data.forEach(function (d) {
//coerce to number
d.ppm_value = +d.ppm_value;
d.yy = +d.yy;
d.mm = +d.mm;
d.date = new Date("20" + d.yy + "/" +d.mm);
var dateTick = format(d.date);
d.date = dateTick;
console.log(d.ppm_value);
//console.log(dateTick);
//console.log(d.date);
});
//var dates = data.map(function(d){ return new Date("2016/01/03"); });
//console.log(formDate);
//map values onto x axis
x.domain([d3.min(data, function(d) { return d.date; }), d3.max(data, function(d) { return d.date; })])
// x.domain([0, d3.max(function(d) { return d.date; })]);
//map values onto y axis
y.domain([0, d3.max(data, function(d) { return d.ppm_value; })]);
chart.append("g")
.attr("class", "x axis")
.attr("transform", "translate(0," + h + ")")
.call(xAxis)
chart.append("g")
.attr("class", "y axis")
.call(yAxis);
var bars = chart.append("g")
.attr("class", "chartobjects");
bars.selectAll(".rect")
.data(data)
.enter().append("rect")
.attr("class", "rectBar")
.on("click",hello)
.attr('x', function(d) {
console.log(d.date);
return x(new Date(d.date));
})
.attr("y", function(d) {
return y(d.ppm_value);
console.log(d.ppm_value);
})
.attr("height", function(d) {
return h - y(d.ppm_value);
})
.attr("width", 15)
.attr("fill", function(d) {
return d.ppm_value > 6 ? "blue" : "red"
});
function hello() {
alert("Hello world!!")
}
function zoomed() {
console.log("Entered zoom function!!!");
var tx = Math.max(0, d3.event.translate[0])
//ty = Math.min(0, d3.event.translate[1]);
zoom.translate([tx]);
bars.attr("transform", "translate(" + [tx] + ")scale(" + d3.event.scale + ")");
chart.select(".x.axis")
.call(xAxis);
//chart.select(".y.axis")
// .call(yAxis);
}
I can not seem to get to the root of the problem. Am I doing something wrong ? What is the problem in your opinion ?
Thanks in advance!
i'm using the following code for generating multi line graph. I'm getting the result. But the graph looks like:
var margin = {top: 20, right: 20, bottom: 30, left: 50},
width = 1025 - margin.left - margin.right,
height = 339 - margin.top - margin.bottom;
var x = d3.scale.linear()
.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.days); })
.y(function(d) { return y(d.testRuns); });
var svg = d3.select("#application_status_graph").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.json("js/lineChartData.json", function(error, data) {
color.domain(d3.keys(data[0]).filter(function(key) {return key !== "days"; }));
data.forEach(function(d) {
d.days = parseInt(d.days);
});
var status = color.domain().map(function(name){
return{
name: name,
values: data.map(function(d){
return {days: d.days, testRuns: +d[name]};
})
}
});
x.domain(d3.extent(data, function(d) { return d.days; }));
y.domain([
d3.min(status, function(c) { return d3.min(c.values, function(v) { return v.testRuns; }); }),
d3.max(status, function(c) { return d3.max(c.values, function(v) { return v.testRuns; }); })
]);
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("Number of Test Runs");
var city = svg.selectAll(".city")
.data(status)
.enter().append("g")
.attr("class", "city");
city.append("path")
.attr("class", "line")
.attr("d", function(d) { return line(d.values); })
.style("stroke", function(d) { return color(d.name); });
city.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.days) + "," + y(d.value.testRuns) + ")"; })
.attr("x", 3)
.attr("dy", ".35em")
.text(function(d) { return d.name; });
});
But i need it in this form:
I'm new to d3. The attribute which i'm using is line only for both. But it is coming as curves. Any help is appreciated. Thanks
the problem is in var line = d3.svg.line()
.interpolate("basis")
.x(function(d) { return x(d.days); })
.y(function(d) { return y(d.testRuns); });
it should be changed to .interpolate("linear") to get the desired effect.
the wiki link explanes all types of line you can obtain:
https://github.com/mbostock/d3/wiki/SVG-Shapes#wiki-line_interpolate
hope this helps!
I'm trying to figure out how to include 2 or more graphs on my website. I am currently using the Area Graph and the Pie Graph. If I disable one of them, then the other works fine, but when I try to use both at the same time, it stops working.
JS code for the Area Graph
var margin = { top: 0, right: 0, bottom: 30, left: 50 },
width = 670 - margin.left - margin.right,
height = 326 - margin.top - margin.bottom;
var parseDate = d3.time.format("%d-%b-%y").parse;
var x = d3.time.scale()
.range([0, width]);
var y = d3.scale.linear()
.range([height, 0]);
var xAxis = d3.svg.axis()
.scale(x)
.orient("bottom");
var yAxis = d3.svg.axis()
.scale(y)
.orient("left");
var svg = d3.select("#watLenYearGra").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 area = d3.svg.area()
.x(function (d) { return x(d.date); })
.y0(height)
.y1(function (d) { return y(d.close); });
$("#watLenYear").click(function () {
$("#watLenYearGra").slideToggle("fast");
});
d3.csv("data.csv", function (error, data) {
data.forEach(function (d) {
d.date = parseDate(d.date);
d.close = +d.close;
});
x.domain(d3.extent(data, function (d) { return d.date; }));
y.domain([0, d3.max(data, function (d) { return d.close; })]);
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("Price ($)");
svg.append("path")
.datum(data)
.attr("class", "area")
.attr("d", area);
});
JS code for the Pie Graph
var width = 670,
height = 326,
radius = Math.min(width, height) / 2;
var color = d3.scale.ordinal()
.range(["#98abc5", "#8a89a6", "#7b6888", "#6b486b", "#a05d56", "#d0743c", "#ff8c00"]);
var arc = d3.svg.arc()
.outerRadius(radius - 10)
.innerRadius(0);
var pie = d3.layout.pie()
.sort(null)
.value(function (d) { return d.population; });
var svg = d3.select("#watLenSzGra").append("svg")
.attr("width", width)
.attr("height", height)
.append("g")
.attr("transform", "translate(" + width / 2 + "," + height / 2 + ")");
$("#watLenSz").click(function () {
$("#watLenSzGra").slideToggle("fast");
});
d3.csv("data1.csv", function (error, data) {
data.forEach(function (d) {
d.population = +d.population;
});
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.data.age); });
g.append("text")
.attr("transform", function (d) { return "translate(" + arc.centroid(d) + ")"; })
.attr("dy", ".35em")
.style("text-anchor", "middle")
.text(function (d) { return d.data.age; });
});
I think the problem lies with the scope of the variables (specifically the svg variable since they have the same name). Right now I have these sets of code in separate JS files, and I link them at the bottom of my body tag in the HTML file.
How can I limit these variables' scope to their files? Is that even the problem? Thanks
It should work if you use closures:
(function() {
var margin = { top: 0, right: 0, bottom: 30, left: 50 },
width = 670 - margin.left - margin.right,
height = 326 - margin.top - margin.bottom;
var parseDate = d3.time.format("%d-%b-%y").parse;
var x = d3.time.scale()
.range([0, width]);
var y = d3.scale.linear()
.range([height, 0]);
var xAxis = d3.svg.axis()
.scale(x)
.orient("bottom");
var yAxis = d3.svg.axis()
.scale(y)
.orient("left");
var svg = d3.select("#watLenYearGra").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 area = d3.svg.area()
.x(function (d) { return x(d.date); })
.y0(height)
.y1(function (d) { return y(d.close); });
$("#watLenYear").click(function () {
$("#watLenYearGra").slideToggle("fast");
});
d3.csv("data.csv", function (error, data) {
data.forEach(function (d) {
d.date = parseDate(d.date);
d.close = +d.close;
});
x.domain(d3.extent(data, function (d) { return d.date; }));
y.domain([0, d3.max(data, function (d) { return d.close; })]);
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("Price ($)");
svg.append("path")
.datum(data)
.attr("class", "area")
.attr("d", area);
});
})();
(function() {
var width = 670,
height = 326,
radius = Math.min(width, height) / 2;
var color = d3.scale.ordinal()
.range(["#98abc5", "#8a89a6", "#7b6888", "#6b486b", "#a05d56", "#d0743c", "#ff8c00"]);
var arc = d3.svg.arc()
.outerRadius(radius - 10)
.innerRadius(0);
var pie = d3.layout.pie()
.sort(null)
.value(function (d) { return d.population; });
var svg = d3.select("#watLenSzGra").append("svg")
.attr("width", width)
.attr("height", height)
.append("g")
.attr("transform", "translate(" + width / 2 + "," + height / 2 + ")");
$("#watLenSz").click(function () {
$("#watLenSzGra").slideToggle("fast");
});
d3.csv("data1.csv", function (error, data) {
data.forEach(function (d) {
d.population = +d.population;
});
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.data.age); });
g.append("text")
.attr("transform", function (d) { return "translate(" + arc.centroid(d) + ")"; })
.attr("dy", ".35em")
.style("text-anchor", "middle")
.text(function (d) { return d.data.age; });
});
})();