4

我正在使用 Mike Bostock 的crossfilter库来过滤和排序大型数据集。我的问题:给定一个具有多个维度的数据集,我如何一次对多个维度进行排序?

示例 JSFiddle

测试数据集:

[
    { cat: "A", val:1 },
    { cat: "B", val:2 },
    { cat: "A", val:11 },
    { cat: "B", val:5 },
    { cat: "A", val:3 },
    { cat: "B", val:2 },
    { cat: "A", val:11 },
    { cat: "B", val:100 }
]

所需输出的示例,按cat, val(升序)排序:

[
    { cat: "A", val:1 },
    { cat: "A", val:3 },
    { cat: "A", val:11 },
    { cat: "A", val:11 },
    { cat: "B", val:2 },
    { cat: "B", val:2 },
    { cat: "B", val:5 },
    { cat: "B", val:100 }
]

到目前为止,我使用的方法是在所需维度上使用字符串连接:

var combos = cf.dimension(function(d) { return d.cat + '|' + d.val; });

这适用于多个基于字符串的维度,但不适用于数字维度,因为它不是自然排序 ( '4' > '11')。我想我可以在数字上使用零填充来完成这项工作,但这对于大型数据集来说可能会变得昂贵,所以我宁愿避免它。有没有另一种方法可以在这里工作,使用交叉过滤器?

任何允许不同维度具有不同排序方向(升序/降序)的解决方案的奖励积分。

澄清:是的,我可能需要切换到本机Array.sort实现。但是使用 crossfilter 的全部意义在于它非常非常快,尤其是对于大型数据集,并且它以一种使重复排序更快的方式缓存排序顺序。所以我真的在这里寻找基于交叉过滤器的答案。

4

4 回答 4

2

我知道它没有使用 crossfilter 库,但为什么不使用 sort 函数来做到这一点呢?

var combos = cf.sort(function(a,b) { 
   if(a.cat == b.cat) return a.val < b.val ? -1 : 1;
   return a.cat < b.cat ? -1 : 1;
});

http://jsfiddle.net/cQXNK/5/

要允许不同的维度具有不同的排序方向,只需将 -1 交换为 1,反之亦然

于 2013-01-09T19:43:36.497 回答
1

使用Array.prototype.sort,您可以:

function sortByPriority(a, b) {
    var p = sortByPriority.properties;
    function pad (str, max) {
        str = String(str);
        return str.length < max ? pad("0" + str, max) : str;
    }

    if (!p) {
        return a - b;
    }
    var ar ='', br = '';
    for (var i = 0, max = p.length; i < max; i++) {
        ar += pad(a[p[i]], 10);
        br += pad(b[p[i]], 10);
    }
    return ar == br ? 0 : ar > br ? 1 : - 1;
}

如何使用:

cat然后排序val

sortByPriority.properties = ['cat', 'val'];
myArray.sort(sortByPriority);

结果:

  • 1
  • 一个 3
  • 11
  • 11
  • 乙二
  • 乙二
  • 乙 5
  • 乙 100

如果你想事先val做:

sortByPriority.properties = ['val', 'cat'];
myArray.sort(sortByPriority);

结果:

  • 1
  • 乙二
  • 乙二
  • 一个 3
  • 乙 5
  • 11
  • 11
  • 乙 100

不是一个超级有效的代码,但你可以改进它。

更新:

您可以使用该pad函数通过 crossfilter 获得相同的结果,看看这个 jsfiddle

var combos = cf.dimension(function(d) { 
    return pad(d.cat, 10) + '|' + pad(d.val, 10); 
});

您还可以从“coll”中的最大字符串更改填充大小相同的长度,这将确保结果。

看到优化:http: //jsfiddle.net/gartz/cQXNK/7/

于 2013-01-09T20:26:09.967 回答
1

这就是我最终做的事情:

  • 我仍然在单个新维度上使用字符串连接,但是
  • 在将度量转换为字符串之前,我将度量转换为正的、可比较的小数,使用交叉过滤器获取最小值/最大值:

    var vals = cf.dimension(function(d) { return d.val }),
        min = vals.bottom(1)[0].val,
        offset =  min < 0 ? Math.abs(min) : 0,
        max = vals.top(1)[0].val + offset,
        valAccessor = function(d) {
            // offset ensures positive numbers, fraction ensures sort order
            return ((d.val + offset) / max).toFixed(8);
        },
        combos = cf.dimension(function(d) { 
            return d.cat + '|' + valAccessor(d); 
        });
    

见工作小提琴:http: //jsfiddle.net/nrabinowitz/cQXNK/9/

这具有正确处理负数的优点 - 据我所知,零填充是不可能的。它似乎也一样快。缺点是它需要在数字列上创建一个新维度,但就我而言,无论如何我通常都需要这样做。

于 2013-01-11T20:21:12.850 回答
0

我还没有测试过性能,但你可以试试 d3.nest。示例代码:

var nested = d3.nest()
.key(function(d) { return d.cat; })
.sortKeys(d3.ascending)
.sortValues(compareValues)
.entries(data);

在这里查看整个小提琴:http: //jsfiddle.net/RFontana/bZX7Q/

如果你运行一些 jsperf,让我知道你会得到什么结果 :)

于 2013-10-22T11:13:36.547 回答