D3.js - Grafico a barre in pila da JSON?

Aug 19 2020

Ho provato a ricreare questo violino: https://jsfiddle.net/1c5eqrp3/3/

Nel contesto di un grafico a barre in pila orizzontale che sto creando utilizzando i miei dati.

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>

Tutto stava andando bene, finché non ho provato a far leggere il bar1Data che avevo creato.

Ora ricevo i seguenti errori:

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

Ho lottato molto per creare un grafico a barre orizzontali in pila utilizzando d3.v5. Ho provato vari esempi usando d3.v4 ma non sono riuscito a farli funzionare. Questo è stato l'esempio più semplice e leggero che ho trovato. Ma non include ancora le scale (che spero di inserire un po 'più tardi).

Potresti aiutarmi a risolvere questo problema con una spiegazione?

Risposte

1 RubenHelsloot Aug 19 2020 at 12:48

In .attr('width', function(d){ return d;}), stai impostando la larghezza della barra sull'intero oggetto d. Devi capire che .data(bar1Data)crea una voce per valore di bar1Datae che quel valore è accessibile come din tutte quelle funzioni. Ciò significa che, non appena sei passato .data(bar1Data), non dovresti più toccare direttamente quell'oggetto.

Ora, dai tuoi dati mi sembra di capire che vuoi usare i valori di tutte le voci precedenti per calcolare la somma cumulativa fino a quel punto. Lo implementerò nella risposta, ma vorrei anche indicarti stack = d3.stack(), il che lo fa per te.

Nota anche che i tuoi valori sono troppo grandi. Devi trasformarli usando un dominio, altrimenti ottieni un pixel per dollaro (o un'altra unità di denaro), il che significa che le barre sono disegnate 100 volte la larghezza del tuo monitor a destra.

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>