d3.js可以使用来自同一来源的数据在同一图表上绘制两个散点图吗?

时间:2012-07-26 15:11:44

标签: javascript d3.js

我发现的所有d3教程都使用排列在对象数组中的数据,从中为数组中的每个对象绘制一个点。给出以下结构中的数据:

data = [
     {id: 1, x: 4, y: 10, type: 1},
     {id: 2, x: 5, y: 20, type: 2}
     ...
]

x和y值用于制作散点图。 type参数用于更改每个点的颜色。请参阅此jsfiddle以获取示例:http://jsfiddle.net/uxbHv/

不幸的是,我有一个不同的数据结构,我无法弄清楚如何通过为每个对象绘制两个数据点来创建相同的图形。以下是一些示例数据:

dataSet = [
     {xVar: 5, yVar1: 90, yVar2: 22},
     {xVar: 25, yVar1: 30, yVar2: 25},
     {xVar: 45, yVar1: 50, yVar2: 80},
     {xVar: 65, yVar1: 55, yVar2: 9},
     {xVar: 85, yVar1: 25, yVar2: 95}
]

我可以针对yVar1或yVar2单独绘制xVar,但我无法弄清楚如何在同一图表上获取两者:http://jsfiddle.net/634QG/

2 个答案:

答案 0 :(得分:31)

使用data-join时的一般规则是,您需要从数据到元素的一对一映射。因此,如果散点图中有两个系列,则需要两个容器元素(例如G elements)来表示系列。由于您当前只有一个data数组,因此您还需要使用array.map将数据表示转换为具有相同表示形式的两个并行数组。这样,您就不必为每个系列重复代码。

假设您的数据在CSV文件中表示,其中 x - 值包含一列,每个系列的 y - 值包含多个其他列:

x,y1,y2
5,90,22
25,30,25
45,50,80
65,55,9
85,25,95

如果您希望代码完全通用,则首先需要计算系列的名称,例如["y1", "y2"]。 (如果您向CSV文件添加了第三列,则可能是["y1", "y2", "y3"]。)您可以使用d3.keys计算名称,从而从对象中提取命名属性。例如,d3.keys({foo: 1, bar: 2})会返回["foo", "bar"]

// Compute the series names ("y1", "y2", etc.) from the loaded CSV.
var seriesNames = d3.keys(data[0])
    .filter(function(d) { return d !== "x"; })
    .sort();

现在您已拥有系列名称,您可以创建一个点阵列数组。外部数组表示系列(其中有两个),内部数组存储数据点。您可以同时将点转换为一致的表示形式(具有xy属性的对象),允许您跨系列重用代码。

// Map the data to an array of arrays of {x, y} tuples.
var series = seriesNames.map(function(series) {
  return data.map(function(d) {
    return {x: +d.x, y: +d[series]};
  });
});

请注意,此代码使用+运算符将CSV值强制转换为数字。 (CSV文件是无类型的,因此它们最初是字符串。)

现在您已将数据映射为常规格式,您可以为每个系列创建G元素,然后在每个点内圈出元素。生成的SVG结构如下所示:

<g class="series">
  <circle class="point" r="4.5" cx="1" cy="2"/>
  <circle class="point" r="4.5" cx="3" cy="2"/>
  …
</g>
<g class="series">
  <circle class="point" r="4.5" cx="5" cy="4"/>
  <circle class="point" r="4.5" cx="7" cy="6"/>
  …
</g>

以及相应的D3代码:

// Add the points!
svg.selectAll(".series")
    .data(series)
  .enter().append("g")
    .attr("class", "series")
    .style("fill", function(d, i) { return z(i); })
  .selectAll(".point")
    .data(function(d) { return d; })
  .enter().append("circle")
    .attr("class", "point")
    .attr("r", 4.5)
    .attr("cx", function(d) { return x(d.x); })
    .attr("cy", function(d) { return y(d.y); });

我还添加了一些代码,通过向包含G元素添加填充样式为每个系列指定唯一的颜色。当然,有很多不同的方法可以做到这一点。 (例如,您可能希望更具体地了解每个系列的颜色。)我还遗漏了计算 x y 域的代码缩放(以及渲染轴),但如果你想看到整个工作示例:

答案 1 :(得分:5)

将每个数据点的两个圆圈放入单个svg:g元素中。这会产生数据到元素的一对一映射,但仍允许您显示两个不同的点。

var nodeEnter = vis1.selectAll("circle")
      .data(dataSet)
      .enter()
      .insert("svg:g");

nodeEnter.insert("svg:circle")
           .attr("cx", function (d) { return 100 - d.xVar})
           .attr("cy", function (d) { return 100 - d.yVar1})
           .attr("r", 2)
           .style("fill", "green");

nodeEnter.insert("svg:circle")
           .attr("cx", function (d) { return 100 - d.xVar})
           .attr("cy", function (d) { return 100 - d.yVar2})
           .attr("r", 2)
           .style("fill", "blue");

工作 JSFiddle