crossfilter“double grouping”其中key是另一个减少的值

时间:2017-02-08 10:01:25

标签: dc.js crossfilter

这是关于mac地址的数据。每分钟记录一次。对于每一分钟,我都有许多独特的Mac地址。

mac_add,created_time
18:59:36:12:23:33,2016-12-07 00:00:00.000
1c:e1:92:34:d7:46,2016-12-07 00:00:00.000
2c:f0:ee:86:bd:51,2016-12-07 00:00:00.000
5c:cf:7f:d3:2e:ce,2016-12-07 00:00:00.000
...
18:59:36:12:23:33,2016-12-07 00:01:00.000
1c:cd:e5:1e:99:78,2016-12-07 00:01:00.000
1c:e1:92:34:d7:46,2016-12-07 00:01:00.000
5c:cf:7f:22:01:df,2016-12-07 00:01:00.000
5c:cf:7f:d3:2e:ce,2016-12-07 00:01:00.000
...

我想使用dc.js和crossfilter创建2个条形图。请参考图表的图片。

2 bar chart image

第一个条形图很容易创建。这是可以推广的。我创建了“created_time”维度,并通过“mac_add”创建了一个group和reduceCount,如下所示:

var moveTime = ndx.dimension(function (d) {
                    return d.dd; //# this is the created_time
                });
var timeGroup = moveTime.group().reduceCount(function (d) {
                    return d.mac_add;
                });
var visitorChart = dc.barChart('#visitor-no-bar');
visitorChart.width(990) 
                .height(350)
                .margins({ top: 0, right: 50, bottom: 20, left: 40 })
                .dimension(moveTime)
                .group(timeGroup)
                .centerBar(true)
                .gap(1)
                .elasticY(true)
                .x(d3.time.scale().domain([new Date(2016, 11, 7), new Date(2016, 11, 13)]))
                .round(d3.time.minute.round)
                .xUnits(d3.time.minute);

visitorChart.render();

问题出在第二个条形图上。这个想法是,一行数据等于1分钟,所以我可以聚合并总和每个mac地址的所有分钟,以获得每个mac地址的时间长度,通过“mac_add”创建另一个维度,并在“mac_add”上执行reduceCount “得到时间长度。然后目标是将时间长度分组30分钟。因此我们可以获得有多少时间长度为30分钟或更少的mac地址,有多少mac_add,时间长度在30分钟到1小时之间,有多少mac_add有时间长度在1小时到1.5小时之间等等...

如果我错了,请纠正我。从逻辑上讲,我认为第二个条形图的尺寸应该是时间长度组(例如< 30,< 1hr,< 1.5hr等)。但是时间长度组本身并没有修复。这取决于第一张图表的画笔选择。也许它只包含30分钟,也许它只包含1.5小时,也许它包含1.5小时和2小时等等......

所以我真的很困惑将哪些参数放入第二个条形图。以及获取所需参数的方法(如何对分组数据进行分组)。请帮我解释一下解决方案。

此致 马文

1 个答案:

答案 0 :(得分:0)

我认为我们过去称之为“双重分组”,但我找不到以前的问题。

设置组

我从一个常规的crossfilter组开始,为mac地址,然后生成一个虚假组,按分钟数聚合。

var minutesPerMacDim = ndx.dimension(function(d) { return d.mac_add; }),
    minutesPerMapGroup = minutesPerMacDim.group();

function bin_keys_by_value(group, bin_value) {
    var _bins;
    return {
        all: function() {
            var bins = {};
            group.all().forEach(function(kv) {
                var valk = bin_value(kv.value);
                bins[valk] = bins[valk] || [];
                bins[valk].push(kv.key);
            });
            _bins = bins;
            // note: Object.keys returning numerical order here might not
            // work everywhere, but I couldn't find a browser where it didn't
            return Object.keys(bins).map(function(bin) {
                return {key: bin, value: bins[bin].length};
            })
        },
        bins: function() {
            return _bins;
        }
    };
}

function bin_30_mins = function(v) {
    return 30 * Math.ceil(v/30);
}

var macsPerMinuteCount = bin_keys_by_value(minutesPerMacGroup);

这将保留每个时间段的mac地址,我们稍后需要进行过滤。将非标准方法bins添加到假组中的情况并不常见,但考虑到过滤界面只允许我们访问密钥,我无法想到保留该信息的有效方法。

由于该函数采用了分箱功能,如果我们想要更复杂的分箱,我们甚至可以使用threshold scale,而不是仅仅舍入到最接近的30分钟。 quantize scale是进行上述舍入的更通用方法。

设置图表

使用此数据来驱动图表很简单:我们可以像往常一样使用维度和假组。

chart
    .dimension(minutesPerMacDim)
    .group(macsPerMinuteCount)

设置图表以便可以过滤它有点复杂:

chart.filterHandler(function(dimension, filters) {
    if(filters.length === 0)
        dimension.filter(null);
    else {
        var bins = chart.group().bins(); // retrieve cached bins
        var macs = filters.map(function(key) { return bins[key]; })
        macs = Array.prototype.concat.apply([], macs);
        var macset = d3.set(macs);
        dimension.filterFunction(function(key) {
            return macset.has(key);
        })
    }
})

回想一下,我们正在使用一个键入mac地址的维度;这很好,因为我们想要过滤mac地址。但是图表正在接收其密钥的分钟计数,filters将包含这些密钥,例如306090等。所以我们需要提供一个filterHandler,它采用分钟计数键并根据这些键过滤维度。

注意1:这都是未经测试的,所以如果它不起作用,请将一个例子作为小提琴或bl.ock发布 - 有小提琴和块可以分叉开始on the main page

注2:严格来说,这不是衡量连接的长度:它是计算连接的总分钟数。不确定这对你来说是否重要。如果用户断开连接然后在时间范围内重新连接,则两个会话将计为一个。我认为你必须预处理以获得持续时间。

编辑:基于你的小提琴(谢谢!),上面的代码确实有效。这只是设置x比例和xUnits正确的问题。

  chart2
      .x(d3.scale.linear().domain([60,1440]))
      .xUnits(function(start, end) {
          return (end-start)/30;
      })

线性刻度在这里会很好 - 我不会尝试量化那个刻度,因为已经设置了30分钟的刻度。我们确实需要设置xUnits,以便dc.js知道制作条形图的宽度。

我不确定为什么elasticX在这里不起作用,但<30 bin完全使其他所有内容相形见绌,所以我认为最好不要这样做。

你小提琴的叉子:https://jsfiddle.net/gordonwoodhull/2a8ow1ay/2/