I’m trying to make a linechart with D3 and React where the x axis is based on Date.now()
object and all the ticks are a minute apart on a 10mn window.
- I can’t generate the line because I get “NaNNaNNaN” in my svg path;
- Can’t seem to figure out how to have ticks minutes apart on my x axis;
Here’s how the data looks like
// data state data = [ {"loadAverage":0.008333333333333333,"timestamp":1632740462342}, {"loadAverage":0.008333333333333333,"timestamp":1632740459323}, {"loadAverage":0.013333333333333334,"timestamp":1632740471400} ];
the timestamp
key is a new Date.now()
coming from the server
useEffect(() => { const svg = d3.select(d3Container.current); let margin = { top: 20, right: 20, bottom: 50, left: 70 }, width = 960 - margin.left - margin.right, height = 500 - margin.top - margin.bottom; // set the ranges let x = d3 .scaleTime() .domain(d3.extent(data, (d) => timeFormat(d.timestamp))) .range([0, width]); let y = d3 .scaleLinear() .domain([0, d3.max(data, (d) => d.loadAverage)]) .nice() .range([height, 0]); // Parse the date let parseTime = d3.timeParse("%s"); let timeFormat = d3.timeFormat("%M:%S"); // Constructing the line const myLine = d3 .line() .x((d) => { const convertedTime = parseTime(d.timestamp); return x(convertedTime); }) .y((d) => { return y(d.loadAverage); }); svg .append("svg") .attr("width", width + margin.left + margin.right) .attr("height", height + margin.top + margin.bottom) .attr("transform", "translate(" + margin.left + "," + margin.top + ")"); svg .select("svg") .selectAll("path") .data([data]) .join("path") .attr("d", (value) => myLine(value)) .attr("fill", "none") .attr("stroke", "steelblue") .attr("stroke-width", 1.5) .attr("stroke-linejoin", "round") .attr("stroke-linecap", "round"); // Add the x Axis svg .select("svg") .attr("transform", "translate(0," + height + ")") .call(d3.axisBottom(x)); // Add the y Axis svg .select("svg") .append("g") .call(d3.axisLeft(y).tickFormat(timeFormat).ticks(10)); }, [data]);
This is my first time using D3, any help would be greatly appreciated !
Edit: here’s what I tried so far
// Constructing the line const myLine = d3 .line() .x((d) => { const convertedTime = new Date(d.timestamp); return x(convertedTime); }) .y((d) => { return y(d.loadAverage); });
Even tried to return convertedTime
wrapped up by parsetime
like so parsetime(convertedTime)
Didn’t work either.
Advertisement
Answer
I think you have a problem in Initializing x scale domain
// set the ranges let x = d3 .scaleTime() // ⬇️ here is the issue, just get rid of timeFormat .domain(d3.extent(data, (d) => timeFormat(d.timestamp))) .range([0, width]);
the scaleTime
expect the domain to be a [Date|number, Date|number]
, you are using timeFormat
which convert number|Date
into a string
based on the given format.
Try to use this instead:
let x = d3 .scaleTime() .domain(d3.extent(data, (d) => d.timestamp)) .range([0, width]); // The short vesion let x = d3.scaleTime(d3.extent(data, (d) => d.timestamp), [0, width])
Constructing the line
const myLine = d3 .line() .x((d) => x(d.timestamp)) .y((d) => y(d.loadAverage));
If you need to convert timestamps into Dates, you can map the whole data array
data = data.map(d=> d.timestamp = new Date(d.timestamp), d)