javascript - 通过多维数据集条形图排序
问题描述
我正在尝试使用 d3 和包含 3 个值的数据集创建条形图。第一个值是条的高度,第二个是条的宽度,第三个是不透明度。我现在的目标是能够通过点击在一个循环中按高度、宽度和不透明度进行排序。但是,我觉得在第一次点击后我得到了看似随机的结果。
我应该使用取模运算,所以我在 click 函数之外设置了 var s = -1,然后设置了 s = (s+1)%3。在 sort 函数中,我有 return d3.ascending(a[s],b[s]);
。
这是我的点击和排序代码。
var s = -1;
svg.on("click", function() {
s = (s + 1)
svg.selectAll("rect")
.data(dataset)
.sort(function(a, b) {
return d3.ascending(a[s], b[s]);
})
.transition()
.duration(500)
.attr("x", function(d, i) {
return xScale(i);
})
});
我期望按高度、宽度和不透明度排序。我在第一次点击时得到高度,但接下来的点击似乎是随机的。
解决方案
问题是这个看似无害的数据绑定:
svg.selectAll("rect")
.data(dataset)// <----- here
.sort(function(a, b) {
//etc...
问题是您将数据附加到先前按索引排序的矩形。这是弄乱排序。
这是一个简单的解释。假设您有 5 个元素(大写),对应于[d, a, e, c, b]
要按字母顺序排序的数据数组(小写):
+------+------+------+------+------+
| D(d) | A(a) | E(e) | C(c) | B(b) |
+------+------+------+------+------+
排序后,您有:
+------+------+------+------+------+
| A(a) | B(b) | C(c) | D(d) | E(e) |
+------+------+------+------+------+
[d, a, e, c, b]
当您将数据(请记住,按该顺序排列的相同数组)重新绑定到已排序的元素时,您将拥有:
+------+------+------+------+------+
| A(d) | B(a) | C(e) | D(c) | E(b) |
+------+------+------+------+------+
如果您按数据排序(这就是这样selection.sort()
做的),您将拥有:
+------+------+------+------+------+
| B(a) | E(b) | D(c) | A(d) | C(e) |
+------+------+------+------+------+
正如你所说,这就是为什么它看起来很随机。
有两种解决方案:
解决方案1:删除数据绑定
最简单的方法是删除数据绑定,因为矩形已经有数据。这是一个包含 3 个矩形的示例,例如每个矩形具有最大的高度、宽度或不透明度:
var data = [
[45, 90, 0.5],
[95, 10, 0.2],
[15, 40, 0.9]
];
var xScale = d3.scaleOrdinal()
.domain([0, 1, 2])
.range([0, 100, 200]);
var svg = d3.select("svg");
var span = d3.select("span")
svg.selectAll(null)
.data(data)
.enter()
.append("rect")
.attr("width", d => d[1])
.attr("height", d => d[0])
.style("opacity", d => d[2])
.attr("x", (_, i) => xScale(i));
var s = -1;
svg.on("click", function() {
s = (s + 1);
span.html(["height", "width", "opacity"][s % 3]);
svg.selectAll("rect")
.sort(function(a, b) {
return d3.ascending(a[s % 3], b[s % 3]);
})
.transition()
.duration(500)
.attr("x", function(d, i) {
return xScale(i);
})
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/5.7.0/d3.min.js"></script>
<p>Sort by: <span></span></p>
<svg></svg>
解决方案2:使用一键功能
第二种解决方案是使用key function,因此您不通过索引绑定数据,而是根据每个矩形的唯一标识来绑定数据。
在我的虚拟数据中,每个高度值都是唯一的,所以我只是这样做:
.data(dataset, d=>d[0])
这是演示:
var data = [
[45, 90, 0.5],
[95, 10, 0.2],
[15, 40, 0.9]
];
var xScale = d3.scaleOrdinal()
.domain([0, 1, 2])
.range([0, 100, 200]);
var svg = d3.select("svg");
var span = d3.select("span")
svg.selectAll(null)
.data(data)
.enter()
.append("rect")
.attr("width", d => d[1])
.attr("height", d => d[0])
.style("opacity", d => d[2])
.attr("x", (_, i) => xScale(i));
var s = -1;
svg.on("click", function() {
s = (s + 1);
span.html(["height", "width", "opacity"][s % 3]);
svg.selectAll("rect")
.data(data, d=>d[0])
.sort(function(a, b) {
return d3.ascending(a[s % 3], b[s % 3]);
})
.transition()
.duration(500)
.attr("x", function(d, i) {
return xScale(i);
})
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/5.7.0/d3.min.js"></script>
<p>Sort by: <span></span></p>
<svg></svg>
推荐阅读
- python - Redis中是否可以按值获取数据?
- c# - 如何使用 WCF 将 pnrp.net 对等名称解析器更改为自定义域对等名称解析器?
- f# - ExcelDna F# 和可选参数
- scala - 如何迭代 DataStream
- php - 为同一组中的所有用户获取数据
- git - 我正在尝试自动化 git clone 请求,压缩结果,并将旧副本覆盖到所有通过一个操作运行
- excel - 尝试在 Excel 中创建自定义搜索按钮
- css - 向下滚动时隐藏,向上滚动时显示
- tensorflow - 如何使用 train_image_classifier.py 训练性别和年龄
- jquery - 在表单中选定控件的 jquery 事件上显示标签(HTMLHelper)