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)создается одна запись для каждого значения bar1Data, и что это значение доступно, как и dво всех этих функциях. Это означает, что как только вы прошли .data(bar1Data), вы больше не должны напрямую касаться этого объекта.

Теперь, исходя из ваших данных, я, кажется, понимаю, что вы хотите использовать значения всех предыдущих записей для вычисления совокупной суммы до этого момента. Я реализую это в ответе, но также хотел бы указать вам stack = d3.stack(), что делает это за вас.

Также обратите внимание, что ваши значения слишком велики. Вам нужно преобразовать их с помощью домена, иначе вы получите один пиксель за доллар (или другую денежную единицу), что означает, что полосы будут нарисованы в 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>