web-dev-qa-db-fra.com

D3 - comment gérer les structures de données JSON?

Je suis nouveau sur D3 et j'ai déjà passé quelques heures à découvrir quoi que ce soit sur le traitement des données structurées, mais sans résultat positif. Je veux créer un graphique à barres en utilisant la structure de données ci-dessous. Les barres sont dessinées (horizontalement), mais uniquement pour l'utilisateur "jim".

var data = [{"user":"jim","scores":[40,20,30,24,18,40]},
            {"user":"ray","scores":[24,20,30,41,12,34]}];

var chart = d3.select("div#charts").append("svg")                                   
              .data(data)
              .attr("class","chart")
              .attr("width",800)
              .attr("height",350);

chart.selectAll("rect")    
    .data(function(d){return d3.values(d.scores);})    
    .enter().append("rect")
    .attr("y", function(d,i){return i * 20;})
    .attr("width",function(d){return d;})
    .attr("height", 20);

Quelqu'un pourrait-il indiquer ce que j'ai fait de mal?

36
Ray

Lorsque vous joignez des données à une sélection via selection.data , le nombre d'éléments dans votre tableau de données doit correspondre au nombre d'éléments dans la sélection. Votre tableau de données comporte deux éléments (pour Jim et Ray), mais la sélection à laquelle vous le liez n'a qu'un seul élément SVG. Essayez-vous de créer plusieurs éléments SVG ou de placer les scores de score pour Jim et Ray dans le même élément SVG?

Si vous souhaitez lier les deux éléments de données à l'élément SVG singulier, vous pouvez encapsuler les données dans un autre tableau:

var chart = d3.select("#charts").append("svg")
    .data([data])
    .attr("class", "chart")
    …

Vous pouvez également utiliser selection.datum , qui lie directement les données sans calculer de jointure:

var chart = d3.select("#charts").append("svg")
    .datum(data)
    .attr("class", "chart")
    …

Si vous souhaitez créer plusieurs éléments SVG pour chaque personne, vous aurez besoin d'une jointure de données:

var chart = d3.select("#charts").selectAll("svg")
    .data(data)
  .enter().append("svg")
    .attr("class", "chart")
    …

Un deuxième problème est que vous ne devez pas utiliser d3.values avec un tableau; cette fonction sert à extraire les valeurs d'un objet. En supposant que vous vouliez un élément SVG par personne (donc deux dans cet exemple), les données pour le rect sont simplement les scores associés à cette personne:

var rect = chart.selectAll("rect")
    .data(function(d) { return d.scores; })
  .enter().append("rect")
    …

Si vous ne l'avez pas déjà fait, je vous recommande de lire ces tutoriels:

92
mbostock

Cela peut clarifier l'aspect imbriqué, en plus de la bonne réponse de mbostock.

Vos données ont 2 degrés d'imbrication. Vous avez un tableau de 2 objets, chacun a un tableau d'entiers. Si vous voulez que votre image finale reflète ces différences, vous devez faire une jointure pour chacune.

Voici une solution: chaque utilisateur est représenté par un élément de groupe g, chaque score étant représenté par un rect. Vous pouvez le faire de deux manières: soit utilisez datum sur le svg, puis une fonction d'identité sur chaque g, soit vous pouvez directement joindre les données sur le g. L'utilisation de data sur le g est plus typique, mais voici les deux façons:

tilisation de datum sur le svg:

var chart = d3.select('body').append('svg')
  .datum(data)             // <---- datum
  .attr('width',800)
  .attr('height',350)
  .selectAll('g')
  .data(function(d){ return d; })  // <----- identity function
  .enter().append('g')
    .attr('class', function(d) { return d.user; })
    .attr('transform', function(d, i) { return 'translate(0, ' + i * 140 + ')'; })
    .selectAll('rect')
    .data(function(d) { return d.scores; })
    .enter().append('rect')
      .attr('y', function(d, i) { return i * 20; })
      .attr('width', function(d) { return d; })
      .attr('height', 20);

tilisation de données sur l'élément de groupe (g):

var chart = d3.select('body').append('svg')
  .attr('width',800)
  .attr('height',350)
  .selectAll('g')
  .data(data)          // <--- attach directly to the g
  .enter().append('g')
    .attr('class', function(d) { return d.user; })
    .attr('transform', function(d, i) { return 'translate(0, ' + i * 140 + ')'; })
    .selectAll('rect')
    .data(function(d) { return d.scores; })
    .enter().append('rect')
      .attr('y', function(d, i) { return i * 20; })
      .attr('width', function(d) { return d; })
      .attr('height', 20);

Encore une fois, vous n'avez pas à créer ces éléments g, mais ce faisant, je peux maintenant représenter les scores utilisateur différemment (ils ont des y différents de la transformation) et je peux également leur donner différents styles, comme ceci:

.jim {
  fill: red;
}
.ray {
  fill: blue;
}
1
Tristan Reid