D3.js - Barchart empilé de JSON?

Aug 19 2020

J'ai essayé de recréer ce violon: https://jsfiddle.net/1c5eqrp3/3/

Dans le contexte d'un histogramme empilé horizontal que je construis à l'aide de mes données.

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>

Tout allait bien, jusqu'à ce que j'essaie de le faire lire les données bar1Data que j'ai créées.

Maintenant, j'obtiens les erreurs suivantes:

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

J'ai eu beaucoup de mal à créer un graphique à barres horizontales empilées en utilisant d3.v5. J'ai essayé divers exemples en utilisant d3.v4 mais je n'ai pas pu les faire fonctionner. C'était l'exemple le plus simple et le plus léger que j'ai trouvé. Mais il n'inclut pas encore les échelles (que j'espère mettre un peu plus tard).

Pourriez-vous s'il vous plaît m'aider à résoudre ce problème avec une explication?

Réponses

1 RubenHelsloot Aug 19 2020 at 12:48

Dans .attr('width', function(d){ return d;}), vous définissez la largeur de la barre sur l'objet entier d. Vous devez comprendre que .data(bar1Data)crée une entrée par valeur de bar1Data, et que cette valeur est accessible comme ddans toutes ces fonctions. Cela signifie que, dès que vous êtes passé .data(bar1Data), vous ne devriez plus toucher directement cet objet.

Maintenant, d'après vos données, je semble comprendre que vous voulez utiliser les valeurs de toutes les entrées précédentes pour calculer la somme cumulée jusqu'à ce point. Je vais mettre cela en œuvre dans la réponse, mais je voudrais également vous indiquer stack = d3.stack()qui le fait pour vous.

Notez également que vos valeurs sont trop grandes. Vous devez les transformer à l'aide d'un domaine, sinon vous obtenez un pixel par dollar (ou autre unité d'argent), ce qui signifie que les barres sont dessinées 100x la largeur de votre moniteur à droite.

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>