通过多维数据集条形图排序

时间:2019-06-29 04:57:10

标签: javascript sorting d3.js svg

我正在尝试使用d3创建条形图,并使用包含3个值的集合的数据集。第一个值是条形高度,第二个值是条形宽度,第三个值是不透明度。我现在的目标是能够通过单击来按高度,宽度和不透明度按周期进行排序。但是,我觉得第一次点击后似乎得到了随机结果。

我应该使用模运算,所以我在点击函数之外设置了var s = -1,然后设置s =(s + 1)%3。在sort函数中,我返回了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);
        })
});

我希望按高度,宽度和不透明度排序。第一次点击时我会感到身高,但接下来的点击似乎是随机的。

1 个答案:

答案 0 :(得分:0)

问题在于看似无害的数据绑定:

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>