crossfilter "double grouping" 其中key是另一个reduction的值

crossfilter "double grouping" where key is the value of another reduction

这是我关于 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 和交叉过滤器创建 2 个条形图。图表请参考图片

第一个条形图很容易创建。它是可刷的。我创建了“created_time”维度,并通过“mac_add”创建了一个组和 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 分钟分组。所以我们可以得到有多少 mac 时间长度不超过 30 分钟的地址,有多少 mac_add 时间长度在 30 分钟到 1 小时之间,有多少 mac_add 有时间长度在 1 小时到 1.5 小时之间,等等...

如有错误请指正。从逻辑上讲,我在想第二个条形图的维度应该是时间长度的组(比如<30、<1hr、<1.5hr等)。但是时间长度组本身是不固定的。这取决于第一个图表的画笔选择。也许只有30分钟,也许只有1.5小时,也许有1.5小时和2小时等等...

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

此致, 马文

我想我们过去曾将其称为 "double grouping",但我找不到之前的问题。

设置群组

我将从 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 地址,我们稍后需要使用这些地址进行过滤。将 non-standard 方法 bins 添加到假组并不常见,但我想不出一种有效的方法来保留该信息,因为过滤接口只会让我们访问密钥。

由于该函数采用分箱函数,我们甚至可以使用 threshold scale if we wanted more complicated bins than just rounding up to the nearest 30 minutes. A 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 个地址。但是图表正在接收 minute-counts 作为其键,filters 将包含这些键,如 306090 等。所以我们需要提供一个 filterHandler,它接受 minute-count 个键并根据这些键过滤维度。

注意 1:这都是未经测试的,所以如果它不起作用,请 post 作为 fiddle 或 bl.ock 的示例 - 有 fiddle s 和块你可以 fork 开始 on the main page.

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

编辑:根据您的 fiddle(谢谢!),上面的代码似乎确实有效。这只是正确设置 x 比例和 xUnits 的问题。

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

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

我不确定为什么 elasticX 在这里不起作用,但是 <30 垃圾箱让其他所有东西都相形见绌,所以我认为最好不要考虑它。

你的 fiddle 的分支:https://jsfiddle.net/gordonwoodhull/2a8ow1ay/2/