D3.js - ¿Gráfico de barras apilado de JSON?
He estado intentando recrear este violín: https://jsfiddle.net/1c5eqrp3/3/
En el contexto de un gráfico de barras apiladas horizontales que estoy construyendo usando mis datos.
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>
Todo iba bien, hasta que intenté que leyera el bar1Data que creé.
Ahora, recibo los siguientes errores:
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
He estado luchando enormemente para crear un gráfico de barras horizontales apiladas usando d3.v5. He estado probando varios ejemplos usando d3.v4 pero no he podido hacer que funcionen. Este fue el ejemplo más simple y liviano que he encontrado. Pero todavía no incluye escalas (que espero incluir un poco más adelante).
¿Podría ayudarme a resolver este problema con una explicación?
Respuestas
En .attr('width', function(d){ return d;})
, está configurando el ancho de la barra para todo el objeto d
. Debe comprender que .data(bar1Data)
crea una entrada por valor de bar1Data
y que ese valor es accesible como d
en todas esas funciones. Eso significa que, tan pronto como hayas pasado .data(bar1Data)
, ya no deberías tocar directamente ese objeto.
Ahora, a partir de sus datos, parece que entiendo que desea usar los valores de todas las entradas anteriores para calcular la suma acumulada hasta ese punto. Implementaré eso en la respuesta, pero también me gustaría señalarle que stack = d3.stack()
lo hace por usted.
Tenga en cuenta también que sus valores son demasiado grandes. Debe transformarlos usando un dominio; de lo contrario, obtendrá un píxel por dólar (u otra unidad de dinero), lo que significa que las barras se dibujan 100 veces el ancho de su monitor a la derecha.
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>