有没有办法告诉 crossfilter 将数组元素视为单独的记录而不是将整个数组视为单个键?

IT技术 javascript d3.js crossfilter dc.js
2021-02-12 16:16:34

我有一些字段值是数组的数据集,我想使用 crossfilter 和 d3.js 或 dc.js 来显示每个值在数据集中出现的次数的直方图。

下面是一个例子:

var data = [
    {"key":"KEY-1","tags":["tag1", "tag2"]},
    {"key":"KEY-2","tags":["tag2"]},
    {"key":"KEY-3","tags":["tag3", "tag1"]}];

var cf = crossfilter(data);

var tags = cf.dimension(function(d){ return d.tags;});
var tagsGroup = tags.group();


dc.rowChart("#chart")
    .renderLabel(true)
    .dimension(tags)
    .group(tagsGroup)
    .xAxis().ticks(3);

dc.renderAll();

和 JSFiddle http://jsfiddle.net/uhXf5/2/

当我运行该代码时,它会生成如下图:

图1

但我想要的是这样的:

在此处输入图片说明

为了让事情变得更加复杂,能够点击任何行并通过点击的标签过滤数据集会很棒。

任何人有任何想法如何实现这一目标?

谢谢,科斯佳

5个回答

自己解决了,这里有工作代码http://jsfiddle.net/uhXf5/6/

这是代码,以防有人遇到类似问题:

function reduceAdd(p, v) {
  v.tags.forEach (function(val, idx) {
     p[val] = (p[val] || 0) + 1; //increment counts
  });
  return p;
}

function reduceRemove(p, v) {
  v.tags.forEach (function(val, idx) {
     p[val] = (p[val] || 0) - 1; //decrement counts
  });
  return p;

}

function reduceInitial() {
  return {};  
}


var data = [
    {"key":"KEY-1","tags":["tag1", "tag2"], "date":new Date("10/02/2012")},
    {"key":"KEY-2","tags":["tag2"], "date": new Date("10/05/2012")},
    {"key":"KEY-3","tags":["tag3", "tag1"], "date":new Date("10/08/2012")}];

var cf = crossfilter(data);

var tags = cf.dimension(function(d){ return d.tags;});
var tagsGroup = tags.groupAll().reduce(reduceAdd, reduceRemove, reduceInitial).value();
// hack to make dc.js charts work
tagsGroup.all = function() {
  var newObject = [];
  for (var key in this) {
    if (this.hasOwnProperty(key) && key != "all") {
      newObject.push({
        key: key,
        value: this[key]
      });
    }
  }
  return newObject;
}


var dates = cf.dimension(function(d){ return d.date;});
var datesGroup = dates.group();


var chart = dc.rowChart("#chart");
    chart                                                                                       
    .renderLabel(true)
    .dimension(tags)
    .group(tagsGroup)
    .filterHandler(function(dimension, filter){     
        dimension.filter(function(d) {return chart.filter() != null ? d.indexOf(chart.filter()) >= 0 : true;}); // perform filtering
        return filter; // return the actual filter value
       })
    .xAxis().ticks(3);

var chart2 = dc.barChart("#chart2");
    chart2  
    .width(500)
    .transitionDuration(800)
    .margins({top: 10, right: 50, bottom: 30, left: 40})
    .dimension(dates)
    .group(datesGroup)
    .elasticY(true)
    .elasticX(true)
    .round(d3.time.day.round)    
    .x(d3.time.scale())    
    .xUnits(d3.time.days)
    .centerBar(true)
    .renderHorizontalGridLines(true)       
    .brushOn(true);    


dc.renderAll();
我必须将最新版本的 crossfiler 和 dc.js 包含到小提琴中以使其工作。也许有更好的方法来添加这些外部资源。这是我第一次使用 JS Fiddle
2021-03-15 16:16:34
很棒的答案!你救了我!
2021-04-07 16:16:34
您提供的 jsfiddle 非常混乱,并且有很多缩小的 javascript。但是谢谢,这肯定是需要的。
2021-04-08 16:16:34
来自 Olivier Nerot:这个解决方案听起来很重要(非常感谢),但是当您选择一个标签时,标签图表似乎没有更新。在小提琴示例中,如果您选择“tag3”,我希望将 tag1 和 tag2 降低为 0(因为只有 KEY-3 具有 tag3)。选择其他图表上的记录有效,所以我想知道为什么在选择标签时也没有进行 reduce()。目前,我还没有找到解决方案,但如果找到任何解决方案,我将编辑此答案以添加它。欢迎任何帮助。
2021-04-08 16:16:34

