Arash Howaida
Arash Howaida

Reputation: 2617

D3.js v5 - simple streamgraph not rendering

In the following snippet, I have an extremely simplified streamgraph using hard coded data and minimal styling.

var margin = {top: 20, right: 30, bottom: 0, left: 10},
width = 900 - margin.left - margin.right,
height = 450 - margin.top - margin.bottom;


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 + ")");

  var data = [{'active': 56733306054.0,
'bond': 3525783856.0,
'date': 'Apr-15',
'mmf': 1403672993.0,
'other': 0.0,
'passive': 6821599781.0},
   {'active': 5716791269.0,
'bond': 6085973929.0,
'date': 'Jul-15',
'mmf': 3941955156.0,
'other': 0.0,
'passive': 669757365.2},
   {'active': 17062997217.0,
'bond': 4039598099.0,
'date': 'Oct-15',
'mmf': 4009017950.0,
'other': 0.0,
'passive': 726136994.3},
   {'active': 7814464858.0,
'bond': 4262416085.0,
'date': 'Jan-16',
'mmf': 6942848526.0,
'other': 1868408693.0,
'passive': 576693480.6},
   {'active': 3225887820.0,
'bond': 1997520654.0,
'date': 'Apr-16',
'mmf': 8803909471.0,
'other': 1829867764.0,
'passive': 658579093.5},
   {'active': 7552865110.0,
'bond': 2774538610.0,
'date': 'Jul-16',
'mmf': 17168667665.0,
'other': 1781624412.0,
'passive': 1062492998.0},
   {'active': 9921160101.0,
'bond': 4749422921.0,
'date': 'Oct-16',
'mmf': 19897031580.0,
'other': 1642076470.0,
'passive': 1001209305.0},
   {'active': 11432914047.0,
'bond': 6282916561.0,
'date': 'Jan-17',
'mmf': 10962414198.0,
'other': 1540325097.0,
'passive': 1865670585.0},
   {'active': 12239976192.0,
'bond': 5161108076.0,
'date': 'Apr-17',
'mmf': 7858159818.0,
'other': 1423157123.0,
'passive': 1394368561.0},
   {'active': 12538521595.0,
'bond': 4844007015.0,
'date': 'Jul-17',
'mmf': 10153574680.0,
'other': 1327231248.0,
'passive': 982569223.9},
   {'active': 13660436312.0,
'bond': 4831471993.0,
'date': 'Oct-17',
'mmf': 9105515290.0,
'other': 1226448389.0,
'passive': 660421454.6},
   {'active': 18238784924.0,
'bond': 6926050754.0,
'date': 'Jan-18',
'mmf': 8997606297.0,
'other': 0.0,
'passive': 502423215.8},
   {'active': 16515400278.0,
'bond': 8174797500.0,
'date': 'Apr-18',
'mmf': 10488139471.0,
'other': 0.0,
'passive': 641632439.9},
   {'active': 14469020809.0,
'bond': 10154350717.0,
'date': 'Jul-18',
'mmf': 12795032278.0,
'other': 0.0,
'passive': 373254191.0},
   {'active': 11941160301.0,
'bond': 11565983214.0,
'date': 'Oct-18',
'mmf': 9868174645.0,
'other': 0.0,
'passive': 500573365.9},
   {'active': 13065033332.0,
'bond': 13150111094.0,
'date': 'Jan-19',
'mmf': 9383725064.0,
'other': 0.0,
'passive': 348729651.5},
   {'active': 13015515107.0,
'bond': 13530625221.0,
'date': 'Apr-19',
'mmf': 10134077571.0,
'other': 0.0,
'passive': 289549779.9},
   {'active': 15803115946.0,
'bond': 16503245488.0,
'date': 'Jul-19',
'mmf': 10938147334.0,
'other': 0.0,
'passive': 364098177.7},
   {'active': 19281878473.0,
'bond': 22592356870.0,
'date': 'Oct-19',
'mmf': 13042415142.0,
'other': 0.0,
'passive': 1182250058.0},
   {'active': 26486960563.0,
'bond': 26446208720.0,
'date': 'Jan-20',
'mmf': 14980167197.0,
'other': 0.0,
'passive': 825650931.1},
   {'active': 26551390384.0,
'bond': 27921669739.0,
'date': 'Apr-20',
'mmf': 9558841841.0,
'other': 0.0,
'passive': 841110171.1},
   {'active': 26498733168.0,
'bond': 23387500164.0,
'date': 'Jul-20',
'mmf': 9754774244.0,
'other': 0.0,
'passive': 930687399.1},
   {'active': 30362195233.0,
'bond': 25104023352.0,
'date': 'Oct-20',
'mmf': 14576442586.0,
'other': 0.0,
'passive': 981491044.5},
   {'active': 33018698783.0,
'bond': 19038462941.0,
'date': 'Jan-21',
'mmf': 15537675080.0,
'other': 0.0,
'passive': 875201720.9},
   {'active': 50881100695.0,
'bond': 15108333888.0,
'date': 'Apr-21',
'mmf': 17027529982.0,
'other': 0.0,
'passive': 781991391.3}];




