gs.csv(data1):
id, name, x_value, y_value
1, fruits, 60, 60
2, vegetables, 70, 70
...
circles.csv(data2):
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来删除data2并将其输入到组中,但它会覆盖原始的__ data_ 。因此,我尝试将键和值添加到每个组的_ data__但没有成功。
答案 0 :(得分: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
内创建的makeCircles
中,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)
}
我做了一个小提琴here.
答案 1 :(得分:2)
您可以通过包含
将queue
库用于d3
<script src="http://d3js.org/queue.v1.min.js"></script>
。然后在你的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; });
我认为这并不适合仅在每个组中创建相关的圈子。必须更多地考虑如何完成这一目标。
答案 2 :(得分:1)
另一种方法是使用nest。如果两个数据文件中的名称(水果,蔬菜等)完全相同,我认为这对您的问题有用。如果重要的话,它具有线性时间而不是二次方的优点。 (如果两个数据文件具有相同的名称但顺序不同,则必须先对它们进行排序,使其为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或帮助函数。
我从这些页面学到了如何做到这一点: