D3.js v5 - 不渲染的简单流图

D3.js v5 - simple streamgraph not rendering

在下面的代码片段中,我有一个使用硬编码数据和最少样式的极其简化的流图。

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>

正如我们所见,图表未显示,但同时未标记任何错误。

问题

鉴于代码的简单性和与看到的功能齐全的模板的相似性 here,为什么此图无法按预期呈现?

我认为您需要将日期转换为数值,这样的一些代码可能会有所帮助

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]); });

这是一个工作示例:

<!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>

正如 James 的回答所说,您应该使用 d3.timeParse 将字符串转换为日期,而不是依赖 Date() 构造函数,即 unreliable。此外,您的 keys 数组应仅包含堆叠的属性,因此不应包含“日期”。

我为 d3.stackOffsetWiggle 引用了 Mike Bostock's streamgraph example for the stack generator and the y-scale. The d3-shape docs 说它“移动基线以最小化图层的加权摆动。建议将此偏移量与由内而外的顺序一起用于流图。 “我遵循该建议而不是使用 d3.stackOffsetSilhouette。最后,按照示例,y 尺度的域应覆盖堆叠数据的最小值和最大值。