javascript - d3 v4 从 CSV 堆叠到分组条形图
问题描述
参考Mike Bostick 的 Stacked to Grouped Bar Chart 示例,我正在修改它以使用 CSV 文件。我已经为此工作了几个星期,并查看了 Stack Overflow 和其他地方的无数示例,我被难住了。
堆积条形图有效。
堆积条形图:
当我转换到分组条形图时,我只会在引用堆叠或分组的键或系列时遇到问题。现在所有的矩形都显示在彼此的顶部,而不是彼此相邻。
分组条形图:
在函数transitionStep2()
中,我想乘以对应于系列或键的数字。我目前乘以这个函数中的数字 1 作为占位符 .attr("x", function(d) { return x(d.data.Year) + x.bandwidth() / 7 * 1; })
。
<!DOCTYPE html>
<script src="https://d3js.org/d3.v4.min.js"></script>
<html><body>
<form>
<label><input type="radio" name="mode" style="margin-left: 10" value="step1" checked>1</label>
<label><input type="radio" name="mode" style="margin-left: 20" value="step2">2</label>
</form>
<svg id = "bar" width = "500" height = "300"></svg>
<script>
var svg = d3.select("#bar"),
margin = {top: 20, right: 20, bottom: 20, left: 20},
width = +svg.attr("width") - margin.left - margin.right,
height = +svg.attr("height") - margin.top - margin.bottom,
g = svg.append("g").attr("transform", "translate(" + margin.left + "," + margin.top + ")");
var x = d3.scaleBand()
.rangeRound([0, width])
.padding(0.08);
var y = d3.scaleLinear()
.range([height, 0]);
var color = d3.scaleOrdinal()
.range(["#7fc97f", "#beaed4", "#fdc086", "#ffff99"]);
d3.csv("data.csv", function(d, i, columns) {
for (i = 1, t = 0; i < columns.length; ++i) t += d[columns[i]] = +d[columns[i]];
d.total = t;
return d;
}, function(error, data) {
if (error) throw error;
var keys = data.columns.slice(1);
x.domain(data.map(function(d) { return d.Year; }));
y.domain([0, d3.max(data, function(d) { return d.total; })]).nice();
color.domain(keys);
g.append("g")
.selectAll("g")
.data(d3.stack().keys(keys)(data))
.enter().append("g")
.attr("fill", function(d) { return color(d.key); })
.selectAll("rect")
.data(function(d) { return d; })
.enter().append("rect")
.attr("x", function(d) { return x(d.data.Year); })
.attr("y", function(d) { return y(d[1]); })
.attr("height", function(d) { return y(d[0]) - y(d[1]); })
.attr("width", x.bandwidth());
rect = g.selectAll("rect");
});
d3.selectAll("input")
.on("change", changed);
function changed() {
if (this.value === "step1") transitionStep1();
else if (this.value === "step2") transitionStep2();
}
function transitionStep1() {
rect.transition()
.attr("y", function(d) { return y(d[1]); })
.attr("x", function(d) { return x(d.data.Year); })
.attr("width", x.bandwidth())
.attr("stroke", "green");
}
function transitionStep2() {
rect.transition()
.attr("x", function(d) { return x(d.data.Year) + x.bandwidth() / 7 * 1; })
.attr("width", x.bandwidth() / 7)
.attr("y", function(d) { return y(d[1] - d[0]); })
.attr("stroke", "red");
}
</script></body></html>
和 csv 文件:
Year,A,B,C,D
1995,60,47,28,39
1996,29,56,99,0
1997,30,26,63,33
1998,37,16,48,0
1999,46,49,64,21
2000,78,88,81,57
2001,18,11,11,64
2002,91,76,79,64
2003,30,99,96,79
解决方案
您所指的示例具有可将图表分组的线性值,因此.attr("x", function(d, i) { return x(i) + x.bandwidth() / n * this.parentNode.__data__.key; })
可以正常工作。
在您的情况下,列/键不是线性比例,而是您必须设置比例的序数值集:
要做到这一点,即设置一个序数比例,这是可以做的:
var x1 = d3.scaleBand();
x1.domain(keys).rangeRound([0, x.bandwidth()]);
所以这个新的规模将有一个x
规模的带宽范围,范围为["A", "B", "C", "D"]
. 使用这个比例来设置 s 的x
属性来对rect
它们进行分组:
.attr("x", function(d) {
return x(d.data.Year) + x1(d3.select(this.parentNode).datum().key);
})
其中d3.select(this.parentNode).datum().key
表示列名。
这是JSFIDDLE(我曾经d3.csvParse
解析过数据,但我相信您会明白这一点。这只是x
您需要重置的属性。
这是一个使用文件的Plunkr 。
这里还有一个代码片段:
var svg = d3.select("#bar"),
margin = {top: 20, right: 20, bottom: 20, left: 20},
width = +svg.attr("width") - margin.left - margin.right,
height = +svg.attr("height") - margin.top - margin.bottom,
g = svg.append("g").attr("transform", "translate(" + margin.left + "," + margin.top + ")");
var x = d3.scaleBand()
.rangeRound([0, width])
.padding(0.08);
var x1 = d3.scaleBand();
var y = d3.scaleLinear()
.range([height, 0]);
var color = d3.scaleOrdinal()
.range(["#7fc97f", "#beaed4", "#fdc086", "#ffff99"]);
var csv = 'Year,A,B,C,D\n1995,60,47,28,39\n1996,29,56,99,0\n1997,30,26,63,33\n1998,37,16,48,0\n1999,46,49,64,21\n2000,78,88,81,57\n2001,18,11,11,64\n2002,91,76,79,64\n2003,30,99,96,79';
var data = d3.csvParse(csv), columns = ["A", "B", "C", "D"];
data.forEach(function(d) {
for (i = 1, t = 0; i < columns.length; ++i) t += d[columns[i]] = +d[columns[i]];
d.total = t;
});
var keys = columns;
x.domain(data.map(function(d) { return d.Year; }));
x1.domain(keys).rangeRound([0, x.bandwidth()]);
y.domain([0, d3.max(data, function(d) { return d.total; })]).nice();
color.domain(keys);
g.append("g")
.selectAll("g")
.data(d3.stack().keys(keys)(data))
.enter().append("g")
.attr("fill", function(d) { return color(d.key); })
.selectAll("rect")
.data(function(d) { return d; })
.enter().append("rect")
.attr("x", function(d) { return x(d.data.Year); })
.attr("y", function(d) { return y(d[1]); })
.attr("height", function(d) { return y(d[0]) - y(d[1]); })
.attr("width", x.bandwidth());
rect = g.selectAll("rect");
d3.selectAll("input")
.on("change", changed);
function changed() {
if (this.value === "step1") transitionStep1();
else if (this.value === "step2") transitionStep2();
}
function transitionStep1() {
rect.transition()
.attr("y", function(d) { return y(d[1]); })
.attr("x", function(d) { return x(d.data.Year); })
.attr("width", x.bandwidth())
.attr("stroke", "green");
}
function transitionStep2() {
rect.transition()
.attr("x", function(d) {
return x(d.data.Year) + x1(d3.select(this.parentNode).datum().key);
})
.attr("width", x.bandwidth() / 7)
.attr("y", function(d) { return y(d[1] - d[0]); })
.attr("stroke", "red");
}
<!DOCTYPE html>
<script src="https://d3js.org/d3.v4.min.js"></script>
<html><body>
<form>
<label><input type="radio" name="mode" style="margin-left: 10" value="step1" checked>1</label>
<label><input type="radio" name="mode" style="margin-left: 20" value="step2">2</label>
</form>
<svg id = "bar" width = "500" height = "300"></svg>
希望有帮助。:)
推荐阅读
- java - 将 Swagger 端点导出为单个 json 文件
- typescript - 打字稿部分联合类型
- java - Android工作室片段活动中的谷歌地图
- java - 使用 maven 将额外的配置文件夹添加到类路径
- quarkus - 简单的 Quarkus Kogito 项目无法构建 - 几个模棱两可且不满足的依赖项
- angular - 我是 Angular 的新手,并试图在每个(onchange)事件之后获取输入并将此输入发送到 set 方法。见下面的代码
- c# - 如果在 foxpro 中,IIF 功能是否被重新定义?
- scala - 如何通过检查不正确的字符串模式来编写测试以进行解析
- reactjs - 放置 React 错误边界组件的理想位置?
- java - 尝试发出简单的 Http GET 请求时连接超时