上面的例子是一个很好的方法。不过,您可以更进一步。在上面的解决方案中,它只会根据您所做的第一个选择进行过滤。任何后续选择都将被忽略。

如果您希望它响应所有选择,您将创建一个 filterHandler 如下:

 barChart.filterHandler (function (dimension, filters) {
   dimension.filter(null);   
    if (filters.length === 0)
        dimension.filter(null);
    else
        dimension.filterFunction(function (d) {
            for (var i=0; i < d.length; i++) {
                if (filters.indexOf(d[i]) >= 0) return true;
            }
            return false; 
        });
  return filters; 
  }
);

这里的工作示例:http : //jsfiddle.net/jeffsteinmetz/cwShL/

盯上杰夫。这是我对 this实现
2021-03-20 16:16:34
有谁知道如何处理嵌套为对象的值?我让计数工作,但它过滤不起作用。这是一个基于@geotheory 小提琴的例子:jsfiddle.net/na1ey6uk/1
2021-03-21 16:16:34
我想通了,我必须稍微调整过滤功能以匹配正确的对象属性:jsfiddle.net/na1ey6uk/2
2021-04-03 16:16:34
crossfilter 的链接在你的小提琴中坏了。如果你将它更新到 CDN,它就可以工作:cdnjs.cloudflare.com/ajax/libs/crossfilter/1.3.7/crossfilter.js
2021-04-09 16:16:34
根据下面马特的建议更新。接得好。
2021-04-10 16:16:34

我想尝试为 Jeff 和 Kostya 列出的方法提供一些背景信息。

您会注意到 tagsGroup 使用 groupAll 与典型的 group 方法不同。Crossfilter 告诉我们“返回的对象类似于标准分组,只是它没有 top 或 order 方法。相反,使用 value 来检索所有匹配记录的 reduce 值。” Kostya 调用“.value()”方法来检索代表整个组的单个对象。

var tagsGroup = tags.groupAll().reduce(reduceAdd, reduceRemove, reduceInitial).value();

此对象不适用于 dc.js,因为 dc.js 期望 group 对象具有 all 方法。Kostya 将该对象修补为具有“all”方法,如下所示:

// hack to make dc.js charts work
tagsGroup.all = function() {
  var newObject = [];
  for (var key in this) {
    if (this.hasOwnProperty(key) && key != "all") {
      newObject.push({
        key: key,
        value: this[key]
      });
    }
  }
  return newObject;
}

这将适用于简单的 dc.js 图表,但您将无法使用所有 dc.js 功能,因为并非所有组功能都存在。例如,您将无法在图表上使用“cap”方法,因为 cap 方法要求组对象具有“top”方法。您还可以像这样修补 top 方法:

topicsGroup.top = function(count) {
    var newObject = this.all();
     newObject.sort(function(a, b){return b.value - a.value});
    return newObject.slice(0, count);
};

这将使您的图表能够使用 cap 方法:

barChart
    .renderLabel(true)
    .height(200)
    .dimension(topicsDim)
    .group(topicsGroup)
    .cap(2)
    .ordering(function(d){return -d.value;})
    .xAxis().ticks(3);

http://jsfiddle.net/djmartin_umich/m7V89/#base提供了更新的示例

杰夫的回答确实有效,但没有必要跟踪“找到”变量或在找到项目时继续循环。如果 X 在 [X,Y,Z] 中,这已经将迭代量减少了 1/3。

else
    dimension.filterFunction(function (d) {
        for (var i=0; i < d.length; i++) {
            if (filters.indexOf(d[i]) >= 0) return true;
        }
        return false; 
    });

或者,您可以修补 dc.js filterFunction 方法,这将处理所有情况。

也许现在删除这个,因为上面的答案已经整合了你建议的更改?你会(当之无愧地)保留你的代表。
2021-03-20 16:16:34

这现在容易多了,因为crossfilterdc支持数组的维度。有关上下文和示例,请参阅此问题:在 dc.js/crossfilter 中使用带有数组的维度