I am a newbie coding js and d3, and I have the code below, I want to display the dates in array on the x axis instead of numbers. I tried many ways to display the dates; most of ways display the dates with no line and data with errors in the HTML console, that it could not read the data type of the dates it only allows numbers.
<!DOCTYPE html>
<meta charset="utf-8">
<body>
</body>
<!-- Load in the d3 library -->
<script src="https://d3js.org/d3.v5.min.js"></script>
<script>
// 2. Use the margin convention practice
var margin = {top: 200, right: 300, bottom: 400, left: 300}
, width = window.innerWidth - margin.left - margin.right // Use the window's width
, height = window.innerHeight - margin.top - margin.bottom; // Use the window's height
var parseDate = d3.timeParse("%Y-%m-%d");
// 8. An array of objects of length N. Each object has key -> value pair, the key being "y" and the value is a random number
var xdata = [
["2021-08-01",(d3.randomUniform(100)()).toFixed(1)],
["2021-08-02",(d3.randomUniform(100)()).toFixed(1)],
["2021-08-03",(d3.randomUniform(100)()).toFixed(1)],
["2021-08-04",(d3.randomUniform(100)()).toFixed(1)],
["2021-08-05",(d3.randomUniform(100)()).toFixed(1)],
["2021-08-06",(d3.randomUniform(100)()).toFixed(1)],
["2021-08-07",(d3.randomUniform(100)()).toFixed(1)],
["2021-08-08",(d3.randomUniform(100)()).toFixed(1)],
["2021-08-09",(d3.randomUniform(100)()).toFixed(1)],
["2021-08-10",(d3.randomUniform(100)()).toFixed(1)],
["2021-08-11",(d3.randomUniform(100)()).toFixed(1)]
];
var n = xdata.length;
var max_value = 0;
var col1 = 0;
for (let i = 0; i < n; i ) {
xdata[i][0] = parseDate(xdata[i][0]);
//console.log(xdata[i][0]);
col1 = parseFloat(xdata[i][1])
if (col1 > max_value)
max_value = col1
}
console.log(max_value);
// 5. X scale will use the index of our data
var xScale = d3.scaleLinear()
.domain([0,n-1 ]) // input
.range([0, width]); // output
// 6. Y scale will use the randomly generate number
var yScale = d3.scaleLinear()
.domain([0, max_value]) // input
.range([height, max_value]); // output
// 7. d3's line generator
var line = d3.line()
.x(function(d, i) { return xScale(i); }) // set the x values for the line generator
.y(function(d) { return yScale(d.y); }) // set the y values for the line generator
.curve(d3.curveMonotoneX) // apply smoothing to the line
//var dataset = d3.range(n).map(function(d) { return {"y": (d3.randomUniform(100)()).toFixed(1) } })
var dataset = xdata.map(function(d) {
return {
x: parseDate(d[0]),
y: d[1]
};
});
// 1. Add the SVG to the page and employ #2
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 ")");
// 3. Call the x axis in a group tag
svg.append("g")
.attr("class", "x axis")
.attr("transform", "translate(0," height ")")
.call(d3.axisBottom(xScale)); // Create an axis component with d3.axisBottom
// 4. Call the y axis in a group tag
svg.append("g")
.attr("class", "y axis")
.call(d3.axisLeft(yScale)); // Create an axis component with d3.axisLeft
// 9. Append the path, bind the data, and call the line generator
svg.append("path")
.datum(dataset) // 10. Binds data to the line
.attr("fill", "none")
.attr("stroke", "steelblue")
.attr("stroke-width", 3)
.attr("d", line); // 11. Calls the line generator
// 12. Appends a circle for each datapoint
svg.selectAll(".dot")
.data(dataset)
.enter().append("circle") // Uses the enter().append() method
.style("fill","steelblue")
.attr("cx", function(d, i) { return xScale(i) })
.attr("cy", function(d) { return yScale(d.y) })
.attr("r", 5);
// 13. Appends data text to each datapoint
svg.selectAll(".text")
.data(dataset)
.enter().append("text")
.style("fill", "red")
.attr("x", function(d, i) { return xScale(i) - 5 })
.attr("y", function(d) { return yScale(d.y) - 20 })
.text(function(d) { return d.y });
</script>
Thanks for your answers in advance
CodePudding user response:
I would suggest parsing the strings into Date objects right away and then using d3.scaleTime
for the x scale. Here is an example below.
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<script src="https://d3js.org/d3.v7.js"></script>
</head>
<body>
<div id="chart"></div>
<script>
// set up
const margin = { top: 40, bottom: 40, left: 40, right: 40 };
const width = 500 - margin.left - margin.right;
const height = 250 - margin.top - margin.bottom;
const svg = d3.select('#chart')
.append('svg')
.attr('width', width margin.left margin.right)
.attr('height', height margin.top margin.bottom);
const g = svg.append('g')
.attr('transform', `translate(${margin.left},${margin.top})`);
// data
const parseDate = d3.timeParse("%Y-%m-%d");
const dataset = [
["2021-08-01",(d3.randomUniform(100)()).toFixed(1)],
["2021-08-02",(d3.randomUniform(100)()).toFixed(1)],
["2021-08-03",(d3.randomUniform(100)()).toFixed(1)],
["2021-08-04",(d3.randomUniform(100)()).toFixed(1)],
["2021-08-05",(d3.randomUniform(100)()).toFixed(1)],
["2021-08-06",(d3.randomUniform(100)()).toFixed(1)],
["2021-08-07",(d3.randomUniform(100)()).toFixed(1)],
["2021-08-08",(d3.randomUniform(100)()).toFixed(1)],
["2021-08-09",(d3.randomUniform(100)()).toFixed(1)],
["2021-08-10",(d3.randomUniform(100)()).toFixed(1)],
["2021-08-11",(d3.randomUniform(100)()).toFixed(1)]
].map(d => ({ date: parseDate(d[0]), value: parseFloat(d[1]) }));
const minMaxDate = d3.extent(dataset, d => d.date);
const maxValue = d3.max(dataset, d => d.value);
// scales
const x = d3.scaleTime()
.domain(minMaxDate)
.range([0, width]);
const y = d3.scaleLinear()
.domain([0, maxValue])
.range([height, 0]);
// line generator
const line = d3.line()
.x(d => x(d.date))
.y(d => y(d.value))
.curve(d3.curveMonotoneX);
// axes
g.append("g")
.attr("class", "x axis")
.attr("transform", `translate(0,${height})`)
.call(d3.axisBottom(x));
g.append("g")
.attr("class", "y axis")
.call(d3.axisLeft(y));
// line
g.append('path')
.datum(dataset)
.attr('fill', 'none')
.attr('stroke', 'steelblue')
.attr('stroke-width', 3)
.attr('d', line);
// circles
g.selectAll('.dot')
.data(dataset)
.join('circle')
.attr('fill', 'steelblue')
.attr('cx', d => x(d.date))
.attr('cy', d => y(d.value))
.attr('r', 5);
// text labels
g.selectAll('.text')
.data(dataset)
.join('text')
.attr('fill', 'red')
.attr('x', d => x(d.date) - 5)
.attr('y', d => y(d.value) - 20)
.text(d => d.value);
</script>
</body>
</html>