具有 Non-Exclusive 属性的维度图表

Dimensional Charting with Non-Exclusive Attributes

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

在本题的设置中,所有的HTTP事务都有timehostrequestHeadersresponseHeadersnumBytes字段。但是,不同的事务有不同的特定 HTTP 请求和响应 header。在上面的 table 中,0 和 1 分别表示特定交易中特定 header 的不存在和存在。 requestHeadersresponseHeaders 的 sub-columns 代表交易中存在的 header 的并集。不同的 HTTP 交易数据集几乎肯定会生成不同的 sub-columns.

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

{
    "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 的所有切片,我想显示(前导)请求和响应的(上限)条形图 header s 字节。例如。 (见第一行),对于时间 0 和主机 a.com,请求 header 的条形图应显示 barbaz 各有 12。

有两个问题,一个是小问题,一个是大问题。

小问题

这不太适合 dc,因为它是 one-directional。这些条形图应该针对其他切片进行更新,但它们不能用于切片本身。例如,您不应该能够 select bar 和 deselect baz,并按字节查找主机的结果细分,因为这意味着什么:主机在有 bar 但没有 baz 的交易中?交易中的主机有 bar 并且有或没有 baz?太不直观了。

如何使一些 dc 图表具有方向性。是通过一些禁用鼠标输入的技巧吗?

主要问题

host相反,foobar是non-exclusive。每个交易的主机要么是某物,要么是另一个,但交易的 header 可能包括 foobar.

的任意组合

如何为 requestHeaders 定义交叉过滤器尺寸,然后如何使用 dc?即

var ndx = crossfilter(data);
...
var requestHeadersDim = ndx.dimension(function(d) {
    // What should go here? 
});

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

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

的领域

小问题

使用simple css hack,我简单定义了

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

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

主要问题

主要问题是通过扭曲crossfilter概念的意义来解决的,"fooling"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
  },
  ...
];

我们可以定义一个"dummy"维度,忽略数据:

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 去吧。从此时开始它正常工作。

我通常处理您陈述的主要问题的方法是转换我的数据,以便每个 header 都有一个单独的记录(这些重复记录中的所有其他字段都是相同的)。然后我使用自定义组聚合来避免 double-counting。这些自定义聚合有点难以管理,所以我使用 'exception' 函数构建了 Reductionio 来帮助解决这个问题 - github.com/esjewett/reductio