javascript - 使用两组的 dc.js 箱形图缩减器

标签 javascript d3.js dc.js

我正在尝试生成一个箱形图,该图将显示单个设备已连接到按其 vendor 分组的网络总数。

数据格式:

{
    "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"
}

摆弄数据 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 个网络......等等.. The Goal

隐藏组的尝试

Gordon在评论中提到crossfilter中不能递归传递两个组。我现在正在尝试生成一个隐藏组,该组可以使用来自 DC git 的以下代码来累积每个 mac 地址的网络,但是我无法将其与 boxplot reducer 啮合。我在这里的方向是否正确?

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)
;

谢谢亚当

最佳答案

理想情况下,我们真的很想在这里对 vendor 使用一个简单的维度,以防我们想在箱线图上使用画笔进行过滤。

那么问题就变成了:我们如何减少两次,一次是获取每个 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;
  }
);

我最近描述了这种模式 in this answer , 所以我不会在这里详细介绍。

这里是示例输出:bins 是 vendor ,每个值都是一个将 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
    ]
  },
  ...

您的示例数据每个 vendor 只有一个 MAC 地址,因此我添加了一些虚假数据,并得到了一个看起来不错的箱线图:

it's a boxplot

Fork of your fiddle .

#MACs 只取前十名

作为在框太多时如何 trim 数据的示例,下面是您将如何按 MAC 地址数量排序,并且只选取 10 个“最受欢迎”的 vendor :

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));

这是我的想法,未经测试,所以如果有任何问题,请编辑/评论。

关于javascript - 使用两组的 dc.js 箱形图缩减器,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/51639127/

相关文章:

javascript - 从 jQuery 内的动态变量加载数据

javascript - Regex(正则表达式),替换javascript中出现的第二个

dc.js - 添加下拉菜单以选择日期范围

javascript - 如何在过滤图表后获取当前组大小

javascript - HTML5 FileReader 输出到 D3.js 图表

javascript - D3.js 获取面积图最近的 X 和 Y 坐标

javascript - 如何在 vuejs 中用新数据刷新数据表?

javascript - 为什么节点列表包含未反射(reflect)在其长度属性中的额外未定义项?

d3.js - 带有 ES6 模块的 d3 v4.0 自定义构建

javascript - D3 : Graph doesn't display the x and y axis