Home > Mobile >  How to cumulate d3.rollup data by year
How to cumulate d3.rollup data by year

Time:10-16

I want to create a cumulative line chart per year.
My data is an unordered list of days and values:

date        value
2019-10-10  32.3104
2020-12-29  190.4676
2019-01-02  400.1440
2020-12-21  100.0
2020-12-21  347.1200
2019-01-02  30.6
...

I want to create a line per year, so I order the data and use rollup to sum and group the data:

data = {
  const sortedData = rawdata.sort((a, b) => d3.ascending(a.date, b.date));
  return d3.rollup(
    sortedData,
    (v) => d3.sum(v, (d) => d.value),
    (d) => d.date.getFullYear(),
    (d) => d.date
  );  
}

This way I get a chart with lines for each year using d3.join

svg.append("g")
  .selectAll('.line')
  .data(data.values())
  .join(
    (enter) =>
      enter
        .append('path')
        .attr('class', 'line')
        .attr('fill', 'none')
        .attr('stroke', (d, i) => colors[i])
        .attr('stroke-width', 1.5)
        .attr('d', line),
    (update) =>
      update.attr('d', line),
  );

Now I want this lines to be cumulative per year, but I got no idea how to use d3.cumsum to add up the sums per year.

I don't know how to sum these rolled-up-data - can anyone help me here?

Here is a full working example of my chart on ObservableHQ

CodePudding user response:

If you want to keep the data in the same format that you currently have so that you don't have to change any other code, then you could do

data = {
  const sortedData = rawdata.sort((a, b) => d3.ascending(a.date, b.date));

  // rollups returns arrays of key-value pairs instead of a map
  const groupByYearDate = d3.rollups(
      sortedData,
      (v) => d3.sum(v, (d) => d.value),
      (d) => d.date.getFullYear(),
      (d) => d.date
    );
  
  return new Map(
    // for each year
    groupByYearDate.map(([year, values]) => {
      // summed is an array of numbers
      const summed = d3.cumsum(values, d => d[1]);
      // dates is an array of dates
      const dates = values.map(d => d[0]);
      // zip them together
      const combined = d3.zip(dates, summed);
      return [year, new Map(combined)]
    })
  );
}

Here's a slightly different example, that makes additional changes to your code.

  • Related