D3.js - Гистограмма с накоплением из JSON?
Я пытался воссоздать эту скрипку: 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, но мне не удалось заставить их работать. Это был самый простой и легкий пример, который я нашел. Но в нем пока нет весов (которые я, надеюсь, добавлю чуть позже).
Не могли бы вы помочь мне решить эту проблему с объяснением?
Ответы
В .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>