使用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();
现在您有了系列名称,您可以创建一个由点组成的数组。外部数组表示系列(其中有两个),内部数组存储数据点。您可以同时将点转换为一致的表示(具有x 和y 属性的对象),从而允许您跨系列重用代码。
// 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 域的代码缩放(以及渲染轴),但如果您想查看整个工作示例: