I am using D3.js to create a simple line graph.
<div>
<h6>Price Over Time</h6>
<div id="priceOverTimeChart"></div>
</div>
// Set the dimensions of the canvas / graph
var margin = {top: 30, right: 20, bottom: 30, left: 50},
width = 800 - margin.left - margin.right,
height = 270 - margin.top - margin.bottom;
// Parse the date / time
var parseDate = d3.time.format("%d-%b-%y").parse;
// Set the ranges
var x = d3.time.scale().range([0, width]);
var y = d3.scale.linear().range([height, 0]);
// Define the axes
var xAxis = d3.svg.axis().scale(x)
.orient("bottom").ticks(10);
var yAxis = d3.svg.axis().scale(y)
.orient("left").ticks(10);
// Define the line
var valueline = d3.svg.line()
.x(function(d) { return x(d.date); })
.y(function(d) { return y(d.close); });
// Adds the svg canvas
var svg = d3.select("#priceOverTimeChart")
.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("data.csv", function(error, data) {
data.forEach(function(d) {
d.date = parseDate(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 valueline path.
svg.append("path")
.attr("class", "line")
.attr("d", valueline(data));
// Add the X Axis
svg.append("g")
.attr("class", "x axis")
.attr("transform", "translate(0," + height + ")")
.call(xAxis);
// Add the Y Axis
svg.append("g")
.attr("class", "y axis")
.call(yAxis);
});
The data for the line graph uses the following data format:
26-Apr-12,0.048
25-Apr-12,0.048
24-Apr-12,0.048
I would like to add an optional string to each record so it looks like:
26-Apr-12,0.048, "product 1 launch",
25-Apr-12,0.048, "product 2",
24-Apr-12,0.048, "product 3"
26-Apr-12,0.048, null
25-Apr-12,0.048, null
24-Apr-12,0.048, null
The graph would then look something like this with the labels on it:
Graph with optional labels
How can I accomplish this? Thanks in advance!
Appending texts to the corresponding x, y position will do the trick.
Please refer this working JS Fiddle
svg.append("g").selectAll("text")
.data(data)
.enter()
.append("text")
.attr("x", function(d) { return x(d.date) - paddingForText })
.attr("y", function(d) { return y(d.close) + paddingForText })
.attr("fill", "red")
.text(function(d) { return d.notes });
I got this CSV data (just a sample- it is bigger):
value;count
25;1
28;1
29;3
31;2
34;1
36;1
37;2
I could not find any example which shows line chart without Date function so I modified the example to make linear scales but it does not work. I am trying to make line chart with this code but I get Error: attribute d: Expected number
// 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;
// set the ranges
var x = d3.scaleLinear().range([0, width]);
var y = d3.scaleLinear().range([height, 0]);
// define the line
var valueline = d3.line()
.x(function(d) { return x(d.value); })
.y(function(d) { return y(d.count); });
// 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 + ")");
// Get the data
d3.csv("exp2.csv", function(error, data) {
if (error) throw error;
// format the data
data.forEach(function(d) {
d.value = +d.value;
d.count = +d.count;
});
// Scale the range of the data
x.domain([0, d3.max(data, function(d) { return d.value; })]);
y.domain([0, d3.max(data, function(d) { return d.count; })]);
// Add the valueline path.
svg.append("path")
.data([data])
.attr("class", "line")
.attr("d", valueline);
// 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));
});
As #GerardoFurtado pointed out your data is not csv (ie comma separated) but semi-colon separated. My initial thought was to close this as a duplicate of this question but it looks like the syntax has changed enough in version 4 that I new answer is warranted.
Do this:
// set up a parser
var parser = d3.dsvFormat(';').parse;
// get the data as text
d3.text("exp2.csv", function(error, data) {
if (error) throw error;
// now parse the data
data = parser(data);
// format the data
data.forEach(function(d) {
d.value = +d.value;
d.count = +d.count;
});
...
Here's a full running example.
I'm tring to create a multi-line chart using D3.js. Here is my sample csv data:
A,B,C,D,E,F,G,date
53831,72169.87,54219,72555,63466,115312,126390,4/26/16
53031,70901.11,5976,5111,62388,111626,123198,7/10/16
51834,69917.12,5449,4902,62990,114296,124833,4/24/16
54637,73016.92,58535,77379,63090,113216,125261,6/14/16
54801,73072.4,57997,75674,63090,113216,125261,6/27/16
53578,71718.19,51085,69152,63370,115061,125949,5/3/16
51679,68897.14,6021,5421,61514,110330,121972,7/24/16
Here is my code snippet. However I keep seeing the error like d is not an expected number(as title shows). Can anyone please point me out?
Also I feel like the way I'm parsing data is ugly (two for loop). Any suggestions are welcome.
// Set the dimensions of the canvas / graph
var margin = {
top: 30,
right: 20,
bottom: 30,
left: 50
},
width = 800 - margin.left - margin.right,
height = 500 - margin.top - margin.bottom;
// Parse the date / time
var parseDate = d3.time.format("%b %Y").parse;
// Set the ranges
var x = d3.time.scale().range([0, width]);
var y = d3.scale.linear().range([height, 0]);
// Define the axes
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);
});
// Adds the svg canvas
var svg = d3.select("#d3-line-chart")
.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("test.csv", function (error, data) {
var res = [];
var cols = d3.keys(data[0])
.filter(function (key) {
return key;
});
for (var j = 0; j < cols.length - 1; j++) {
var col = cols[j];
var row = [];
for (var i = 0; i < data.length; i++) {
row.push({
symbol: col,
date: data[i]["date"],
value: +data[i][col]
});
}
res.push(row);
}
// Scale the range of the data
x.domain(d3.extent(res, function (d) {
return d.date;
}));
y.domain([0, d3.max(res, function (d) {
return d.value;
})]);
svg.selectAll(".line")
.data(res)
.enter().append("path")
.attr("class", "line")
.attr("d", line);
// Add the X Axis
svg.append("g")
.attr("class", "x axis")
.attr("transform", "translate(0," + height + ")")
.call(xAxis);
// Add the Y Axis
svg.append("g")
.attr("class", "y axis")
.call(yAxis);
});
Firstly, provide sorted data in the CSV on the basis of date so:
Instead of this:
A,B,C,D,E,F,G,date
53831,72169.87,54219,72555,63466,115312,126390,4/26/16
53031,70901.11,5976,5111,62388,111626,123198,7/10/16
51834,69917.12,5449,4902,62990,114296,124833,4/24/16
54637,73016.92,58535,77379,63090,113216,125261,6/14/16
54801,73072.4,57997,75674,63090,113216,125261,6/27/16
53578,71718.19,51085,69152,63370,115061,125949,5/3/16
51679,68897.14,6021,5421,61514,110330,121972,7/24/16
Provide sorted CSV:
A,B,C,D,E,F,G,date
51834,69917.12,5449,4902,62990,114296,124833,4/24/16
53831,72169.87,54219,72555,63466,115312,126390,4/26/16
53578,71718.19,51085,69152,63370,115061,125949,5/3/16
54637,73016.92,58535,77379,63090,113216,125261,6/14/16
54801,73072.4,57997,75674,63090,113216,125261,6/27/16
53031,70901.11,5976,5111,62388,111626,123198,7/10/16
51679,68897.14,6021,5421,61514,110330,121972,7/24/16
Secondly:
The date parser you providing is incorrect:
var parseDate = d3.time.format("%b %Y").parse;
Should have been this:
var parseDate = d3.time.format("%m/%d/%Y").parse;
Because your date is in the format ,4/26/16.
Thirdly,
The way you are calculating the x and y domain extent is wrong:
So instead of this:
// Scale the range of the data
x.domain(d3.extent(res, function (d) {
return d.date;
}));
y.domain([0, d3.max(res, function (d) {
return d.value;
})]);
It should have been:
x.domain(d3.extent(data, function (d) {
return parseDate(d.date);
}));
y.domain([0, d3.max(res, function (d) {
return d3.max(d, function(d2){console.log(d2);return d2.value;});
})]);
Reason: the res array you creating is an array inside an array so need that handling in here.
Working code here
Here is my code:
// Set the dimensions of the canvas / graph
var margin = {top: 30, right: 20, bottom: 30, left: 50},
width = 600 - margin.left - margin.right,
height = 270 - margin.top - margin.bottom;
// Parse the date / time
var parseDate = d3.time.format("%d-%m-%y").parse;
// Set the ranges
var x = d3.time.scale().range([0, width]);
var y = d3.scale.linear().range([height, 0]);
// Define the axes
var xAxis = d3.svg.axis().scale(x)
.orient("bottom").ticks(5);
var yAxis = d3.svg.axis().scale(y)
.orient("left").ticks(5);
// Define the line
var valueline = d3.svg.line()
.x(function(d) { return x(+d.AtTime); })
.y(function(d) { return y(d.Temperature); });
// Adds the svg canvas
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("../datasets/test.csv", function(error, data) {
data.forEach(function(d) {
d.AtTime = parseDate(d.AtTime);
d.Temperature = +d.Temperature;
});
console.log(data);
// Scale the range of the data
x.domain(d3.extent(data, function(d) { return d.AtTime; }));
y.domain([0, d3.max(data, function(d) { return d.Temperature; })]);
// Add the valueline path.
svg.append("path")
.attr("class", "line")
.attr("d", valueline(data));
// Add the X Axis
svg.append("g")
.attr("class", "x axis")
.attr("transform", "translate(0," + height + ")")
.call(xAxis);
// Add the Y Axis
svg.append("g")
.attr("class", "y axis")
.call(yAxis);
});
And the line chat just can not show...Here is my simple csv dataset:
Category,Temperature,Pressure,AtTime
A,76,400,1-1-2014
B,23,239,2-3-2013
C,38.6,378,2-6-2016
D,43.2,289,3-5-2015
E,49,501,3-5-2015
I used console.log(data) to check, and I found that AtTime has null value.... I do not why..Anyone could help me with this? Thanks!
It's one character in your time format string.
//var parseDate = d3.time.format("%d-%m-%y").parse; // lower-case Y expects two digits only
var parseDate = d3.time.format("%d-%m-%Y").parse; // use capital Y for 4 digit years
see https://github.com/d3/d3/wiki/Time-Formatting#format for all the different modifiers
There is a mistake in your dates, I parsed them and ordered them so the line would render correctly.
data.forEach(function(d) {
var dateArr = d.AtTime.split('-');
d.AtTime = new Date(+dateArr[2], +dateArr[1], +dateArr[0]);
d.Temperature = +d.Temperature;
})
data.sort(function(a, b) {
return new Date(b.AtTime) - new Date(a.AtTime);
});
Remove the implicit number cast in your valueLine function:
var valueline = d3.svg.line()
.x(function(d) {
return x(d.AtTime);
})
.y(function(d) {
return y(d.Temperature);
});
And finally if you don't have any css add a stroke to your line so it renders:
svg.append("path")
.attr("class", "line")
.attr('stroke', 'red')
.attr("d", valueline(data));
Working plnkr: https://plnkr.co/edit/ujFDtHkdtC2EeBwJ1foU?p=preview
Fiddle (updating it now): http://jsfiddle.net/hy6r1peb/
I'm attempting to plot points via line graph based on the following data pulled from a JSON API:
var data = {
"january": [
{"total": "31", "date": "2015-01-01"},
{"total": "19", "date": "2015-01-05"},
{"total": "4", "date": "2015-01-10"}
], "error": false, "status": 200
};
This is my code from the d3noobs tutorial. It throws an error on line 1 of d3.js. Where have I gone wrong?
// Set the dimensions of the canvas / graph
var margin = {top: 30, right: 20, bottom: 30, left: 50},
width = 600 - margin.left - margin.right,
height = 270 - margin.top - margin.bottom;
// Parse the date / time
var parseDate = d3.time.format("%Y-%m-%d").parse;
// Set the ranges
var x = d3.time.scale().range([0, width]);
var y = d3.scale.linear().range([height, 0]);
// Define the axes
var xAxis = d3.svg.axis().scale(x)
.orient("bottom").ticks(5);
var yAxis = d3.svg.axis().scale(y)
.orient("left").ticks(5);
// Define the line
var valueline = d3.svg.line()
.x(function(d) { return x(d.date); })
.y(function(d) { return y(d.close); });
// Adds the svg canvas
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.json(URL, function(error, data) {
if (error) return console.warn(error);
else {
data = data.january;
data.forEach(function(d) {
d.date = parseDate(d.date);
d.total = +d.total;
});
}
// 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.total; })]);
// Add the valueline path.
svg.append("path")
.attr("class", "line")
.attr("d", valueline(data));
// Add the X Axis
svg.append("g")
.attr("class", "x axis")
.attr("transform", "translate(0," + height + ")")
.call(xAxis);
// Add the Y Axis
svg.append("g")
.attr("class", "y axis")
.call(yAxis);
});
I typo'd and had d.close in there when I shouldn't have. Doing this purely to close the question -- all credit goes to Terry for his sharp eyes.