4

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

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} 等。据我所知,交叉过滤器只能输入每行的属性方面。有没有解决的办法?

4

2 回答 2

4

接受挑战!

确实,crossfilter 不支持这种双重缩减,但如果您愿意接受效率的轻微损失,您可以创建具有所需行为的“假维度”和“假组”。幸运的是,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在这里:http: //jsfiddle.net/gordonwoodhull/55zf7L1L/

于 2014-11-28T22:27:45.677 回答
1

在此处输入图像描述 JSFillde 链接

非规范化 + 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 可以非常强大,API 可以从这里访问。 JSFillde 链接

于 2014-02-04T21:32:34.460 回答