使用D3.js处理嵌套数据

3

我正在尝试使用D3展示一张漂亮的折线图。我遇到的问题是数据格式。

以下是我的数据(以示例形式):

var data = [
    {
      label: "name",
      data: [[14444123, 0.012321312],
             [14444123, 0.012321312],
             [14444123, 0.012321312], ...]
    },{
      label: "another name",
      data: [[14444123, 0.012321312],
             [14444123, 0.012321312],
             [14444123, 0.012321312], ...]
    }
];

每个条目都包含其名称以及一个数据属性,其中包含点数组(每个点表示为数组,其中item [0]是x时间戳,item [1]是值)。
我的问题是它没有正确工作。
这是我现在拥有的D3代码:
var w = options.width,
    h = options.height,
    p = options.padding,
    x = d3.scale.linear()
        .domain([0, 1])
        .range([0, w]),
    y = d3.scale.linear()
        .domain([options.ydomainstart, options.ydomainend])
        .range([h, 0]);

var vis = d3.select(options.element)
    .data(data)
   .append("svg:svg")
    .attr("width", w + p * 2)
    .attr("height", h + p * 2)
   .append("svg:g");

vis.append("svg:line")
    .attr("stroke", '#808080')
    .attr("x1", p)
    .attr("x2", p)
    .attr("y1", 0)
    .attr("y2", h - p);

vis.append("svg:line")
    .attr("stroke", '#808080')
    .attr("x1", p)
    .attr("x2", w)
    .attr("y1", h - p)
    .attr("y2", h - p);

var rules = vis.selectAll("g.rule")
    .data(data)
   .enter()
   .append("svg:text")
    .attr("x", w - p)
    .attr("y", function(d, i) { return 15 + i*12; })
    .attr("text-anchor", "end")
    .attr("font-size", 12)
    .attr("fill", function(d, i) { return defaultColors[i % 5]; })
    .text(function(d) { return d.label;});

var lines = rules.data(function(d, i) {
        return d.data;
    })
   .append("svg:path")
    .attr("stroke", function(d, i) { return defaultColors[i % 5]; })
    .attr("d", d3.svg.line()
    .x(function(d) {
        return x(d[0]);
    })
    .y(function(d) {
        return y(d[1]);
    }));

我遇到的问题出现在代码的这一部分:
.x(function(d) {
    return x(d[0]);
})
.y(function(d) {
    return y(d[1]);
}));

'd' 内的数据并不是点数组 [x, y],而是每个数组中的每个值。

也就是说,在第一个项目上,d 包含 x 坐标,在第二个项目上,它有 y 坐标,在第三个项目上,它包含下一个点的 x 坐标,以此类推。

就像它递归地进入数组,然后再对其中每个值进行操作。

我不知道该如何解决这个问题。

1个回答

10

这里有几个问题。

首先,你正在将svg:path元素附加到svg:text元素上。我觉得你是想创建一个带有类名“rule”的svg:g元素,但你的代码将选择器rules定义为一组svg:text元素。首先创建svg:g元素,然后再附加svg:text元素:

var rules = vis.selectAll("g.rule")
    .data(data)
  .enter().append("svg:g")
    .attr("class", "rule");

rules.append("svg:text")
    …

第二个问题是数据操作符每组只被评估一次,而不是每个元素一次。请参见API参考中的"Operating on Selections"部分以了解更多详细信息。在vis中您有一个svg:svg元素,因此在rules中您有一个组,因此您的数据函数仅被调用一次:
function(d, i) {
  return d.data;
}

然后,生成的数据元素会被映射到rules选择器...这些选择器已经在之前的selectAll和append中定义了数据。
简单的解决方法是使用map运算符而不是data运算符,后者每个组只评估一次,而不是每个元素都评估一次。
rules.append("svg:path")
    .map(function(d) { return d.data; })
    .attr("d", d3.svg.line()
    …

另外,您也可以直接将数据传递给线条生成器,但这需要您提前声明线条生成器,而不是将其嵌入其中:

var line = d3.svg.line()
    .x(function(d) { return x(d[0]); })
    .y(function(d) { return y(d[1]); });

rules.append("svg:path")
    .attr("d", function(d) { return line(d.data); })
    …

希望这有所帮助!

请注意,map方法已被弃用,应使用datum方法。 - Christian-G

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