【问题标题】:Crossfilter query交叉过滤查询
【发布时间】:2025-12-21 07:20:16
【问题描述】:

是否可以过滤以数组为值的交叉过滤器数据集?

例如,假设我有以下数据集:

var data = [
  {
    bookname: "the joy of clojure",
    authors: ["Michael Fogus", "Chris Houser"],
    tags: ["clojure", "lisp"]
  },
  {
    bookname: "Eloquent Ruby",
    authors: ["Russ Olsen"],
    tags: ["ruby"]
  },
  {
    bookname: "Design Patterns in Ruby",
    authors: ["Russ Olsen"],
    tags: ["design patterns", "ruby"]
  }
];

有没有一种简单的方法可以访问带有特定标签的书籍?还有那些有特定作者的书?到目前为止,我理解如何使用交叉过滤器的方式让我做了这样的事情:

var filtered_data = crossfilter(data);
var tags = filtered_data.dimension(function(d) {return d.tags});
var tag = tags.group();

然后当我访问分组时(像这样):

tag.all()

我明白了:

[{key: ["clojure", "lisp"], value: 1}, 
 {key: ["design patterns", "ruby"], value: 1}, 
 {key: ["ruby"], value: 1}]

当我更喜欢这个时:

[{key: "ruby", value: 2}, 
 {key: "clojure", value: 1}, 
 {key: "lisp", value: 1},
 {key: "design patterns", value: 1}]

【问题讨论】:

  • 我相信我的问题会随着时间的推移变得越来越模糊。这是相当不幸的。
  • 一点也不晦涩。我今天正在寻找这个。

标签: d3.js crossfilter


【解决方案1】:

我在下面的代码中添加了 cmets。大图:使用reduce函数。

var data = ...
var filtered_data = crossfilter(data);
var tags = filtered_data.dimension(function(d) {return d.tags});

tags.groupAll().reduce(reduceAdd, reduceRemove, reduceInitial).value()

注意我是如何使用 groupAll() 而不是 group() b/c 我们希望我们的 reduce 函数(定义如下)在一个组上操作而不是3组。

现在 reduce 函数应该如下所示:

/*
 v is the row in the dataset

 p is {} for the first execution (passed from reduceInitial). 
 For every subsequent execution it is the value returned from reduceAdd of the prev row
*/
function reduceAdd(p, v) {
  v.tags.forEach (function(val, idx) {
     p[val] = (p[val] || 0) + 1; //increment counts
  });
  return p;
}

function reduceRemove(p, v) {
   //omitted. not useful for demonstration
}

function reduceInitial() {
  /* this is how our reduce function is seeded. similar to how inject or fold 
   works in functional languages. this map will contain the final counts 
   by the time we are done reducing our entire data set.*/
  return {};  
}

【讨论】:

  • 啊..我完全忽略了使用 groupAll 函数。谢谢,我还没有测试过你的答案,但它看起来绝对是我需要的正确组合。非常感谢!
  • 这太棒了。谢谢。
  • 答案的作者或其他人可以完成这个答案吗?因为问题是“是否有一种简单的方法可以访问由特定标签标记的书籍?以及具有特定作者的书籍?”但目前这个答案只能产生一些“.value()”结果并且它不清楚(至少对我来说,可能是因为对 Crossfilter 来说是全新的,但仍然是)如何产生这些问题所要求的最终答案,以及我也可以用知道。提前致谢。
  • 这对于在不考虑单个组的情况下查找选择平均值很有用。谢谢。
  • @DestinyArchitect:未经测试,但我认为您正在寻找这样的东西:var reduction = tags.groupAll().reduce(reduceAdd, reduceRemove, reduceInitial);var books_on_design_patterns = reduction.value()["design patterns"]
【解决方案2】:

我从未使用过“crossfilter”(我假设这是一个 JS 库)。不过这里有一些纯 JS 方法。

这...

data.filter(function(d) {
  return d.authors.indexOf("Michael Fogus") !== -1;
})

返回这个:

[{bookname:"the joy of clojure", authors:["Michael Fogus", "Chris Houser"], tags:["clojure", "lisp"]}]

这...

var res = {};
data.forEach(function(d) {
  d.tags.forEach(function(tag) {
    res.hasOwnProperty(tag) ? res[tag]++ : res[tag] = 1
  });
})

返回这个:

({clojure:1, lisp:1, ruby:2, 'design patterns':1})

对于其中任何一个,您都可以申请 d3.entries 以获取您的 {key:"ruby", value: 2} 格式。

【讨论】:

  • 感谢您的努力,但我使用交叉过滤器的原因是为了对一些相当大的数据集进行排序。 Crossfilter 实际上是另一个 mike bostock 库,是一个非常有趣的想法,如果我能弄清楚如何正确使用它的话..