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
Advertisement
Answer
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>