Skip to content

Instantly share code, notes, and snippets.

@d3noob
Last active June 19, 2020 06:54
Show Gist options
  • Save d3noob/8603837 to your computer and use it in GitHub Desktop.
Save d3noob/8603837 to your computer and use it in GitHub Desktop.
Multiple line graphs with labels
license: mit

This is an example of a line graph with multiple lines and labels at the end of those lines.

Created in responce to a question on d3noob.org

A few points to note:

  1. The csv file starts with the newest data point and ends with the oldest. If you needed to get the 'y' position of the last point in the array you will need to use data[data.length-1].open rather than data[0].open.

  2. I've left some console.log calls in the code as small demos.

  3. The script makes the adjustment for scale using the y function.

date close open
1-May-12 68.13 34.12
30-Apr-12 63.98 45.56
27-Apr-12 67.00 67.89
26-Apr-12 89.70 78.54
25-Apr-12 99.00 89.23
24-Apr-12 130.28 99.23
23-Apr-12 166.70 101.34
20-Apr-12 234.98 122.34
19-Apr-12 345.44 134.56
18-Apr-12 443.34 160.45
17-Apr-12 543.70 180.34
16-Apr-12 580.13 210.23
13-Apr-12 605.23 223.45
12-Apr-12 622.77 201.56
11-Apr-12 626.20 212.67
10-Apr-12 628.44 310.45
9-Apr-12 636.23 350.45
5-Apr-12 633.68 410.23
4-Apr-12 624.31 430.56
3-Apr-12 629.32 460.34
2-Apr-12 618.63 510.34
30-Mar-12 599.55 534.23
29-Mar-12 609.86 578.23
28-Mar-12 617.62 590.12
27-Mar-12 614.48 560.34
26-Mar-12 606.98 580.12
<!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="https://cdnjs.cloudflare.com/ajax/libs/d3/3.5.17/d3.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 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 valueline2 = d3.svg.line()
.x(function(d) { return x(d.date); })
.y(function(d) { return y(d.open); });
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("data2b.csv", function(error, data) {
data.forEach(function(d) {
d.date = parseDate(d.date);
d.close = +d.close;
d.open = +d.open;
});
// 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 Math.max(d.close, d.open); })]);
svg.append("path") // Add the valueline path.
.attr("class", "line")
.attr("d", valueline(data));
svg.append("path") // Add the valueline2 path.
.attr("class", "line")
.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") // Add the Y Axis
.attr("class", "y axis")
.call(yAxis);
svg.append("text")
.attr("transform", "translate(" + (width+3) + "," + y(data[0].open) + ")")
.attr("dy", ".35em")
.attr("text-anchor", "start")
.style("fill", "red")
.text("Open");
svg.append("text")
.attr("transform", "translate(" + (width+3) + "," + y(data[0].close) + ")")
.attr("dy", ".35em")
.attr("text-anchor", "start")
.style("fill", "steelblue")
.text("Close");
console.log(data.length-1);
console.log(data[data.length-1].open);
console.log(data[0].open);
console.log(y(data[0].open));
console.log(y(data[0].close));
});
</script>
</body>
@lubegasimon
Copy link

lubegasimon commented Oct 6, 2019

On line 88, this, .attr("transform", "translate("+(width+3)+","+y(data[0].open)+")") can be made dynamic this way, .attr("transform", "translate(" + (width + 3) + "," + (d => d3.max(d, d=> y(d.close)))(data) + ")"), because I believe there can be cases where one needs to query some specific entities out of many and they don't know what entity will take on the first position as explained in https://leanpub.com/D3-Tips-and-Tricks/read#leanpub-auto-labelling-multiple-lines-on-a-graph. Take an example where you're given 1000 years, and you're asked to query the years where the number of females was higher than that of males, the result will most probably not be in order.. and so you need to make the code dynamic.

Basically, what this code snippet ( .attr("transform", "translate(" + (width + 3) + "," + (d => d3.max(d, d=> y(d.close)))(data) + ")") ) does in that context, is to grab the position of the value that is maximum respective of what line.

alternatively, it could be written this way,
const returnPosForMaxValue = (data) => d3.max(data, d=> y(d.close)); the called in place, .attr("transform", "translate(" + (width + 3) + "," + returnPosForMaxValue(data) + ")")

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment