循环 JSON 数据以创建 d3 饼图
Loop over JSON data to create d3 pie charts
我已经成功地使用 d3 和 JSON 创建了我的第一个饼图,但我正在努力让多个饼图出现。我查看了许多示例,包括 Mike Bostock's donut-multiples,看来我的代码中需要一个 for-each 循环。
这是我的程序的简化版本,它生成一个饼图而不是两个:
<!doctype html>
<html>
<head>
<title>Pie Chart Test</title>
<meta charset="UTF-8">
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/3.5.5/d3.min.js"></script>
</head>
<style>
.arc path {
stroke: white;
}
</style>
<body>
<script>
var width = 300,
height = 300,
radius = Math.min(width, height) / 2;
var arc = d3.svg.arc()
.outerRadius(radius)
.innerRadius(radius - (radius / 2));
var pie = d3.layout.pie()
.sort(null)
.value(function(d) { return d.ratio; });
var svg = d3.select("body").append("svg")
.attr("width", width)
.attr("height", height)
.append("g")
.attr("transform", "translate(" + width / 2 + "," + height / 2 + ")");
d3.json("data.json", function(error, data) {
if (error) throw error;
node = data.data[0].pie1.results;
var g = svg.selectAll(".arc")
.data(pie(node))
.enter().append("g")
.attr("class", "arc")
g.append("path")
.attr("d", arc)
.style("fill", function(d) {
if (d.data.failures > 0) {
return "#d63535";
} else {
return "#22c12d";
}
});
});
</script>
</body>
</html>
这是填充图表的 JSON:
{"data":[
{"pie1": {
"results": [
{"ratio": 0.04, "total": 7, "failures": 1},
{"ratio": 0.04, "total": 8, "failures": 0},
{"ratio": 0.04, "total": 9001, "failures": 0}
]}
},
{"pie2": {
"results": [
{"ratio": 0.04, "total": 10, "failures": 0},
{"ratio": 0.04, "total": 11, "failures": 1},
{"ratio": 0.04, "total": 12, "failures": 1}
]}
}
]}
您有密码 node = data.data[0].pie1.results;
但是你对 pie2 什么也没做。
如果您想对其进行硬编码,您可以使用 node2 = data.data[0].pie2.results;
,但最好尝试以编程方式进行。
类似
for( pie in data.data[0] ){
node = pie.results;
var g = svg.selectAll(".arc")
.data(pie(node))
.enter().append("g")
.attr("class", "arc")
g.append("path")
.attr("d", arc)
.style("fill", function(d) {
if (d.data.failures > 0) {
return "#d63535";
} else {
return "#22c12d";
}
});
}
it appears I need a for-each loop in my code
一般来说,d3中for each循环是可以避免的。大多数 d3 的规范示例都避免使用它们。
您可以使用标准的 d3 输入选择来为数据数组中的每个项目创建一个 svg 元素(即每个饼图一个 svg),而不是 for-each 循环。这将每个饼图的数据绑定到其各自的 svg:
var svg = d3.select("body").selectAll("svg")
.data(data.data)
.enter()
.append("svg")
.attr("width", width)
.attr("height", height)
.append("g")
.attr("transform", "translate(" + width / 2 + "," + height / 2 + ")");
现在您可以使用绑定数据为每个 svg 创建饼图:
var g = svg.selectAll(".arc")
.data(function(d,i) { return pie(d["pie"+(i+1)].results) })
.enter().append("g")
.attr("class", "arc")
g.append("path")
.attr("d", arc)
.style("fill", function(d) { return (d.data.failures > 0) ? "#d63535" : "#22c12d"; });
所有这些看起来像:
var data = {"data":[
{"pie1": {
"results": [
{"ratio": 0.04, "total": 7, "failures": 1},
{"ratio": 0.04, "total": 8, "failures": 0},
{"ratio": 0.04, "total": 9001, "failures": 0}
]}
},
{"pie2": {
"results": [
{"ratio": 0.04, "total": 10, "failures": 0},
{"ratio": 0.04, "total": 11, "failures": 1},
{"ratio": 0.04, "total": 12, "failures": 1}
]}
}
]};
var width = 300,
height = 300,
radius = Math.min(width, height) / 2;
var arc = d3.svg.arc()
.outerRadius(radius)
.innerRadius(radius - (radius / 2));
var pie = d3.layout.pie()
.sort(null)
.value(function(d) { return d.ratio; });
var svg = d3.select("body").selectAll("svg")
.data(data.data)
.enter()
.append("svg")
.attr("width", width)
.attr("height", height)
.append("g")
.attr("transform", "translate(" + width / 2 + "," + height / 2 + ")");
var g = svg.selectAll(".arc")
.data(function(d,i) { return pie(d["pie"+(i+1)].results) })
.enter().append("g")
.attr("class", "arc")
g.append("path")
.attr("d", arc)
.style("fill", function(d) { return (d.data.failures > 0) ? "#d63535" : "#22c12d"; });
.arc path {
stroke: white;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/3.5.5/d3.min.js"></script>
当然,您的数据结构确实使事情复杂化了一点。我将它保留在上面的代码片段中,但稍作更改将有助于简化代码:
var data = [
{
"name":"pie1",
"results": [
{"ratio": 0.04, "total": 7, "failures": 1},
{"ratio": 0.04, "total": 8, "failures": 0},
{"ratio": 0.04, "total": 9001, "failures": 0}
]
},
{
"name":"pie2",
"results": [
{"ratio": 0.04, "total": 10, "failures": 0},
{"ratio": 0.04, "total": 11, "failures": 1},
{"ratio": 0.04, "total": 12, "failures": 1}
]
}
]
var width = 300,
height = 300,
radius = Math.min(width, height) / 2;
var arc = d3.svg.arc()
.outerRadius(radius)
.innerRadius(radius - (radius / 2));
var pie = d3.layout.pie()
.sort(null)
.value(function(d) { return d.ratio; });
var svg = d3.select("body").selectAll("svg")
.data(data)
.enter()
.append("svg")
.attr("width", width)
.attr("height", height)
.append("g")
.attr("transform", "translate(" + width / 2 + "," + height / 2 + ")");
var g = svg.selectAll(".arc")
.data(function(d,i) { return pie(d.results) })
.enter().append("g")
.attr("class", "arc")
g.append("path")
.attr("d", arc)
.style("fill", function(d) { return (d.data.failures > 0) ? "#d63535" : "#22c12d"; });
.arc path {
stroke: white;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/3.5.5/d3.min.js"></script>
第二个代码段不需要访问数据数组中每个元素的唯一 属性。如果每个数据元素都有唯一标识符,只需将其分配给数据数组中每个项目的通用名称 属性。
您不必将数据绑定到多个 svg 元素,您也可以使用多个 g 元素(例如)。
我已经成功地使用 d3 和 JSON 创建了我的第一个饼图,但我正在努力让多个饼图出现。我查看了许多示例,包括 Mike Bostock's donut-multiples,看来我的代码中需要一个 for-each 循环。
这是我的程序的简化版本,它生成一个饼图而不是两个:
<!doctype html>
<html>
<head>
<title>Pie Chart Test</title>
<meta charset="UTF-8">
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/3.5.5/d3.min.js"></script>
</head>
<style>
.arc path {
stroke: white;
}
</style>
<body>
<script>
var width = 300,
height = 300,
radius = Math.min(width, height) / 2;
var arc = d3.svg.arc()
.outerRadius(radius)
.innerRadius(radius - (radius / 2));
var pie = d3.layout.pie()
.sort(null)
.value(function(d) { return d.ratio; });
var svg = d3.select("body").append("svg")
.attr("width", width)
.attr("height", height)
.append("g")
.attr("transform", "translate(" + width / 2 + "," + height / 2 + ")");
d3.json("data.json", function(error, data) {
if (error) throw error;
node = data.data[0].pie1.results;
var g = svg.selectAll(".arc")
.data(pie(node))
.enter().append("g")
.attr("class", "arc")
g.append("path")
.attr("d", arc)
.style("fill", function(d) {
if (d.data.failures > 0) {
return "#d63535";
} else {
return "#22c12d";
}
});
});
</script>
</body>
</html>
这是填充图表的 JSON:
{"data":[
{"pie1": {
"results": [
{"ratio": 0.04, "total": 7, "failures": 1},
{"ratio": 0.04, "total": 8, "failures": 0},
{"ratio": 0.04, "total": 9001, "failures": 0}
]}
},
{"pie2": {
"results": [
{"ratio": 0.04, "total": 10, "failures": 0},
{"ratio": 0.04, "total": 11, "failures": 1},
{"ratio": 0.04, "total": 12, "failures": 1}
]}
}
]}
您有密码 node = data.data[0].pie1.results;
但是你对 pie2 什么也没做。
如果您想对其进行硬编码,您可以使用 node2 = data.data[0].pie2.results;
,但最好尝试以编程方式进行。
类似
for( pie in data.data[0] ){
node = pie.results;
var g = svg.selectAll(".arc")
.data(pie(node))
.enter().append("g")
.attr("class", "arc")
g.append("path")
.attr("d", arc)
.style("fill", function(d) {
if (d.data.failures > 0) {
return "#d63535";
} else {
return "#22c12d";
}
});
}
it appears I need a for-each loop in my code
一般来说,d3中for each循环是可以避免的。大多数 d3 的规范示例都避免使用它们。
您可以使用标准的 d3 输入选择来为数据数组中的每个项目创建一个 svg 元素(即每个饼图一个 svg),而不是 for-each 循环。这将每个饼图的数据绑定到其各自的 svg:
var svg = d3.select("body").selectAll("svg")
.data(data.data)
.enter()
.append("svg")
.attr("width", width)
.attr("height", height)
.append("g")
.attr("transform", "translate(" + width / 2 + "," + height / 2 + ")");
现在您可以使用绑定数据为每个 svg 创建饼图:
var g = svg.selectAll(".arc")
.data(function(d,i) { return pie(d["pie"+(i+1)].results) })
.enter().append("g")
.attr("class", "arc")
g.append("path")
.attr("d", arc)
.style("fill", function(d) { return (d.data.failures > 0) ? "#d63535" : "#22c12d"; });
所有这些看起来像:
var data = {"data":[
{"pie1": {
"results": [
{"ratio": 0.04, "total": 7, "failures": 1},
{"ratio": 0.04, "total": 8, "failures": 0},
{"ratio": 0.04, "total": 9001, "failures": 0}
]}
},
{"pie2": {
"results": [
{"ratio": 0.04, "total": 10, "failures": 0},
{"ratio": 0.04, "total": 11, "failures": 1},
{"ratio": 0.04, "total": 12, "failures": 1}
]}
}
]};
var width = 300,
height = 300,
radius = Math.min(width, height) / 2;
var arc = d3.svg.arc()
.outerRadius(radius)
.innerRadius(radius - (radius / 2));
var pie = d3.layout.pie()
.sort(null)
.value(function(d) { return d.ratio; });
var svg = d3.select("body").selectAll("svg")
.data(data.data)
.enter()
.append("svg")
.attr("width", width)
.attr("height", height)
.append("g")
.attr("transform", "translate(" + width / 2 + "," + height / 2 + ")");
var g = svg.selectAll(".arc")
.data(function(d,i) { return pie(d["pie"+(i+1)].results) })
.enter().append("g")
.attr("class", "arc")
g.append("path")
.attr("d", arc)
.style("fill", function(d) { return (d.data.failures > 0) ? "#d63535" : "#22c12d"; });
.arc path {
stroke: white;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/3.5.5/d3.min.js"></script>
当然,您的数据结构确实使事情复杂化了一点。我将它保留在上面的代码片段中,但稍作更改将有助于简化代码:
var data = [
{
"name":"pie1",
"results": [
{"ratio": 0.04, "total": 7, "failures": 1},
{"ratio": 0.04, "total": 8, "failures": 0},
{"ratio": 0.04, "total": 9001, "failures": 0}
]
},
{
"name":"pie2",
"results": [
{"ratio": 0.04, "total": 10, "failures": 0},
{"ratio": 0.04, "total": 11, "failures": 1},
{"ratio": 0.04, "total": 12, "failures": 1}
]
}
]
var width = 300,
height = 300,
radius = Math.min(width, height) / 2;
var arc = d3.svg.arc()
.outerRadius(radius)
.innerRadius(radius - (radius / 2));
var pie = d3.layout.pie()
.sort(null)
.value(function(d) { return d.ratio; });
var svg = d3.select("body").selectAll("svg")
.data(data)
.enter()
.append("svg")
.attr("width", width)
.attr("height", height)
.append("g")
.attr("transform", "translate(" + width / 2 + "," + height / 2 + ")");
var g = svg.selectAll(".arc")
.data(function(d,i) { return pie(d.results) })
.enter().append("g")
.attr("class", "arc")
g.append("path")
.attr("d", arc)
.style("fill", function(d) { return (d.data.failures > 0) ? "#d63535" : "#22c12d"; });
.arc path {
stroke: white;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/3.5.5/d3.min.js"></script>
第二个代码段不需要访问数据数组中每个元素的唯一 属性。如果每个数据元素都有唯一标识符,只需将其分配给数据数组中每个项目的通用名称 属性。
您不必将数据绑定到多个 svg 元素,您也可以使用多个 g 元素(例如)。