I have been working through the "simple graph" example in the D3 Tips and Tricks book here: https://leanpub.com/D3-Tips-and-Tricks/read
And I am working on the Update Data Dynamically section, but its not working and I cannot figure out why. The alternate data is being read, the X and Y axis are being adjusted, but the value line itself is never updated. I am using v3.4.6 of D3 and tested with the latest Firefox and Chrome. Can someone help me spot the error?
Example HTML:
<!DOCTYPE html>
<meta charset="utf-8">
<style>
body { font: 12px Arial;}
path {
stroke: steelblue;
stroke-width: 2;
fill: none;
}
.axis path,
.axis line {
fill: none;
stroke: grey;
stroke-width: 1;
shape-rendering: crispEdges;
}
</style>
<body>
<script type="text/javascript" src="d3/d3.js"></script>
<script>
var margin = {top: 30, right: 20, bottom: 30, left: 50},
width = 600 - margin.left - margin.right,
height = 270 - 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").ticks(5);
var yAxis = d3.svg.axis().scale(y)
.orient("left").ticks(5);
var valueline = d3.svg.line()
.x(function(d) { return x(d.date); })
.y(function(d) { return y(d.close); });
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("simple.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; })]);
svg.append("path") // Add the valueline path.
.attr("d", valueline(data));
svg.append("g") // Add the X Axis
.attr("class", "x axis")
.attr("transform", "translate(0," + height + ")")
.call(xAxis);
svg.append("g") // Add the Y Axis
.attr("class", "y axis")
.call(yAxis);
});
function updateData() {
// Get the data again
d3.csv("simple-alt.csv", function(error, data) {
data.forEach(function(d) {
d.date = parseDate(d.date);
d.close = +d.close;
});
// Scale the range of the data again
x.domain(d3.extent(data, function(d) { return d.date; }));
y.domain([0, d3.max(data, function(d) { return d.close; })]);
// Select the section we want to apply our changes to
var svg = d3.select("body").transition();
// Make the changes
svg.select(".line") // change the line
.duration(750)
.attr("d", valueline(data));
svg.select(".x.axis") // change the x axis
.duration(750)
.call(xAxis);
svg.select(".y.axis") // change the y axis
.duration(750)
.call(yAxis);
});
}
</script>
<div id="option">
<input name="updateButton"
type="button"
value="Update"
onclick="updateData()"
/>
</div>
</body>
Example 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
Example Alt Data:
date,close
10-May-12,99.55
8-May-12,76.86
6-May-12,67.62
4-May-12,64.48
2-May-12,60.98
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,90.28
23-Apr-12,106.70
20-Apr-12,94.98
19-Apr-12,85.44
18-Apr-12,73.34
17-Apr-12,53.70
16-Apr-12,50.13
13-Apr-12,65.23
12-Apr-12,62.77
11-Apr-12,66.20
10-Apr-12,68.44
9-Apr-12,66.23
5-Apr-12,63.68
4-Apr-12,64.31
3-Apr-12,69.32
2-Apr-12,61.63
You haven't declared the path to be of class line. So your later selection svg.select(".line") is empty. Working demo here.
Related
I have trying to add another graph line to show another set of data. I am struggling to add another line of data. the technologies i am using are, D3.js, HTML and CSS.
Could someone help me with adding another line of data into this graph please?
CSS -
<style>
.axis path,
.axis line {
fill: none;
stroke: #000;
shape-rendering: crispEdges;
}
.x.axis path {
display: none;
}
.line {
fill: none;
stroke: steelblue;
stroke-width: 1.5px;
}
</style>
and this is my D3.js to create the graph.
<script src="http://d3js.org/d3.v3.min.js"></script>
<script>
var arrData = [
["2012-10-02",200],
["2012-10-09", 300],
["2012-10-12", 150]];
var margin = {top: 20, right: 20, 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 xAxis = d3.svg.axis()
.scale(x)
.orient("bottom");
var yAxis = d3.svg.axis()
.scale(y)
.orient("left");
var line = d3.svg.line()
.x(function(d) { return x(d.date); })
.y(function(d) { return y(d.close); });
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 = arrData.map(function(d) {
return {
date: parseDate(d[0]),
close: d[1]
};
});
console.log(data);
x.domain(d3.extent(data, function(d) { return d.date; }));
y.domain(d3.extent(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", "line")
.attr("d", line);
</script>
Well, that would be really simple, since you already done the job. The working example is here and what I did was simply copying your pieces of code according to the new data arrData2
Given a new data:
var arrData2 = [
["2012-10-02",250],
["2012-10-09", 200],
["2012-10-12", 100]
];
You just have to adjust your data, as you did before:
var data2 = arrData2.map(function(d) {
return {
date: parseDate(d[0]),
close: d[1]
};
});
And add a new line according to data2:
svg.append("path")
.datum(data2)
.attr("class", "line")
.attr("d", line);
Js line Chart and i am able to draw chart.While i am using json data I am getting:
Error: Cannot read property 'length' of null(…)
My function is like. i am getting error while parsing data. I am getting json response correctly, but I am enable to draw a chart. Can anyone tell me what is wrong i am doing?
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-%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(5);
var yAxis = d3.svg.axis().scale(y)
.orient("left").ticks(20);
// Define the line
var valueline = d3.svg.line()
.x(function (d) { return x(d.dategraph); })
.y(function (d) { return y(d.assetcount); });
// Adds the svg canvas
var svg = d3.select("linegrapg")
.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 = [{"dategraph":"16-Nov-16","assetcount":299},{"dategraph":"19-Nov-16","assetcount":0},
{"dategraph":"08-Nov-16","assetcount":18},{"dategraph":"14-Nov-16","assetcount":10},
{"dategraph":"17-Nov-16","assetcount":2},{"dategraph":"18-Nov-16","assetcount":0}]
data = JSON.parse(dataset.d);
data.forEach(function (d) {
d.Letter = parseDate(d.dategraph);
d.Freq = +d.assetcount;
});
// Scale the range of the data
x.domain(d3.extent(data, function (d) { return d.Letter; }));
y.domain([0, d3.max(data, function (d) { return d.Freq; })]);
// 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);
}
}
Based on your question, the code runs by commenting out the JSON.parse function call and the two trailing curly braces. The data variable is already an array of objects so there is no need to de-serialize it (from a string type). Other than that, I wasn't able to reproduce the error you were getting. Have a look at the code snippet below.
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-%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(5);
var yAxis = d3.svg.axis().scale(y)
.orient("left").ticks(20);
// Define the line
var valueline = d3.svg.line()
.x(function(d) {
return x(d.dategraph);
})
.y(function(d) {
return y(d.assetcount);
});
// 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 + ")");
var data = [{
"dategraph": "16-Nov-16",
"assetcount": 299
}, {
"dategraph": "19-Nov-16",
"assetcount": 0
}, {
"dategraph": "08-Nov-16",
"assetcount": 18
}, {
"dategraph": "14-Nov-16",
"assetcount": 10
}, {
"dategraph": "17-Nov-16",
"assetcount": 2
}, {
"dategraph": "18-Nov-16",
"assetcount": 0
}];
//var data = JSON.parse(dataset);
data.forEach(function(d) {
d.Letter = parseDate(d.dategraph);
d.Freq = +d.assetcount;
});
// Scale the range of the data
x.domain(d3.extent(data, function(d) {
return d.Letter;
}));
y.domain([0, d3.max(data, function(d) {
return d.Freq;
})]);
// 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);
<script src="https://d3js.org/d3.v3.min.js"></script>
My Working Example with tooltip
<!DOCTYPE html>
<meta charset="utf-8">
<style> /* set the CSS */
body { font: 12px Arial;}
path {
stroke: steelblue;
stroke-width: 2;
fill: none;
}
.axis path,
.axis line {
fill: none;
stroke: grey;
stroke-width: 1;
shape-rendering: crispEdges;
}
div.tip {
position: absolute;
text-align: center;
width: auto;
height: auto;
padding: 2px;
font: 12px sans-serif;
background: black;
border: 0px;
border-radius: 8px;
pointer-events: none;
color:white;
border-radius: 8px 8px 8px 8px;
}
</style>
<body>
<!-- load the d3.js library -->
<script src="http://d3js.org/d3.v3.min.js"></script>
<script>
// 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-%b-%y").parse;
var formatTime = d3.time.format("%e %B");
// 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); });
// Define the div for the tooltip
var div = d3.select("body").append("div")
.attr("class", "tip")
.style("opacity", 0);
// 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
var data = [
{"date":"08-Nov-16","close":299},
{"date":"09-Nov-16","close":10},
{"date":"10-Nov-16","close":18},
{"date":"11-Nov-16","close":10},
{"date":"12-Nov-16","close":2},
{"date":"13-Nov-16","close":50}];
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 scatterplot
svg.selectAll("dot")
.data(data)
.enter().append("circle")
.attr("r", 5)
.attr("cx", function(d) { return x(d.date); })
.attr("cy", function(d) { return y(d.close); })
.on("mouseover", function(d) {
div.transition()
.duration(200)
.style("opacity", .9);
div .html(formatTime(d.date) + "<br/>" + d.close)
.style("left", (d3.event.pageX) + "px")
.style("top", (d3.event.pageY - 28) + "px");
})
.on("mouseout", function(d) {
div.transition()
.duration(500)
.style("opacity", 0);
});
// 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);
</script>
</body>
I'm using this snippet to understand how d3.js works.
I changed this line of code -> var parseDate = d3.time.format('%H-%M-%e-%b-%y').parse;
<!DOCTYPE html>
<meta charset="utf-8">
<style>
body {
font: 10px sans-serif;
}
.axis path,
.axis line {
fill: none;
stroke: #000;
shape-rendering: crispEdges;
}
.x.axis path {
display: none;
}
.line {
fill: none;
stroke: steelblue;
stroke-width: 1.5px;
}
</style>
<body>
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/3.5.5/d3.min.js"></script>
<script>
var margin = {top: 20, right: 20, bottom: 30, left: 50},
width = 1000 - margin.left - margin.right,
height = 600 - margin.top - margin.bottom;
var parseDate = d3.time.format('%H-%M-%e-%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 line = d3.svg.line()
.x(function(d) { return x(d.date); })
.y(function(d) { return y(d.close); });
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.tsv("data/plottingData/first.tsv", function(error, data) {
if (error) throw error;
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(d3.extent(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)
svg.append("path")
.datum(data)
.attr("class", "line")
.attr("d", line);
});
</script>
because the file is formatted in this way:
11-30-1-May-12 582.13
10-30-1-Apr-12 382.13
9-30-1-Mar-12 482.13
...
but I'm continuing to view only day-month into x-Axis.
what's wrong?
thanks in advance.
You are parsing the date in correct format, but on X-axis you did not provide the format, on which you want to show date. So just use "xAxis.tickFormat(parseDate)" in your code. Example is here:
d3.svg.axis()
.scale(x)
.orient("bottom").tickFormat(d3.time.format('%H-%M-%e-%b-%y'));
d3 noob here.
I'd like to draw something like this:
(http://bl.ocks.org/mbostock/3883195)
But with a heat aspect, sort of like this:
Code (from above link):
<!DOCTYPE html>
<meta charset="utf-8">
<style>
body {
font: 10px sans-serif;
}
.axis path,
.axis line {
fill: none;
stroke: #000;
shape-rendering: crispEdges;
}
.area {
fill: steelblue;
}
</style>
<body>
<center>
<div>
<script src="http://d3js.org/d3.v3.js"></script>
<script>
var margin = {top: 20, right: 20, bottom: 30, left: 50},
width = 960 - margin.left - margin.right,
height = 500 - 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 area = d3.svg.area()
.x(function(d) { return x(d.date); })
.y0(height)
.y1(function(d) { return y(d.close); });
var svg = d3.select("div").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.tsv("data.tsv", 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("path")
.datum(data)
.attr("class", "area")
.attr("d", area);
svg.append("g")
.attr("class", "x axis")
.attr("transform", "translate(0," + height + ")")
// .attr("style", "stroke: #001")
.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 ($)");
});
</script>
</div>
</center>
</body>
Perhaps changing the area variable in someway? I could add another column to the data, say, color. But how would I apply the color to the "vertical line" of each day?
Thoughts? Thanks in advance!
I am having a formatting issue with a d3 graphical representation.
The X axis is entered in years, which I thought would be easier to treat as any other number (instead of as dateFormat... %Y )
The output is automatically (?) adds the comma for the thousandths place- my assumption.
Is there a way to keep this the 4 dig int that it is?
(this is my first foray into HTML, CSS, JS and d3-- thank you for empathy/comments)
Thanks.
<!DOCTYPE html>
<meta charset="utf-8">
<style>
body { font: 12px Arial;}
path {
stroke: steelblue;
stroke-width: 2;
fill: none;
}
.axis path,
.axis line {
fill: none;
stroke: grey;
stroke-width: 1;
shape-rendering: crispEdges;
}
</style>
<body>
<script src="http://d3js.org/d3.v3.min.js"></script>
<script>
var margin = {top: 30, right: 40, bottom: 30, left: 50},
width = 600 - margin.left - margin.right,
height = 270 - margin.top - margin.bottom;
var x = d3.scale.linear().range([0, width]);
var y0 = d3.scale.linear().range([height, 0]);
var y1 = d3.scale.linear().range([height, 0]);
var xAxis = d3.svg.axis().scale(x)
.orient("bottom").ticks(5);
var yAxisLeft = d3.svg.axis().scale(y0)
.orient("left").ticks(5);
var yAxisRight = d3.svg.axis().scale(y1)
.orient("right").ticks(5);
var valueline = d3.svg.line()
.x(function(d) { return x(d.year); })
.y(function(d) { return y0(d.freq); });
var valueline2 = d3.svg.line()
.x(function(d) { return x(d.year); })
.y(function(d) { return y1(d.fn); });
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("duallines2.csv", function(error, data) {
data.forEach(function(d) {
d.year = +(d.year);
d.freq = +d.freq;
d.fn = +d.fn;
});
// Scale the range of the data
x.domain(d3.extent(data, function(d) { return d.year; }));
y0.domain([0, d3.max(data, function(d) {
return Math.max(d.freq); })]);
y1.domain([0, d3.max(data, function(d) {
return Math.max(d.fn); })]);
svg.append("path") // Add the valueline path.
.attr("d", valueline(data));
svg.append("path") // Add the valueline2 path.
.style("stroke", "red")
.attr("d", valueline2(data));
svg.append("g") // Add the X Axis
.attr("class", "x axis")
.attr("transform", "translate(0," + height + ")")
.call(xAxis);
svg.append("g")
.attr("class", "y axis")
.style("fill", "steelblue")
.call(yAxisLeft);
svg.append("g")
.attr("class", "y axis")
.attr("transform", "translate(" + width + " ,0)")
.style("fill", "red")
.call(yAxisRight);
});
</script>
</body>
The datafile looks like this:
"duallines2.csv"
year,freq,fn
1950,58.13,3.41
1951,53.98,4.55
1952,67.00,6.78
1953,89.70,7.85
1954,99.00,8.92
1955,130.28,9.92
1956,166.70,10.13
1957,234.98,12.23
1958,345.44,13.45
1959,443.34,16.04
1960,543.70,18.03
1961,580.13,21.02
1962,605.23,22.34
1963,622.77,20.15
1964,626.20,21.26
1965,628.44,31.04
1966,636.23,35.04
1967,633.68,41.02
1968,624.31,43.05
1969,629.32,46.03
1970,618.63,51.03
1971,599.55,53.42
1973,609.86,57.82
1974,617.62,59.01
1975,614.48,56.03
1976,606.98,58.01
You can define your own format -- in this case making each xAxis tick label an integer -- and then tell the xAxis to use that format:
var format = d3.format("d");
var xAxis = d3.svg.axis()
.scale(x)
.orient("bottom")
.ticks(5)
.tickFormat(format); // <----- tell the xAxis to use your custom formatter
And here's the updated output you'll get:
See the docs on formatting numbers here.