简体   繁体   English

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

[英]Can d3.js draw two scatterplots on the same graph using data from the same source?

All of the d3 tutorials I've found use data arranged in arrays of objects from which they graph one point for each object in the array. 我发现的所有d3教程都使用以对象数组排列的数据,从中为数组中的每个对象绘制一个点。 Given data in the following structure: 给定以下结构中的数据:

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

The x and y values are used to make a scatterplot. x和y值用于制作散点图。 The type parameter is used to change the color of each point. type参数用于更改每个点的颜色。 See this jsfiddle for an example: http://jsfiddle.net/uxbHv/ 请参阅此jsfiddle以获取示例: http//jsfiddle.net/uxbHv/

Unfortuately, I have a different data structure and I can't figure out how to create the same graph by drawing two data points for each object. 不幸的是,我有一个不同的数据结构,我无法弄清楚如何通过为每个对象绘制两个数据点来创建相同的图形。 Here is some example data: 以下是一些示例数据:

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}
]

I can graph xVar individually against yVar1 or yVar2, but I can not figure out how to get both on the same graph: http://jsfiddle.net/634QG/ 我可以针对yVar1或yVar2单独绘制xVar,但我无法弄清楚如何在同一个图表上获取两者: http//jsfiddle.net/634QG/

The general rule when using a data-join is that you want a one-to-one mapping from data to elements . 使用数据连接时的一般规则是,您需要从数据到元素的一对一映射 So, if you have two series in your scatterplot, you'll want two container elements (such as G elements ) to represent the series. 因此,如果散点图中有两个系列,则需要两个容器元素(例如G元素 )来表示系列。 Since you currently have only one data array, you'll also want to use array.map to convert the data representation into two parallel arrays with the same representation. 由于您当前只有一个data数组,因此您还需要使用array.map将数据表示转换为具有相同表示形式的两个并行数组。 This way, you don't have to duplicate code for each series. 这样,您就不必为每个系列重复代码。

Say your data was represented in a CSV file with one column for the x -values, and multiple other columns for the y -values of each series: 假设您的数据在CSV文件中表示,其中x列为一列,每个系列的y值为多个其他列:

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

If you want the code to be completely generic, you first need to compute the series' names, such as ["y1", "y2"] . 如果您希望代码完全通用,则首先需要计算系列的名称,例如["y1", "y2"] (If you added a third column to the CSV file, it might be ["y1", "y2", "y3"] .) You can compute the names using d3.keys , which extracts the named properties from an object. (如果在CSV文件中添加了第三列,则可能是["y1", "y2", "y3"] 。)您可以使用d3.keys计算名称,从中提取对象的命名属性。 For example, d3.keys({foo: 1, bar: 2}) returns ["foo", "bar"] . 例如, 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();

Now that you have the series names, you can create an array of arrays of points. 现在您已拥有系列名称,您可以创建一个点数组数组。 The outer array represents the series (of which there are two) and the inner arrays store the data points. 外部数组表示系列(其中有两个),内部数组存储数据点。 You can simultaneously convert the points to a consistent representation (objects with x and y properties), allowing you to reuse code across series. 您可以同时将点转换为一致的表示(具有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]};
  });
});

Note this code uses the + operator to coerce the CSV values to numbers. 请注意,此代码使用+运算符将CSV值强制转换为数字。 (CSV files are untyped, so they are initially strings.) (CSV文件是无类型的,因此它们最初是字符串。)

Now that you've mapped your data to a regular format, you can create G elements for each series, and then circle elements within for each point. 现在您已将数据映射为常规格式,您可以为每个系列创建G元素,然后为每个点创建圆形元素。 The resulting SVG structure will look like this: 生成的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>

And the corresponding D3 code: 和相应的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); });

I've also added a bit of code to assign each series a unique color by adding a fill style to the containing G element. 我还添加了一些代码,通过向包含G元素添加填充样式为每个系列分配一个独特的颜色。 There are lots of different ways to do this, of course. 当然,有很多不同的方法可以做到这一点。 (You might want to be more specific about the color for each series, for example.) I've also left out the code that computes the domains of your x and y scales (as well as rendering the axes), but if you want to see the entire working example: (例如,您可能想要更具体地说明每个系列的颜色。)我还省略了计算xy比例域(以及渲染轴)的代码,但是如果你想要的话看到整个工作示例:

Place the two circles for each data point into a single svg:g element. 将每个数据点的两个圆放入单个svg:g元素中。 This produces a one-to-one mapping for the data to elements but still allows you to show two different points. 这会为数据生成一对一的映射到元素,但仍允许您显示两个不同的点。

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

Working JSFiddle . 工作JSFiddle

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM