D3.js - Stacked Barchart z JSON?

Aug 19 2020

Próbowałem odtworzyć to skrzypce: https://jsfiddle.net/1c5eqrp3/3/

W kontekście poziomego skumulowanego wykresu słupkowego, który tworzę na podstawie moich danych.

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>

Wszystko szło dobrze, dopóki nie spróbowałem go zmusić do odczytania utworzonych przeze mnie bar1Data.

Teraz otrzymuję następujące błędy:

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

Bardzo się starałem stworzyć skumulowany poziomy wykres słupkowy za pomocą d3.v5. Próbowałem różnych przykładów przy użyciu d3.v4, ale nie byłem w stanie zmusić ich do działania. To był najprostszy i najlżejszy przykład, jaki znalazłem. Ale nie zawiera jeszcze skal (które mam nadzieję wstawię trochę później).

Czy możesz mi pomóc rozwiązać ten problem z wyjaśnieniem?

Odpowiedzi

1 RubenHelsloot Aug 19 2020 at 12:48

W programie .attr('width', function(d){ return d;})ustawiasz szerokość paska na cały obiekt d. Musisz zrozumieć, że .data(bar1Data)tworzy jeden wpis dla każdej wartości bar1Datai że ta wartość jest dostępna jak dwe wszystkich tych funkcjach. Oznacza to, że gdy tylko zdasz .data(bar1Data), nie powinieneś już bezpośrednio dotykać tego obiektu.

Teraz, z twoich danych, wydaje mi się, że rozumiem, że chcesz użyć wartości wszystkich poprzednich wpisów do obliczenia skumulowanej sumy do tego momentu. Uwzględnię to w odpowiedzi, ale chciałbym również wskazać Ci stack = d3.stack(), co zrobi to za Ciebie.

Zwróć również uwagę, że twoje wartości są zbyt duże. Musisz je przekształcić za pomocą domeny, w przeciwnym razie otrzymasz jeden piksel na dolara (lub inną jednostkę ceny), co oznacza, że ​​paski są narysowane 100x szerokości monitora po prawej stronie.

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>