D3.js-JSONからの積み上げ棒グラフ?

Aug 19 2020

私はこのフィドルを再現しようとしています: https://jsfiddle.net/1c5eqrp3/3/

データを使用して作成している水平積み上げ棒グラフのコンテキストで。

var bar1Data = [{
    position: 1,
    label: '70k',
    value: 1708026
  },
  {
    position: 2,
    label: '71K - 149K',
    value: 1915059
  },
];

//sort bars based on value
bar1Data = bar1Data.sort(function(a, b) {
  return d3.ascending(a.position, b.position);
})

var colors = ['green', 'blue'];

var bars = d3.select('#bars')
  .append('svg')
  .attr('width', 800)
  .attr('height', 200);

bars.selectAll('rect')
  .data(bar1Data)
  .enter()
  .append('rect')
  .attr('width', function(d) {
    return d;
  })
  .attr('x', function(d, i) {
    return sum(bar1Data.value, 0, i);
  })
  .attr('fill', function(d, i) {
    return colors[i];
  })
  .attr('y', 0)
  .attr('height', 100);

function sum(array, start, end) {
  var total = 0;
  for (var i = start; i < end; i++) total += array[i];
  return total;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/5.7.0/d3.min.js"></script>
<div id="bar"></div>

作成したbar1Dataを読み取ろうとするまで、すべてが順調に進んでいました。

現在、次のエラーが発生しています。

Error: <rect> attribute width: Expected length, "[object Object]".
(anonymous) @ d3.js:1211
selection_each @ d3.js:1176
selection_attr @ d3.js:1233
(anonymous) @ bars3.js:23
Promise.then (async)
(anonymous) @ bars3.js:1
bars3.js:33 Uncaught (in promise) TypeError: Cannot read property '0' of undefined
    at sum (bars3.js:33)
    at SVGRectElement.<anonymous> (bars3.js:26)
    at SVGRectElement.<anonymous> (d3.js:1209)
    at Selection.selection_each [as each] (d3.js:1176)
    at Selection.selection_attr [as attr] (d3.js:1233)
    at bars3.js:25

d3.v5を使用して積み上げ水平棒グラフを作成するのに非常に苦労しています。d3.v4を使用してさまざまな例を試しましたが、それらを機能させることができませんでした。これは私が見つけた最も単純で最も軽量な例でした。しかし、それはまだスケールを含んでいません(私が少し後で入れたいと思います)。

説明を付けてこの問題を解決するのを手伝っていただけませんか。

回答

1 RubenHelsloot Aug 19 2020 at 12:48

では.attr('width', function(d){ return d;})、バーの幅をオブジェクト全体に設定していますd。の.data(bar1Data)値ごとに1つのエントリが作成さbar1Dataれること、およびdそれらすべての関数と同様にその値にアクセスできることを理解する必要があります。つまり、通過するとすぐに、.data(bar1Data)そのオブジェクトに直接触れてはいけないということです。

さて、あなたのデータから、前のすべてのエントリの値を使用して、その時点までの累積合計を計算する必要があることを理解しているようです。私はそれを答えに実装しますがstack = d3.stack()、あなたのためにそれを行う、に向けてあなたを指摘したいと思います。

値が大きすぎることにも注意してください。ドメインを使用して変換する必要があります。そうしないと、1ドルあたり1ピクセル(または他の単位)になります。つまり、バーはモニターの幅の100倍右に描画されます。

var bar1Data = [{
    position: 1,
    label: '70k',
    value: 50
  },
  {
    position: 2,
    label: '71K - 149K',
    value: 40
  },
];

//sort bars based on value
bar1Data = bar1Data.sort(function(a, b) {
  return d3.ascending(a.position, b.position);
})

var colors = ['green', 'blue'];

var bars = d3.select('#bars')
  .append('svg')
  .attr('width', 800)
  .attr('height', 200);

bars.selectAll('rect')
  .data(bar1Data)
  .enter()
  .append('rect')
  .attr('width', function(d) {
    return d.value;
  })
  .attr('x', function(d, i) {
    // Note that I use `.map` here to take only the `.value` attribute
    // from the data and create an array of numbers.
    return sum(bar1Data.map((e) => e.value), 0, i);
  })
  .attr('fill', function(d, i) {
    return colors[i];
  })
  .attr('y', 0)
  .attr('height', 100);

function sum(array, start, end) {
  var total = 0;
  for (var i = start; i < end; i++) total += array[i];
  return total;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/5.7.0/d3.min.js"></script>
<div id='bars'>
</div>