3

gs.csv(数据1):

id, name, x_value, y_value
1, fruits, 60, 60
2, vegetables, 70, 70
...

circles.csv(数据2):

id, name, value, cx_value, cy_value
1, fruits, apple, 10, 10
2, fruits, pear, 20, 20
3, fruits, strawberry, 30, 30
4, vegetables, carrot, 40, 40
5, vegetables, celery, 50, 50
...

我有两个如上所述的数据文件。gs.csv (data1) 仅包含有关 g 元素的数据,而 circles.csv (data2) 仅包含有关圆的数据,我需要一种正确的方法来加入它们并创建以下内容:



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

我无法加入这些文件,因为它们更复杂。我认为该代码应该类似于:



    d3.csv("gs.csv", function(data1) {
     d3.csv("circles.csv", function(data2) {
       var svg = ...
       var groups = svg.selectAll(".groups")
                  .data(data1)
                  .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(data2, function(d) { return d.id; })
        .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; });
    })
  });

我想到了两个解决方案,但总是有一些问题:首先,我尝试在第二个 .data() 中过滤来自 data2 的行,但我不知道如何访问实际组的属性以过滤除具有同名值。其次,我尝试使用键 d.name 将 d3.nest data2 放入组中,但它会覆盖原始 __ data_ 。因此,我尝试将键和值添加到每个组的 _ data__ 但没有成功。

4

3 回答 3

6

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

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

这将为您提供每个组的相关圈子。请注意,在selectAll我们创建的 内makeCirclesD是指与给定圆圈关联的数据。我们可以d改用,但最好避免在 group datad和 circle data之间发生变量混淆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)
}

我在这里做了一个小提琴。

于 2013-07-23T20:14:04.957 回答
2

您可以queue通过包含使用 d3 库

<script src="http://d3js.org/queue.v1.min.js"></script>

在您的 HTML 文件中。然后在你的 javascript 中包括

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

function ready(error, gs, circles){
...
}

现在,在该准备好的函数中,您可以访问这两个数据集。您可以先创建组:

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

我认为,尽管这对于仅在每个组中创建相关圈子并不完全有效。必须更多地考虑如何准确地实现这一点。

于 2013-07-23T19:21:26.753 回答
1

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

var nest = d3.nest()
             .key(function(d) {return d.name;})
             .entries(data2);

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

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]是 data1 文件的一行,d[1].values是 data2 文件的一组行。

不需要 .each、.filter 或辅助函数。

我从这些页面学习了如何做到这一点:

于 2016-05-07T01:54:42.907 回答