具有非独占属性的维度图表

时间:2015-05-14 06:15:18

标签: dc.js crossfilter

以下是显示HTTP事务的示意图,简化表。我想使用dc为其构建DC分析,但有些列不能很好地映射到crossfilter

Schematic HTTP Transactions

在此问题的设置中,所有HTTP事务都包含字段timehostrequestHeadersresponseHeadersnumBytes。但是,不同的事务具有不同的特定HTTP请求和响应头。在上表中,0和1分别表示特定事务中特定标头的缺失和存在。 requestHeadersresponseHeaders的子列表示事务中存在的标头的联合。不同的HTTP事务数据集几乎肯定会生成不同的子列。

对于这个问题,此图表中的一行用以下代码表示:

{
    "time": 0,
    "host": "a.com",
    "requestHeaders": {"foo": 0, "bar": 1, "baz": 1},
    "responseHeaders": {"shmip": 0, "shmap": 1, "shmoop": 0},
    "numBytes": 12
}

timehostnumBytes都可以很容易地转换为crossfilter,因此可以构建图表来回答诸如所看到的总字节数之类的内容对于主机a.com的2到4之间的交易。例如,

var ndx = crossfilter(data);
...
var hostDim = ndx.dimension(function(d) {
    return d.host;
});
var hostBytes = hostDim.group().reduceSum(function(d) {
    return d.numBytes;
});

问题在于,对于timehost的所有切片,我想按字节显示(前导)请求和响应标头的(上限)条形图。例如。 (请参阅第一行),对于时间0和主机a.com,请求标题条形图应显示barbaz各有12个。

有两个问题,一个是小问题,一个是主要问题。

次要问题

这不适合dc,因为它是单向的。应该为其他切片更新这些条形图,但它们不能用于切片。例如,您应该无法选择bar并取消选择baz,并按字节查找主机的细分结果,因为这意味着什么:具有{{1}的交易中的主机但是没有bar?拥有baz并且有或没有bar的交易中的主机?这太不直观了。

如何使一些baz图表成为一个方向。是通过一些禁用鼠标输入的黑客吗?

重大问题

dc相反,hostfoo是非排他性的。每个交易的主机都是某个或另一个,但交易的标头可能包含barfoo的任意组合。

如何为bar定义交叉滤波器维度,那么,我如何使用requestHeaders?那是

dc

2 个答案:

答案 0 :(得分:1)

通过查看dc的源代码来破解它(有效但非常不优雅)。可能会扭曲crossfilter的含义以达到预期的效果。

最终结果是fiddle。它比问题稍微有限,因为responseHeaders的字段被硬编码为foobarbaz。删除此限制更多是在简单的Javascript域中。

次要问题

使用simple css hack,我只是定义了

.avoid-clicks {
    pointer-events: none;
}

并给了div这个班。不雅但有效。

重大问题

通过扭曲crossfilter概念的含义和“愚弄”dc来解决主要问题。

假设数据如下:

var transactions = [
  {
      "time": 0,
      "host": "a.com",
      "requestHeaders": {"foo": 0, "bar": 1, "baz": 1},
      "responseHeaders": {"shmip": 0, "shmap": 1, "shmoop": 0},
      "numBytes": 12
  },
  {
      "time": 1,
      "host": "b.org",
      "requestHeaders": {"foo": 0, "bar": 1, "baz": 1},
      "responseHeaders": {"shmip": 0, "shmap": 1, "shmoop": 1},
      "numBytes": 3
  },
  ...
];

我们可以定义一个“虚拟”维度,忽略数据:

var transactionsNdx = crossfilter(transactions);

var dummyDim = transactionsNdx
  .dimension(function(d) {
    return 0;
  });

使用此维度,我们可以定义一个计算过滤行总数foobarbaz字节的组:

var requestHeadersGroup = dummyDim
  .group()
  .reduce(
    /* callback for when data is added to the current filter results */
    function (p, v) {
      return {
        "foo": p.foo + v.requestHeaders.foo * v.numBytes,
        "bar": p.bar + v.requestHeaders.bar * v.numBytes,
        "baz": p.baz + v.requestHeaders.baz * v.numBytes,
      }
    },
    /* callback for when data is removed from the current filter results */
    function (p, v) {
      return {
        "foo": p.foo - v.requestHeaders.foo * v.numBytes,
        "bar": p.bar - v.requestHeaders.bar * v.numBytes,
        "baz": p.baz - v.requestHeaders.baz * v.numBytes,
      }
    },
    /* initialize p */
    function () {
      return {
        "foo": 0,
        "bar": 0,
        "baz": 0
      }
    }
  );

请注意,这根本不是一个合适的crossfilter组。它不会将维度映射到它们的值。相反,它将0映射到一个值,该值本身将维度映射到它们的值(丑陋!)。因此,我们需要将此组转换为实际上看起来像crossfilter组的内容:

var getSortedFromGroup = function() {
  var all = requestHeadersGroup.all()[0].value;
  all = [
    {
      "key": "foo",
      "value": all.foo
    },
    {
      "key": "bar",
      "value": all.bar
    },
    {
      "key": "foo",
      "value": all.baz
    }];
  return all.sort(function(lhs, rhs) {
    return lhs.value - rhs.value;
  });
}
var requestHeadersDisplayGroup = {
  "top": function(k) {
      return getSortedFromGroup();
    },
  "all": function() {
      return getSortedFromGroup();
    },
};

我们现在可以创建常规dc图表,并传递适配器组 requestHeadersDisplayGroup到它。它从这一点开始正常工作。

答案 1 :(得分:1)

我通常处理您所说的主要问题的方法是转换我的数据,以便每个标题都有一个单独的记录(这些重复记录中的所有其他字段都是相同的)。然后我使用自定义组聚合来避免重复计算。这些自定义聚合有点难以管理,所以我建立了Reductio来帮助解决这个问题,使用'例外'功能 - github.com/esjewett/reductio