d3: 对嵌套数据中的多条线进行着色

4

我目前正在使用JSON对象数组组装一些带有数据点圆圈的线图,格式如下:

var data = [{
    "name": "metric1",
    "datapoints": [
        [10.0, 1333519140],
        [48.0, 1333519200]
    ]
}, {
    "name": "metric2",
    "datapoints": [
        [48.0, 1333519200],
        [12.0, 1333519260]
    ]
}]

我希望每个指标都有不同的颜色,所以我正在尝试根据数组数据中对象的索引来为它们着色。目前我仅仅放置圆圈的代码如下:
// We bind an svg group to each metric.
var metric_groups = this.vis.selectAll("g.metric_group")
  .data(data).enter()
  .append("g")
    .attr("class", "metric_group");

// Then bind a circle for each datapoint.
var circles = metric_groups.selectAll("circle")
.data(function(d) { return d.datapoints; });

circles.enter().append("circle")
  .attr("r", 3.5);

现在,如果我将最后一部分更改为类似以下的内容:
circles.enter().append("circle")
  .attr("r", 3.5);
  .style("fill", function(d,i) { return i%2 ? "red" : "blue"; }

我得到了交替的红色和蓝色圆圈,这是可以预料的。
嵌套选择:“嵌套和索引”中获取了一些建议后,我尝试了以下方法:

circles.enter().append("circle")
  .attr("r", 3.5);
  .style("fill", function(d,i,j) { return j%2 ? "red" : "blue"; }

这段代码不起作用(j未定义),可能是因为我们在命名属性datapoints中,而不是数组元素中。如何在不改变数据结构的情况下实现所需的着色?谢谢!


我通过在组上设置样式而不是圆圈来解决了这个问题(为什么要在Stack Overflow上发布才想到呢?)。我仍然希望得到一个更少折衷的答案(例如,在组内圆圈和路径需要不同样式的情况下,或者子元素的属性由于某种原因无法继承)。 - diet_coke
2个回答

3
最简单的做法是使圆形元素从父级G元素继承填充样式:
var color = d3.scale.category20();

var metricGroup = vis.selectAll(".metric-group")
    .data(data)
  .enter().append("g")
    .attr("class", "metric-group")
    .style("fill", function(d) { return color(d.name); });

var circle = metricGroup.selectAll("circle")
    .data(function(d) { return d.datapoints; })
  .enter().append("circle")
    .attr("r", 3.5);

如果您将分类颜色定义为 CSS 类,则还可以使用动态类名并以这种方式继承:
var metricGroup = vis.selectAll(".metric-group")
    .data(data)
  .enter().append("g")
    .attr("class", function(d) { return "metric-group " + color(d.name); });

使用对应的CSS:

.metric1 circle { fill: red; }
.metric2 circle { fill: blue; }

另一种方法是使用each来访问父级数据:

metricGroup.each(function(p, j) {
  d3.select(this).selectAll("circle")
      .data(p.datapoints)
    .enter().append("circle")
      .attr("r", 3.5)
      .style("fill", color(p.name));
});

我认为使用群组索引 j 也可以;我不确定为什么您的代码中未定义它,但是您的示例代码中有一个多余的分号(在 .attr("r", 3.5); 中),因此可能还有其他问题。无论如何,从数据派生分类颜色比从组索引更符合惯用法,因此我建议使用上述技术之一。

1
在我的一个可视化项目中,我遇到了同样的问题。我的解决方法如下(根据你的示例进行了调整):
var count = 0;

var color = d3.scale.category20();

var metric_groups = this.vis.selectAll("g.metric_group")
  .data(data).enter()
  .append("g")
    .attr("class", "metric_group");

var circles = metric_groups.selectAll("circle")
  .data(function(d) {return d.datapoints;});

circles.enter().append("circle")
  .attr("r", 3.5)
  .style("fill", function(d,i){
    d.number = count;
    count++;
    return color(d.number);
  });

关键是给每个数据赋予一个唯一的属性,可以用来在不同的分组中为相应的元素赋予独特的颜色。

希望这能帮助或支持您找到类似的解决方案!


非常感谢您的回答!如果我误解了,那么这段代码看起来像是为每个圆分配一个独特的颜色,这有点解决了不同的问题。我的问题是希望同一行中的每个圆都具有相同的颜色。似乎我可能会想出类似的东西,但我想知道是否有更惯用的方法。 - diet_coke
啊,现在我知道问题出在哪里了!我会考虑一下的! - EightBitBoy
另一种方法是使用for循环:迭代度量并在循环内绘制每个图形和相应的圆。这样做不太美观,但确实可行。 - EightBitBoy

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接