Group line plotting in D3 with d3.group

Viewed 888

I am working with a tidy-long data structure with three columns: date, ID, num_orders.

date          ID          num_orders
"2018-08-22"  1           3
"2018-08-23"  7           1
"2018-08-23"  10          1
"2018-08-23"  17          1
"2018-08-23"  19          1
.
.
.

I would like to plot a line for each ID with date and num_orders as the x- and y-axis respectively, using D3.js. I am using this as a model for what I am doing, but in that code, the author is using the nest() function, which is no longer used in v6 of D3; the method used now is group(). So my code now is:

const margin = { top: 10, right: 30, bottom: 30, left: 60 };
const width = 1000 - margin.left - margin.right;
const height = 600 - margin.top - margin.bottom;

const svg = d3.select('#my_dataviz')
    .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})`);
                
d3.json("./data.json")
    .then( function(data) {        
        const grouped_data = d3.group(data, d => d.ID);

        parseDate = d3.timeParse('%Y-%m-%d')
        const xScale = d3.scaleTime()
                        .domain(d3.extent(data, d => parseDate(d.date)))
                        .range([0, width]);
        svg.append('g')
            .attr('transform', `translate(0, ${height})`)
            .call(d3.axisBottom(xScale));
        
        const yScale = d3.scaleLinear()
                        .domain([0, d3.max(data, d => d.num_orders)])
                        .range([height, 0]);
        svg.append('g')
            .call(d3.axisLeft(yScale));

        const myColor = d3.scaleOrdinal()
                            .domain(grouped_data.keys())
                            .range(d3.schemeSet3);
        
        svg.selectAll('.line')
            .data(grouped_data)
            .enter()
            .append('path')
                .attr('fill', 'none')
                .attr('stroke', d => myColor(d.keys))
                .attr('stroke-width', 1.5)
                .attr('d', function(d){
                                return d3.line()
                                    .x(d => xScale(d.date))
                                    .y(d => yScale(d.num_orders))
                                    (d.values);
                });
    } )

So far I can get the axis with tick-marks to show up, but none of the lines are showing up, which makes me think the problem is in the final svg.selectAll('.line') statement. I'm pretty new to D3, so any guidance on this problem is appreciated. (And if you have any overall suggestions for my code, I also welcome this.)

2 Answers

d3.line() is accepting arrays only, while d.values() is an iterator. By converting it into an array the problem is solved.

Notice that, on the snippet I removed the parseDate because I am generating data as Dates. You most likely will need to keep the parseDate

const margin = { top: 10, right: 30, bottom: 30, left: 60 };
const width = 1000 - margin.left - margin.right;
const height = 600 - margin.top - margin.bottom;

var x = d3.timeDays(new Date(2020, 06, 01), new Date(2020, 10, 30));
var y = Array.from({length: x.length}, Math.random).map(n => Math.floor(n * 10) + 5);
var data = x.map((v, i) => {
  return {
    "date": v,
    "id": Math.floor(Math.random() * (10 + 1)),
    "num_orders": y[i]
  };
});

const svg = d3.select('#my_dataviz')
    .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})`);


const grouped_data = d3.group(data, d => d.id);

parseDate = d3.timeParse('%Y-%m-%d');

const xScale = d3.scaleTime()
                .domain(d3.extent(data, d => d.date))
                .range([0, width]);
svg.append('g')
    .attr('transform', `translate(0, ${height})`)
    .call(d3.axisBottom(xScale));

const yScale = d3.scaleLinear()
                .domain([0, d3.max(data, d => d.num_orders)])
                .range([height, 0]);
svg.append('g')
    .call(d3.axisLeft(yScale));

const myColor = d3.scaleOrdinal()
                    .domain(grouped_data.keys())
                    .range(d3.schemeSet3);

const line = d3.line()
              .x(d => { return xScale(d.date); })
              .y(d => yScale(d.num_orders));

svg.selectAll('.line')
    .data(grouped_data)
    .enter()
    .append('path')
        .attr('fill', 'none')
        .attr('stroke', d => myColor(d[0]))
        .attr('stroke-width', 1.5)
        .attr('d', (d) => line(Array.from(d.values())[1]));
   
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/6.5.0/d3.min.js"></script>
<div id="my_dataviz"></div>

Try to parse the data on the line generator

const margin = { top: 10, right: 30, bottom: 30, left: 60 };
const width = 1000 - margin.left - margin.right;
const height = 600 - margin.top - margin.bottom;

const svg = d3.select('#my_dataviz')
    .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})`);
                
d3.json("./data.json")
    .then( function(data) {        
        const grouped_data = d3.group(data, d => d.ID);

        parseDate = d3.timeParse('%Y-%m-%d')
        const xScale = d3.scaleTime()
                        .domain(d3.extent(data, d => parseDate(d.date)))
                        .range([0, width]);
        svg.append('g')
            .attr('transform', `translate(0, ${height})`)
            .call(d3.axisBottom(xScale));
        
        const yScale = d3.scaleLinear()
                        .domain([0, d3.max(data, d => d.num_orders)])
                        .range([height, 0]);
        svg.append('g')
            .call(d3.axisLeft(yScale));

        const myColor = d3.scaleOrdinal()
                            .domain(grouped_data.keys())
                            .range(d3.schemeSet3);
        
        svg.selectAll('.line')
            .data(grouped_data)
            .enter()
            .append('path')
                .attr('fill', 'none')
                .attr('stroke', d => myColor(d.keys))
                .attr('stroke-width', 1.5)
                .attr('d', function(d){
                                return d3.line()
                                    .x(d => xScale(parseData(d.date)))
                                    .y(d => yScale(d.num_orders))
                                    (d.values);
                });
    } )
Related