d3.js:如何连接来自更多来源的数据

d3.js:如何连接来自更多来源的数据,d3.js,D3.js,gs.csv(数据1): circles.csv(数据2): 我有两个如上所述的数据文件。gs.csv(数据1)仅包含关于g元素和圆的数据。csv(数据2)仅包含关于圆的数据,我需要一种正确的方式来连接它们并创建以下内容: <g class="groups" id="fruits" transform="translate(60,90)"> <circle class="some" id="apple" cx="10" cy="10"/>

gs.csv(数据1):

circles.csv(数据2):

我有两个如上所述的数据文件。gs.csv(数据1)仅包含关于g元素和圆的数据。csv(数据2)仅包含关于圆的数据,我需要一种正确的方式来连接它们并创建以下内容:



    <g class="groups" id="fruits" transform="translate(60,90)">
      <circle class="some" id="apple" cx="10" cy="10"/>
      <circle class="some" id="pear" cx="20" cy="20"/>
      <circle class="some" id="strawberry" cx="30" cy="30"/>
      ...
    </g>
    <g class="groups" id="vegetables" transform="translate(70,70)">
      <circle class="some" id="carrot" cx="40" cy="40">
      <circle class="some" id="celery" cx="50" cy="50">
      ...
    </g>
我想到了两种解决方案,但总有一些陷阱: 首先,我尝试在第二个.data()中从data2中筛选行,但我不知道如何访问实际组的属性以筛选除具有相同名称值的圆以外的所有圆。
其次,我尝试用键d.name嵌套数据2并将其输入组,但它会覆盖原始的数据。因此,我尝试将键和值添加到每个组的uu数据中,但没有成功。

您可以通过包括

在HTML文件中。然后在javascript中包含

queue()
   .defer(d3.csv, "gs.csv")
   .defer(d3.csv, "circles.csv")
   .await(ready);

function ready(error, gs, circles){
...
}
现在,在ready函数中,您可以访问这两个数据集。您可以首先创建以下组:

var groups = d3.selectAll(".groups")
    .data(gs)
  .enter().append("g")
    .attr("class", "groups")
    .attr("id", function(d) { return d.name; })
    .attr("transform", function(d){return "translate(" + d.x_value + "," + d.y_value + ")"});
然后

groups.selectAll(".some")
    .data(circles)
  .enter().append("circle")
    .attr("class", "some")
    .attr("id", function(d) { return d.value; })
    .attr("cx", function(d) { return d.cx_value; })
    .attr("cy", function(d) { return d.cy_value; });

我认为这对于在每个小组中只创建相关的圈子来说并不太有效。对于如何准确实现这一点,还需要更多的思考。

最简单的方法是将数据组放入列表中,然后对其进行操作,如下所示:

groupData = [data1, data2];

groups = svg.selectAll('g')
    .data(groupData)
    .append('g')
现在您有了两个组,每个组都附加了您想要在组中添加的内容的数据。您可以创建一个函数,根据组中的数据附加圆,然后为每个组调用它

