【问题标题】:DC.js histogram of crossfilter dimension counts交叉过滤器维度计数的 DC.js 直方图
【发布时间】:2013-11-14 22:12:21
【问题描述】:

我有一个交叉过滤器,输入了以下数据结构。

project | subproject | cost
data = [
["PrA", "SubPr1", 100],
["PrA", "SubPr2", 150],
["PrA", "SubPr3", 100],
["PrB", "SubPr4", 300],
["PrB", "SubPr5", 500],
["PrC", "SubPr6", 450]]

我可以创建一个包含每个项目总成本的条形图:

var ndx = crossfilter(data)
var projDim = ndx.dimension(function(d){return d.project;});
var projGroup = costDim.group().reduceSum(function(d){return d.budget;});

我想要做的是按项目成本创建一个 dc.js 直方图...所以 {450: 2, 300: 1} 等。据我所知,crossfilter 只能有每一行的属性维度的输入。有没有解决的办法?

【问题讨论】:

  • benlaird,如果你找到了答案,你介意分享一下吗?
  • dc.js 中的直方图需要第二次分组。我目前的解决方案是不使用 dc.js 作为直方图。相反,我使用 crossfilter 进行分组,然后使用 d3 创建直方图(不需要对值进行分组)。

标签: crossfilter dc.js


【解决方案1】:

接受挑战!

确实,crossfilter 不支持这种双重缩减,但如果您愿意接受效率的轻微损失,您可以创建具有所需行为的“假尺寸”和“fake groups”。幸运的是,dc.js 并没有使用太多的 crossfilter API,因此您不必实现太多方法。

技巧的第一部分是复制维度和组,以便新维度和旧维度相互观察过滤。

第二部分是创建假组和维度,它们遍历复制组的 bin,并根据值而不是键重新组合和重新过滤。

一般解决方案的开始如下。对于某些图表,还需要实现group.top(),通常只需将其转发到group.all()即可。

function values_dimension(dim, group) {
    return {
        filter: function(v) {
            if(v !== null)
                throw new Error("don't know how to do this!");
            return dim.filter(null);
        },
        filterFunction: function(f) {
            var f2 = [];
            group.all().forEach(function(kv) {
                if(f(kv.value))
                    f2.push(kv.key);
            });
            dim.filterFunction(function(k) {
                return f2.indexOf(k) >= 0;
            });
            return this;
        }
    };
}

function values_group(group) {
    return {
        all: function() {
            var byv = [];
            group.all().forEach(function(kv) {
                if(kv.value === 0)
                    return;
                byv[kv.value] = (byv[kv.value] || 0) + 1;
            });
            var all2 = [];
            byv.forEach(function(d, i) {
                all2.push({key: i, value: d});
            });
            return all2;
        }
    };
}

// duplicate the dimension & group so each will observe filtering on the other
var projDim2 = ndx.dimension(function(d){return d.project;});
var projGroup2 = projDim2.group().reduceSum(function(d){return d.budget;});
var countBudgetDim = values_dimension(projDim2, projGroup2),
    countBudgetGroup = values_group(projGroup2);

jsfiddle 在这里:http://jsfiddle.net/gordonwoodhull/55zf7L1L/

【讨论】:

  • 谢谢!太棒了,你真是个天才!
【解决方案2】:

JSFillde Link

非规范化 + Map-reduce。请注意,数据已经将每个项目的成本包括在第 4 列(这可以很容易地预先计算)。这是一个 hack,但希望是一个简单的方法,以便让 DC.js 和 crossfilter 工作而无需太多更改。

var data = [
    ["PrA", "SubPr1", 100, 450],
    ["PrA", "SubPr2", 150, 450],
    ["PrA", "SubPr3", 200, 450],
    ["PrB", "SubPr4", 300, 800],
    ["PrB", "SubPr5", 500, 800],
    ["PrC", "SubPr6", 450, 450]
];

var newdata = data.map(function (d) {
    return {
        project: d[0],
        subproject: d[1],
        budget: d[2],
        cost: d[3]
    };
})

var ndx = crossfilter(newdata),
    costDim = ndx.dimension(function (d) {
        return d.cost;
    }),

    visitedProj = {},
    costGroup = costDim.group().reduce(function (p, v) {

        if (visitedProj[v.project]) return p;
        console.info(v.project);
        visitedProj[v.project] = true;
        return p + 1;
    }, null, function () {
        return 0;
    });

dc.rowChart("#costChart")
    .renderLabel(true)
    .dimension(costDim)
    .group(costGroup)
    .xAxis().ticks(2);

dc.renderAll();

Map-Reduce 功能非常强大,可以从here 访问 API。 JSFillde Link

【讨论】:

  • leoh,你能详细说明一下吗?有没有办法将这两个维度结合起来?这似乎不是原样的答案。
  • 当然,这个想法是 map-reduce,我已经使用问题中最初提供的示例数据通过一个简单的演示更新了我的答案。
  • 这不是本和我要问的。我们想先按项目分组,然后按成本分组。单独做每一个都很简单。重申本的问题:我想做的是按项目成本创建一个 dc.js 直方图......所以 {450: 2, 300: 1}
  • 现在我记得我发布的原始答案。对不起,我花了一段时间才把它找回来。答案已更新。
  • 嗯。此 hack 不与 crossfilter 集成(请参阅我在赏金中的评论)。假设我们过滤掉 subpr2。直方图中的数字都没有正确更新。
猜你喜欢
  • 2019-07-18
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多