var keys = ['date','bond','active','passive','mmf','other'];


var x = d3.scaleTime()
  .domain([new Date('01/01/2014'), new Date('01/01/2022')])
  .range([ 0, width ]);
svg.append("g")
  .attr("transform", "translate(0," + height*0.8 + ")")
  .call(d3.axisBottom(x).tickSize(-height*.7).tickValues([new Date("01/01/2014"),new Date('01/01/2018')]))
  .select(".domain").remove()

svg.selectAll(".tick line").attr("stroke", "#b8b8b8")

var y = d3.scaleLinear()
  .domain([0, 1000000000])
  .range([ height, 0 ]);

var stackedData = d3.stack()
  .offset(d3.stackOffsetSilhouette)
  .keys(keys)
  (data)

var area = d3.area()
  .x(function(d) { return x(new Date(d.data.date)); })
  .y0(function(d) { return y(d[0]); })
  .y1(function(d) { return y(d[1]); });

svg
  .selectAll("mylayers")
  .data(stackedData)
  .enter()
  .append("path")
    .attr("class", "myArea")
    .style("fill", "#003366")
    .attr("d", area);
<script src="https://d3js.org/d3.v5.min.js"></script>

As we can see, the graph is not displayed, but at the same time, no errors were flagged.

Question

Given the simplicity of the code and the similarity to the fully functional template seen here, why might this graph not render as expected?

Upvotes: 1

Views: 145

Answers (2)

Dan
Dan

Reputation: 1591

Here's a working example:

<!DOCTYPE HTML>
<html>

<head>
  <meta charset="UTF-8">
  <script src="https://d3js.org/d3.v7.min.js"></script>
</head>

