I have the following data in a csv file named salesData.csv
:
let salesData = [
{
year : 2021,
sales : 171,
expenses : 64
},
{
year : 2020,
sales : 145,
expenses : 64
},
{
year : 2019,
sales : 147,
expenses : 64
},
{
year : 2018,
sales : 161,
expenses : 64
},
{
year : 2017,
sales : 171,
expenses : 64
},
{
year : 2016,
sales : 141,
expenses : 52
},
{
year : 2015,
sales : 115,
expenses : 52
},
{
year : 2014,
sales : 132,
expenses : 52
},
{
year : 2013,
sales : 146,
expenses : 52
}
]
My goal is to render a line chart based on user selection of a "metric" (in this case, either sales
or expenses
).
I can render a bar chart with this data, but not a line chart based on the same data. Here's what it looks like:
Why does the line chart not render properly?
Thanks in advance for your help!
Here is my code:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>Sales Data</title>
<style type="text/css">
.line {
fill: none;
stroke: teal;
stroke-width: 2;
}
</style>
</head>
<body>
<div id="chart-area"></div>
<script type="text/javascript">
let margin = {top: 40, right: 40, bottom: 60, left: 60};
let width = 600 - margin.left - margin.right;
let height = 500 - margin.top - margin.bottom;
let svg = d3.select("#chart-area").append("svg")
.attr('viewBox', [0, 0, width + margin.left + margin.right, height + margin.top + margin.bottom] )
.append("g")
.attr("transform", "translate(" + margin.left + "," + margin.top + ")");
let padding = 30;
// Date parser
let parseDate = d3.timeParse("%Y");
// Creates scale functions
let xScale = d3.scaleTime()
.range([ 0, width ]);
let yScale = d3.scaleLinear()
.range([ height, 0 ]);
// Defines axes
let xAxis = d3.axisBottom()
.scale(xScale);
let yAxis = d3.axisLeft()
.scale(yScale);
let xAxisGroup = svg.append('g')
.attr('class', 'axis')
.attr('transform', 'translate(0,' + height + ')')
.call(xAxis);
let yAxisGroup = svg.append('g')
.attr('class', 'axis')
.attr('transform', 'translate(0, 0)')
.call(yAxis);
let xAxisLabel = svg.append('text')
.attr('id', 'y-axis-label')
.attr('class', 'axis-label');
// Initialize data
loadSalesData();
// Data
let data;
// Load CSV file
function loadSalesData() {
d3.csv("salesData.csv", row => {
row.YEAR = parseDate(row.YEAR);
row.REVENUE = +row.REVENUE;
row.EXPENSES = +row.EXPENSES;
return row
}).then(csv => {
// Store csv data in global variable
data = csv;
// Draw the chart
renderChart();
});
}
// Renders the chart
function renderChart() {
console.table(data);
// Gets the user's selection from the drop-down menu
let metricType = d3.select('#metric-type').property('value');
console.log(metricType);
// Sorts the data based on the user's selected metric
data.sort(( a, b ) => b[metricType] - a[metricType]);
// Specifies the DOMAINS
xScale.domain([d3.min(data, function(d) {return d.year; }), d3.max(data, function(d) {return d.year; }) ]);
yScale.domain([0, d3.max(data, function(d) {return d[metricType]; }) ]);
let lineGenerator = d3.line()
.x(function (d) { return xScale(d.year); })
.y(function (d) { return yScale(d[metricType]); })
.curve(d3.curveMonotoneX)(data)
;
// Draws bar chart
let bars = svg.selectAll('.bar')
.data(data);
bars
.exit()
.remove()
bars
.enter()
.append('rect')
.attr('class', 'bar')
.merge(bars)
.transition()
.duration(800)
.attr('x', d => xScale(d.YEAR))
.attr('y', d => yScale(d[metricType]))
.attr('height', d => (height - yScale(d[metricType])))
.attr('width', 10);
// Draws line chart
let linePlot = svg.append('path');
linePlot
.datum(data)
.attr('class', 'line')
.attr('d', lineGenerator)
xAxisGroup
.transition()
.duration(1000)
.style('font-size', '15px')
.call(xAxis);
yAxisGroup
.transition()
.duration(1000)
.style('font-size', '15px')
.call(yAxis);
}
</script>
<script src="https://d3js.org/d3.v7.min.js"></script>
</body>
</html>
The issue is with the sort.
This:
data.sort(( a, b ) => b[metricType] - a[metricType]);
Needs to become this:
data.sort(( a, b ) => b[metricType] - a[metricType]);
data.sort(( a, b ) => b.YEAR - a.YEAR);