【问题标题】:crossfilter "double grouping" where key is the value of another reduction交叉过滤器“双重分组”,其中键是另一个减少的值
【发布时间】:2017-02-08 10:01:24
【问题描述】:

这是我关于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 个条形图。图表请参考图片。

第一个条形图很容易创建。它是可刷的。我创建了“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_add”创建另一个维度并在“mac_add”上执行 reduceCount " 来获取时间长度。然后目标是将时间长度按 30 分钟分组。所以我们可以得到有多少时间长度为30分钟或更短的mac地址,有多少时间长度在30分钟到1小时之间的mac_add,有多少时间长度在1小时到1.5小时之间的mac_add等等......

如果我错了,请纠正我。从逻辑上讲,我认为第二个条形图的维度应该是时间长度的组(例如

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

问候, 马文

【问题讨论】:

    标签: dc.js crossfilter


    【解决方案1】:

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

    设置组

    我会从 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);
    

    这将保留每个时间 bin 的 mac 地址,我们稍后将需要这些地址进行过滤。将非标准方法bins 添加到假组中并不常见,但我想不出一种有效的方法来保留该信息,因为过滤接口只会让我们访问密钥。

    由于该函数采用分箱功能,如果我们想要更复杂的分箱而不是四舍五入到最接近的 30 分钟,我们甚至可以使用threshold scalequantize 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:这一切都未经测试,所以如果它不起作用,请发布一个 fiddle 或 bl.ock 的示例 - 您可以使用 fiddles 和 blocks 来开始 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 垃圾箱让其他一切都相形见绌,所以我认为最好把它排除在外。

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

    【讨论】:

    • 非常感谢您的回复。我遵循了你的代码。但是,我不熟悉分箱,并将 bin 结果用于图表。我在这里创建了一个小提琴:jsfiddle.net/yugdablos/vpL1b03n/1 如您所见,第二个图表是空的。你能告诉我我的代码有什么问题吗?
    • 无论如何,“它正在计算连接的总分钟数。” => 是的,这就是我的目标。一行数据被认为是mac地址的分钟连接。因为我无法得到连接的开始和结束。它们太随意了。
    • 哇!这是很多数据。看起来减少工作正常,但.x() 比例和.xUnits() 存在问题。抱歉,如果我上面的评论让您感到困惑 - 我不建议使用 d3.scale.quantize 作为 x 比例,但您可以使用它来代替 bin_30_mins。一旦我可以在这个慢速连接上分叉它,我会发布一个更正的小提琴。
    • 太棒了。它完全显示了我的预期。将其标记为答案。但是,我有一个快速的问题。我想知道这个分箱过程是否需要更多时间来处理?因为我得到了很多使图表运行缓慢的数据。你之前看到的数据只有我整体数据的1/8。您认为运行缓慢仅仅是因为我的大数据,还是因为分箱过程而运行缓慢?
    • 显然 jsfiddle 在处理这么多数据时犹豫不决。但是如果你看到你的图表运行缓慢 IRL,我猜这可能是顶部图表中的 bin 数量 = DOM 元素的数量。 1 天 = 24*60 条对于浏览器来说是可以绘制的,但已经超过了像素数,所以你并没有真正看到它。 Take a look at this example 有关如何按小时分类等的一些提示。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2015-02-05
    • 1970-01-01
    • 1970-01-01
    • 2012-06-19
    相关资源
    最近更新 更多