dc.js 使用两组的箱形图缩减器
dc.js Box plot reducer using two groups
我正在尝试制作一个箱线图,它将显示单个设备连接到的网络总数(按供应商分组)。
数据格式:
{
"SSID": "eduroam",
"identifier": "Client",
"latitude": 52.4505,
"longitude": -1.9361,
"mac": "dc:d9:16:##:##:##",
"packet": "PR-REQ",
"timestamp": "2018-07-10 12:25:26",
"vendor": "Huawei Technologies Co.Ltd"
}
Fiddle有数据
https://jsfiddle.net/v4a8g2bo/
我已经设法使用以下代码获得单个设备连接到的网络的总和。之前过滤的数据仅包含指向 mac 地址的唯一网络,因此使用计数器可以对网络进行计数。
var mac = ndx.dimension(function (d) { return d["mac"]; });
var SSIDstoSingleMAC = mac.group().reduceSum(function (d) { return
+d.counter});
我的问题在于尝试将这个分组总和传递到另一个组,该组将输出一个数组以用于箱线图
var vendor = ndx.dimension(function (d) { return d["vendor"]; });
//Used to count number of networks per device
var mac = ndx.dimension(function (d) { return d["mac"]; });
var SSIDstoSingleMAC = mac.group().reduceSum(function (d) { return
+d.counter});
//This is where things fall down
var boxplotGroup = SSIDstoSingleMAC.group().reduce(
function (p, v) {
let dv = v.counter;
if (dv != null) p.push(dv);
return p;
},
function (p, v) {
let dv = v.counter;
if (dv != null) p.splice(p.indexOf(dv), 1);
return p;
},
function () {
return [];
}
);
var boxPlot = dc.boxPlot("#boxPlot");
boxPlot
.width(1200)
.height(600)
.dimension(vendor)
.group(boxplotGroup)
.tickFormat(d3.format('.1f'))
.elasticY(true)
.elasticX(true)
;
这是目标:
前任。 Apple [7,5,10,2] = 四台苹果设备.. 一台设备已连接到 7 个网络......等等..
尝试隐藏组
Gordon在评论中提到crossfilter中不能递归传递两个组。我现在正在尝试生成一个隐藏组,该组可以使用来自 DC git 的以下代码来累积每个 mac 地址的网络,但是我无法将其与箱线图减速器啮合。我走的方向对吗?
https://github.com/dc-js/dc.js/wiki/FAQ#accumulate-values
var allDim = ndx.dimension(function (d) { return d; });
function accumulate_group(source_group) {
return {
all:function () {
var cumulate = 0;
return source_group.all().map(function(d) {
cumulate += d.counter;
return {key:d.mac, value:cumulate};
});
}
};
}
var boxPlotDim = accumulate_group(allDim);
var boxPlotGroup = boxPlotDim.group().reduce(
function(p,v) {
p.push(v.value());
return p;
},
function(p,v) {
p.splice(p.indexOf(v.value()), 1);
return p;
},
function() {
return [];
}
);
var boxPlot = dc.boxPlot("#boxPlot");
boxPlot
.width(1200)
.height(600)
.dimension(vendor)
.group(boxPlotGroup)
.tickFormat(d3.format('.1f'))
.elasticY(true)
.elasticX(true)
;
谢谢亚当
理想情况下,我们真的很想在这里对供应商使用一个简单的维度,以防我们想在箱线图上使用画笔进行过滤。
那么问题就变成了:我们如何减少两次,一次是为了获取每个 MAC 地址的计数,然后再次将这些计数转换成一个数组。
第一部分有一个标准答案:只是减少到一个对象而不是一个值:
var vendorMacCountsGroup = vendor.group().reduce(
function(p, v) { // add
p[v.mac] = (p[v.mac] || 0) + v.counter;
return p;
},
function(p, v) { // remove
p[v.mac] -= v.counter;
return p;
},
function() { // init
return {}; // macs;
}
);
我最近描述过这个模式,这里就不赘述了。
这是示例输出:箱子是供应商,每个值都是一个对象映射 mac 地址到计数:
[
{
"key": "Asustek Computer Inc.",
"value": {
"1c:b7:2c:48": 8,
"1c:b7:be:ef": 3
}
},
{
"key": "Huawei Technologies Co.Ltd",
"value": {
"dc:d9:16:3d": 14,
"dc:da:16:3d": 2,
"dc:d9:16:3a": 1,
"dc:d9:16:3b": 1
}
},
...
接下来,我们真的只需要计数而忘记 MAC 地址。 JavaScript 有一个很好的内置函数,Object.values。我们只需要将其应用于我们组中的每个对象值。我们还将丢弃任何零,因为只有当 MAC 地址在其他地方被过滤掉时才会发生这种情况。
function flatten_object_group(group) {
return {
all: function() {
return group.all().map(function(kv) {
return {
key: kv.key,
value: Object.values(kv.value).filter(function(v) { return v>0; })
};
});
}
};
}
var boxPlotGroup = flatten_object_group(vendorMacCountsGroup);
示例输出:
[
{
"key": "Asustek Computer Inc.",
"value": [
8,
3
]
},
{
"key": "Huawei Technologies Co.Ltd",
"value": [
14,
2,
1,
1
]
},
...
您的示例数据每个供应商只有一个 MAC 地址,因此我添加了一些虚假数据,并得到了一个看起来不错的箱线图:
在#MACs
之前只取前十名
作为一个例子,如果有太多的框,你可能如何 trim 数据,这里是你如何按 MAC 地址的数量排序,并且只取 10 "most popular" 供应商:
function top_ten_by_length(group) {
return {
all: function() {
return group.all().sort(function(a,b) {
return b.value.length - a.value.length;
}).slice(0, 10);
}
};
}
这样写:
var boxPlotGroup = top_ten_by_length(flatten_object_group(vendorMacCountsGroup));
这是我的想法,未经测试,所以请 edit/comment 如果有任何问题。
我正在尝试制作一个箱线图,它将显示单个设备连接到的网络总数(按供应商分组)。
数据格式:
{
"SSID": "eduroam",
"identifier": "Client",
"latitude": 52.4505,
"longitude": -1.9361,
"mac": "dc:d9:16:##:##:##",
"packet": "PR-REQ",
"timestamp": "2018-07-10 12:25:26",
"vendor": "Huawei Technologies Co.Ltd"
}
Fiddle有数据 https://jsfiddle.net/v4a8g2bo/
我已经设法使用以下代码获得单个设备连接到的网络的总和。之前过滤的数据仅包含指向 mac 地址的唯一网络,因此使用计数器可以对网络进行计数。
var mac = ndx.dimension(function (d) { return d["mac"]; });
var SSIDstoSingleMAC = mac.group().reduceSum(function (d) { return
+d.counter});
我的问题在于尝试将这个分组总和传递到另一个组,该组将输出一个数组以用于箱线图
var vendor = ndx.dimension(function (d) { return d["vendor"]; });
//Used to count number of networks per device
var mac = ndx.dimension(function (d) { return d["mac"]; });
var SSIDstoSingleMAC = mac.group().reduceSum(function (d) { return
+d.counter});
//This is where things fall down
var boxplotGroup = SSIDstoSingleMAC.group().reduce(
function (p, v) {
let dv = v.counter;
if (dv != null) p.push(dv);
return p;
},
function (p, v) {
let dv = v.counter;
if (dv != null) p.splice(p.indexOf(dv), 1);
return p;
},
function () {
return [];
}
);
var boxPlot = dc.boxPlot("#boxPlot");
boxPlot
.width(1200)
.height(600)
.dimension(vendor)
.group(boxplotGroup)
.tickFormat(d3.format('.1f'))
.elasticY(true)
.elasticX(true)
;
这是目标:
前任。 Apple [7,5,10,2] = 四台苹果设备.. 一台设备已连接到 7 个网络......等等..
尝试隐藏组
Gordon在评论中提到crossfilter中不能递归传递两个组。我现在正在尝试生成一个隐藏组,该组可以使用来自 DC git 的以下代码来累积每个 mac 地址的网络,但是我无法将其与箱线图减速器啮合。我走的方向对吗?
https://github.com/dc-js/dc.js/wiki/FAQ#accumulate-values
var allDim = ndx.dimension(function (d) { return d; });
function accumulate_group(source_group) {
return {
all:function () {
var cumulate = 0;
return source_group.all().map(function(d) {
cumulate += d.counter;
return {key:d.mac, value:cumulate};
});
}
};
}
var boxPlotDim = accumulate_group(allDim);
var boxPlotGroup = boxPlotDim.group().reduce(
function(p,v) {
p.push(v.value());
return p;
},
function(p,v) {
p.splice(p.indexOf(v.value()), 1);
return p;
},
function() {
return [];
}
);
var boxPlot = dc.boxPlot("#boxPlot");
boxPlot
.width(1200)
.height(600)
.dimension(vendor)
.group(boxPlotGroup)
.tickFormat(d3.format('.1f'))
.elasticY(true)
.elasticX(true)
;
谢谢亚当
理想情况下,我们真的很想在这里对供应商使用一个简单的维度,以防我们想在箱线图上使用画笔进行过滤。
那么问题就变成了:我们如何减少两次,一次是为了获取每个 MAC 地址的计数,然后再次将这些计数转换成一个数组。
第一部分有一个标准答案:只是减少到一个对象而不是一个值:
var vendorMacCountsGroup = vendor.group().reduce(
function(p, v) { // add
p[v.mac] = (p[v.mac] || 0) + v.counter;
return p;
},
function(p, v) { // remove
p[v.mac] -= v.counter;
return p;
},
function() { // init
return {}; // macs;
}
);
我最近描述过这个模式
这是示例输出:箱子是供应商,每个值都是一个对象映射 mac 地址到计数:
[
{
"key": "Asustek Computer Inc.",
"value": {
"1c:b7:2c:48": 8,
"1c:b7:be:ef": 3
}
},
{
"key": "Huawei Technologies Co.Ltd",
"value": {
"dc:d9:16:3d": 14,
"dc:da:16:3d": 2,
"dc:d9:16:3a": 1,
"dc:d9:16:3b": 1
}
},
...
接下来,我们真的只需要计数而忘记 MAC 地址。 JavaScript 有一个很好的内置函数,Object.values。我们只需要将其应用于我们组中的每个对象值。我们还将丢弃任何零,因为只有当 MAC 地址在其他地方被过滤掉时才会发生这种情况。
function flatten_object_group(group) {
return {
all: function() {
return group.all().map(function(kv) {
return {
key: kv.key,
value: Object.values(kv.value).filter(function(v) { return v>0; })
};
});
}
};
}
var boxPlotGroup = flatten_object_group(vendorMacCountsGroup);
示例输出:
[
{
"key": "Asustek Computer Inc.",
"value": [
8,
3
]
},
{
"key": "Huawei Technologies Co.Ltd",
"value": [
14,
2,
1,
1
]
},
...
您的示例数据每个供应商只有一个 MAC 地址,因此我添加了一些虚假数据,并得到了一个看起来不错的箱线图:
在#MACs
之前只取前十名作为一个例子,如果有太多的框,你可能如何 trim 数据,这里是你如何按 MAC 地址的数量排序,并且只取 10 "most popular" 供应商:
function top_ten_by_length(group) {
return {
all: function() {
return group.all().sort(function(a,b) {
return b.value.length - a.value.length;
}).slice(0, 10);
}
};
}
这样写:
var boxPlotGroup = top_ten_by_length(flatten_object_group(vendorMacCountsGroup));
这是我的想法,未经测试,所以请 edit/comment 如果有任何问题。