37

我是 D3 的新手,已经花了几个小时来了解有关处理结构化数据的任何信息,但没有得到积极的结果。我想使用下面的数据结构创建一个条形图。绘制条形(水平),但仅适用于用户“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);

谁能指出我做错了什么?

4

2 回答 2

99

When you join data to a selection via selection.data, the number of elements in your data array should match the number of elements in the selection. Your data array has two elements (for Jim and Ray), but the selection you are binding it to only has one SVG element. Are you trying to create multiple SVG elements, or put the score rects for both Jim and Ray in the same SVG element?

If you want to bind both data elements to the singular SVG element, you can wrap the data in another array:

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

Alternatively, use selection.datum, which binds data directly without computing a join:

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

If you want to create multiple SVG elements for each person, then you'll need a data-join:

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

A second problem is that you shouldn't use d3.values with an array; that function is for extracting the values of an object. Assuming you wanted one SVG element per person (so, two in this example), then the data for the rect is simply that person's associated scores:

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

If you haven't already, I recommend reading these tutorials:

于 2012-04-10T15:18:45.850 回答
1

除了 mbostock 的好答案之外,这可能会澄清嵌套方面。

您的数据有 2 度嵌套。您有一个包含 2 个对象的数组,每个对象都有一个整数数组。如果您希望最终图像反映这些差异,则需要为每个图像进行连接。

这是一个解决方案:每个用户由一个组g元素表示,每个分数由一个rect. 您可以通过以下几种方式做到这一点:要么datum在 svg 上使用,然后在g每个g. 使用dataong更典型,但这里有两种方法:

在 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);

使用 group ( 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);

同样,您不必创建这些 g 元素,但通过这样做,我现在可以以不同的方式表示用户分数(它们与变换有不同的 y),我还可以为它们赋予不同的样式,如下所示:

.jim {
  fill: red;
}
.ray {
  fill: blue;
}
于 2015-08-17T18:14:09.960 回答