function makeCircles(d){
    d3.select(this).selectAll('circle')
        .data(d)
        .append('circle')
        .attr('rx',function(D){return D.circleRadius})

groups.each(makeCircles);
这将为您提供每组的相关圆圈。请注意,在我们在
makeCircles
中创建的
selectAll
中,
D
指的是与给定圆关联的数据。我们可以使用
d
,但最好避免组数据
d
和圆数据
d
之间的变量混淆。这样,我们也可以在定义圆属性的任何函数中使用这两种方法

如果我们希望第一个数据包含第二个数据,我们可以使用相同的函数,但使用基于d的过滤器替换d本身。代码如下所示:

var root = d3.select('body').append('svg'),

    data1 = [{'name':'foo','value':10},{'name':'foo','value':3},{'name':'foo','value':8},{'name':'bar','value':10},{'name':'bar','value':1},{'name':'bar','value':15}],

    data2 = [{'name':'foo','color':'green','x':10},{'name':'bar','color':'blue','x':70}];

console.log('foo')

var groups = root.selectAll('g')
    .data(data2)
    .enter()
    .append('g')
    .attr('transform',function(d){return 'translate(' + d.x + ',10)'})
    .each(addCircles);

function addCircles(d){
    d3.select(this).selectAll('circle')
        .data(data1.filter(function(D){return D.name == d.name }))
        .enter()
        .append('circle')
        .attr('r',5)
        .attr('cx', 0)
        .attr('cy', function(D){return D.value * 30})
        .style('fill',d.color)
}

我做了一把小提琴

另一种方法是使用nest。我认为,如果两个数据文件中的名称(水果、蔬菜等)完全相同,这将解决您的问题。如果有必要的话,它的优点是线性时间而不是二次时间。(如果两个数据文件的名称相同,但顺序不同,则必须首先对它们进行排序,使其为O(nlogn)。)

这将水果行和蔬菜行分成不同的数组

var zip = d3.zip(data1,nest);
这将两个文件合并为一个数据集。它形成一个数组,每个唯一名称有一个项。(这是要求名称相同、顺序相同的部分。)数组的每个项都是一个包含两项的数组:第一项是来自data1的行,第二项是来自data2的一组行(在嵌套结构中)。现在,您可以将数据连接到SVG:

        svg.selectAll("g")
           .data(zip)
           .enter()
           .append("g")
           .attr("class","groups")
           .attr("id",function(d){return d[0].name;})
           .attr("transform",function(d){return "translate("+d[0].x_value+","+d[0].y_value+")"})
           .selectAll("circle")
           .data(function(d){return d[1].values;})
           .enter()
           .append("circle")
           .attr("class", "some")
           .attr("id", function(d) { return d.value; })
           .attr("cx", function(d) { return d.cx_value; })
           .attr("cy", function(d) { return d.cy_value; });
d[0]
是数据1文件的一行,
d[1]。值
是数据2文件的一组行

不需要.each、.filter或一个helper函数

我从以下几页中学到了如何做到这一点:


感谢您将我引向队列库,但只创建相关的圆对我来说至关重要。谢谢您,但如果我正确地找到了解决方案,那么data1只包含关于g元素的数据,data2只包含关于圆的数据,我需要以正确的方式加入它们。请使用
数组。筛选器()
javascript方法,使用相同的通用代码结构,基于d筛选第二个数据。请参阅我的编辑以了解如何做到这一点。诀窍仍然是在函数调用中添加所有的圆,以便它们在创建时可以看到其父对象的数据。
var root = d3.select('body').append('svg'),

    data1 = [{'name':'foo','value':10},{'name':'foo','value':3},{'name':'foo','value':8},{'name':'bar','value':10},{'name':'bar','value':1},{'name':'bar','value':15}],

    data2 = [{'name':'foo','color':'green','x':10},{'name':'bar','color':'blue','x':70}];

console.log('foo')

var groups = root.selectAll('g')
    .data(data2)
    .enter()
    .append('g')
    .attr('transform',function(d){return 'translate(' + d.x + ',10)'})
    .each(addCircles);

function addCircles(d){
    d3.select(this).selectAll('circle')
        .data(data1.filter(function(D){return D.name == d.name }))
        .enter()
        .append('circle')
        .attr('r',5)
        .attr('cx', 0)
        .attr('cy', function(D){return D.value * 30})
        .style('fill',d.color)
}
var nest = d3.nest()
             .key(function(d) {return d.name;})
             .entries(data2);
var zip = d3.zip(data1,nest);
        svg.selectAll("g")
           .data(zip)
           .enter()
           .append("g")
           .attr("class","groups")
           .attr("id",function(d){return d[0].name;})
           .attr("transform",function(d){return "translate("+d[0].x_value+","+d[0].y_value+")"})
           .selectAll("circle")
           .data(function(d){return d[1].values;})
           .enter()
           .append("circle")
           .attr("class", "some")
           .attr("id", function(d) { return d.value; })
           .attr("cx", function(d) { return d.cx_value; })
           .attr("cy", function(d) { return d.cy_value; });