<body>
  <script>
    const margin = { top: 20, right: 20, bottom: 20, left: 20 },
      width = 900 - margin.left - margin.right,
      height = 450 - margin.top - margin.bottom;

    const 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})`);

    const parseDate = d3.timeParse('%b-%y');

    var data = [{
      'active': 56733306054.0,
      'bond': 3525783856.0,
      'date': 'Apr-15',
      'mmf': 1403672993.0,
      'other': 0.0,
      'passive': 6821599781.0
    },
    {
      'active': 5716791269.0,
      'bond': 6085973929.0,
      'date': 'Jul-15',
      'mmf': 3941955156.0,
      'other': 0.0,
      'passive': 669757365.2
    },
    {
      'active': 17062997217.0,
      'bond': 4039598099.0,
      'date': 'Oct-15',
      'mmf': 4009017950.0,
      'other': 0.0,
      'passive': 726136994.3
    },
    {
      'active': 7814464858.0,
      'bond': 4262416085.0,
      'date': 'Jan-16',
      'mmf': 6942848526.0,
      'other': 1868408693.0,
      'passive': 576693480.6
    },
    {
      'active': 3225887820.0,
      'bond': 1997520654.0,
      'date': 'Apr-16',
      'mmf': 8803909471.0,
      'other': 1829867764.0,
      'passive': 658579093.5
    },
    {
      'active': 7552865110.0,
      'bond': 2774538610.0,
      'date': 'Jul-16',
      'mmf': 17168667665.0,
      'other': 1781624412.0,
      'passive': 1062492998.0
    },
    {
      'active': 9921160101.0,
      'bond': 4749422921.0,
      'date': 'Oct-16',
      'mmf': 19897031580.0,
      'other': 1642076470.0,
      'passive': 1001209305.0
    },
    {
      'active': 11432914047.0,
      'bond': 6282916561.0,
      'date': 'Jan-17',
      'mmf': 10962414198.0,
      'other': 1540325097.0,
      'passive': 1865670585.0
    },
    {
      'active': 12239976192.0,
      'bond': 5161108076.0,
      'date': 'Apr-17',
      'mmf': 7858159818.0,
      'other': 1423157123.0,
      'passive': 1394368561.0
    },
    {
      'active': 12538521595.0,
      'bond': 4844007015.0,
      'date': 'Jul-17',
      'mmf': 10153574680.0,
      'other': 1327231248.0,
      'passive': 982569223.9
    },
    {
      'active': 13660436312.0,
      'bond': 4831471993.0,
      'date': 'Oct-17',
      'mmf': 9105515290.0,
      'other': 1226448389.0,
      'passive': 660421454.6
    },
    {
      'active': 18238784924.0,
      'bond': 6926050754.0,
      'date': 'Jan-18',
      'mmf': 8997606297.0,
      'other': 0.0,
      'passive': 502423215.8
    },
    {
      'active': 16515400278.0,
      'bond': 8174797500.0,
      'date': 'Apr-18',
      'mmf': 10488139471.0,
      'other': 0.0,
      'passive': 641632439.9
    },
    {
      'active': 14469020809.0,
      'bond': 10154350717.0,
      'date': 'Jul-18',
      'mmf': 12795032278.0,
      'other': 0.0,
      'passive': 373254191.0
    },
    {
      'active': 11941160301.0,
      'bond': 11565983214.0,
      'date': 'Oct-18',
      'mmf': 9868174645.0,
      'other': 0.0,
      'passive': 500573365.9
    },
    {
      'active': 13065033332.0,
      'bond': 13150111094.0,
      'date': 'Jan-19',
      'mmf': 9383725064.0,
      'other': 0.0,
      'passive': 348729651.5
    },
    {
      'active': 13015515107.0,
      'bond': 13530625221.0,
      'date': 'Apr-19',
      'mmf': 10134077571.0,
      'other': 0.0,
      'passive': 289549779.9
    },
    {
      'active': 15803115946.0,
      'bond': 16503245488.0,
      'date': 'Jul-19',
      'mmf': 10938147334.0,
      'other': 0.0,
      'passive': 364098177.7
    },
    {
      'active': 19281878473.0,
      'bond': 22592356870.0,
      'date': 'Oct-19',
      'mmf': 13042415142.0,
      'other': 0.0,
      'passive': 1182250058.0
    },
    {
      'active': 26486960563.0,
      'bond': 26446208720.0,
      'date': 'Jan-20',
      'mmf': 14980167197.0,
      'other': 0.0,
      'passive': 825650931.1
    },
    {
      'active': 26551390384.0,
      'bond': 27921669739.0,
      'date': 'Apr-20',
      'mmf': 9558841841.0,
      'other': 0.0,
      'passive': 841110171.1
    },
    {
      'active': 26498733168.0,
      'bond': 23387500164.0,
      'date': 'Jul-20',
      'mmf': 9754774244.0,
      'other': 0.0,
      'passive': 930687399.1
    },
    {
      'active': 30362195233.0,
      'bond': 25104023352.0,
      'date': 'Oct-20',
      'mmf': 14576442586.0,
      'other': 0.0,
      'passive': 981491044.5
    },
    {
      'active': 33018698783.0,
      'bond': 19038462941.0,
      'date': 'Jan-21',
      'mmf': 15537675080.0,
      'other': 0.0,
      'passive': 875201720.9
    },
    {
      'active': 50881100695.0,
      'bond': 15108333888.0,
      'date': 'Apr-21',
      'mmf': 17027529982.0,
      'other': 0.0,
      'passive': 781991391.3
    }].map(d => {
      d['date'] = parseDate(d['date']);
      return d;
    });

    const keys = ['bond', 'active', 'passive', 'mmf', 'other'];

    const x = d3.scaleTime()
        .domain(d3.extent(data, d => d.date))
        .range([0, width]);

    svg.append("g")
        .attr("transform", `translate(0,${height})`)
        .call(
          d3.axisBottom(x)
            .tickSize(-height)
        )
        .call(g => g.select(".domain").remove())
        .call(g => g.selectAll(".tick line").attr("stroke", "#b8b8b8"));


    /*
    code from https://observablehq.com/@d3/streamgraph

    The d3-shape docs say that d3.stackOffsetWiggle "Shifts the baseline
    so as to minimize the weighted wiggle of layers. This offset is recommended
    for streamgraphs in conjunction with the inside-out order."
    */
    const stackedData = d3.stack()
        .order(d3.stackOrderInsideOut)
        .offset(d3.stackOffsetWiggle)
        .keys(keys)
        (data);

    // the domain of the y-scale should cover the min and max of the stacked data
    // code from https://observablehq.com/@d3/streamgraph
    const y = d3.scaleLinear()
        .domain([
          d3.min(stackedData, d => d3.min(d, d => d[0])),
          d3.max(stackedData, d => d3.max(d, d => d[1]))
        ])
        .range([height, 0]);

    const color = d3.scaleOrdinal()
        .domain(keys)
        .range(d3.schemeCategory10);

    const area = d3.area()
        .x(d => x(d.data.date))
        .y0(d => y(d[0]))
        .y1(d => y(d[1]));

    svg.append('g')
      .selectAll("path")
      .data(stackedData)
      .join("path")
        .style("fill", d => color(d.key))
        .attr("d", area);
  </script>
</body>

Like James's answer says, you should use d3.timeParse to convert strings into Dates, rather than relying on the Date() constructor, which is unreliable. In addition, your keys array should only include the attributes that are being stacked, so it should not contain "date".

I referenced Mike Bostock's streamgraph example for the stack generator and the y-scale. The d3-shape docs for d3.stackOffsetWiggle say that it "Shifts the baseline so as to minimize the weighted wiggle of layers. This offset is recommended for streamgraphs in conjunction with the inside-out order." I'm following that recommendation rather than using d3.stackOffsetSilhouette. Lastly, following the example, the domain of the y-scale should cover the min and max of the stacked data.

Upvotes: 3

James Mudd
James Mudd

Reputation: 2393

I think you need to convert your dates into a numeric value so some code like this might help

var dateParser = d3.timeParse('%b-%y')
var area = d3.area()
  .x(function(d) { return x(dateParser(d.data.date)); })
  .y0(function(d) { return y(d[0]); })
  .y1(function(d) { return y(d[1]); });

Upvotes: 2

Related Questions