DC.js交叉滤波器维度计数的直方图

时间:2013-11-14 22:12:22

标签: crossfilter dc.js

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

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只能拥有每一行的属性输入维度。有没有解决的办法?

2 个答案:

答案 0 :(得分:3)

接受挑战!

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

技巧的第一部分是复制维度和组,以便新维度和旧维度都会在另一维上观察过滤。

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

下面是一般解决方案的开始。对于某些图表,还需要实现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 here:http://jsfiddle.net/gordonwoodhull/55zf7L1L/

答案 1 :(得分:0)

enter image description here JSFillde Link

Denormalize + Map-reduce。请注意,数据已经包含每个项目的成本作为第4列(这可以很容易地预先计算)。这是一个黑客,但希望是一个简单的方法,以使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