有没有办法告诉 crossfilter 将数组元素视为单独的记录,而不是将整个数组视为单个键?
Posted
技术标签:
【中文标题】有没有办法告诉 crossfilter 将数组元素视为单独的记录,而不是将整个数组视为单个键?【英文标题】:Is there a way to tell crossfilter to treat elements of array as separate records instead of treating whole array as single key? 【发布时间】:2013-07-05 16:00:06 【问题描述】:我有一个数据集,其中一些字段值是数组,我想使用 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】:自己解决了,这里是工作代码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();
【讨论】:
您提供的 jsfiddle 非常混乱,并且有很多缩小的 javascript。但是谢谢,这肯定是需要的。 我必须在 fiddle 中包含最新版本的 crossfiler 和 dc.js 才能使其工作。也许有更好的方法来添加这些外部资源。这是我第一次使用 JS Fiddle 很棒的答案!你救了我! 来自 Olivier Nerot:这个解决方案听起来很相关(非常感谢),但是当您选择一个标签时,标签图表似乎没有更新。在小提琴示例中,如果您选择“tag3”,我希望将 tag1 和 tag2 降低为 0(因为只有 KEY-3 有 tag3)。选择其他图表上的记录有效,所以我想知道为什么在选择标签时也没有进行 reduce()。目前,我还没有找到解决方案,但如果我找到任何解决方案,我将编辑此答案以添加它。欢迎任何帮助。【参考方案2】:上面的例子是一个很好的方法。不过,您可以更进一步。 在上面的解决方案中,它只会根据您所做的第一个选择进行过滤。任何后续选择都将被忽略。
如果您希望它响应所有选择,您将创建一个 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/
【讨论】:
根据以下 Matt 的建议进行了更新。很好的收获。 交叉过滤器的链接在您的小提琴中损坏。如果你将它更新到 CDN,它可以工作:cdnjs.cloudflare.com/ajax/libs/crossfilter/1.3.7/crossfilter.js 看在杰夫身上。这是my implementation of this。 有谁知道如何处理嵌套为对象的值?我得到了计数,但它的过滤不起作用。这是一个基于@geotheory 小提琴的示例:jsfiddle.net/na1ey6uk/1 我想通了,我必须稍微调整过滤功能以匹配正确的对象属性:jsfiddle.net/na1ey6uk/2【参考方案3】:Jeff 的回答确实有效,但无需跟踪“找到”变量或在找到项目时继续循环。如果 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 方法,它可以处理所有情况。
【讨论】:
也许现在删除这个,因为上面的答案已经整合了您建议的更改?你会(当之无愧地)保持你的代表。【参考方案4】:我想尝试为 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 对该对象进行了修补,使其具有像这样的“全部”方法:
// 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 提供更新示例
【讨论】:
【参考方案5】:现在这更容易了,因为 crossfilter
和 dc
支持数组维度。有关上下文和示例,请参阅此问题:Using dimensions with arrays in dc.js/crossfilter
【讨论】:
以上是关于有没有办法告诉 crossfilter 将数组元素视为单独的记录,而不是将整个数组视为单个键?的主要内容,如果未能解决你的问题,请参考以下文章
有没有办法告诉 Chrome 网络调试器在页面坐标中显示当前鼠标位置?