dc.js: 如何交叉过滤复杂对象?

dc.js: How to crossfilter complex object?

我有一个复杂的 JSON 对象,想要交叉过滤数据(键应该是 x 轴,值应该是 y 轴),我该如何处理这个对象?

signalData: {
    signal1: {
      name: "",
      data: {2,3,1,4,5,1,3},
    },
    signal2: {
      name: "",
      data: {2,3,1,4,5,1,3},
    },
    signal3: {
      name: "",
      data: {2,3,1,4,5,1,3},
    },

我认为主要是交叉过滤数据键,因为我想在 3 个不同的图表中使用这些数据。

这里有一张图可能更好地描述了我想做的事情 ;)

这是一些代码:

var data = {
   "1": 10,
   "2": 20,
   "3": 30,
   "4": 40,
   "5": 50,
   "6": 60
 };
var dataModel = Object.keys(data).map(function(d) {
  return {
     key: +d,
     value: data[d]
   };
});
signalCF = crossfilter(dataModel);
signalDim = signalCF.dimension(dc.pluck("key"));
signalGroup = signalDim.group().reduceSum(function(d) {
  return d.value; });

我不确定我们是否使用相同的“过滤器”定义;我的意思是问你是否打算使用crossfilter的过滤功能,即在一个维度上刷并仅在无关视图中看到所选数据

range/focus 功能不需要过滤,因为所有图表都在同一维度上,没有数据被过滤。

假设您确实要在此数据维度上进行过滤,则需要展平数据。

如果你的原始数据看起来像

signalData: {
    signal1: {
      name: "",
      data: {2,3,1,4,5,1,3},
    },
    signal2: {
      name: "",
      data: {2,3,1,4,5,1,3},
    },
    signal3: {
      name: "",
      data: {2,3,1,4,5,1,3},
    },

您的扁平化数据可能看起来像

[
  {
    signal1: 2,
    signal2: 2,
    signal3: 2
  },
  {
    signal1: 3,
    signal2: 3,
    signal3: 3
  },
  {
    signal1: 1,
    signal2: 1,
    signal3: 1
  },
  {
    signal1: 4,
    signal2: 4,
    signal3: 4
  },
  // ...

这是我想表达的唯一一点,其余的应该是显而易见的...

我对在数组上使用 Object.keys 持谨慎态度。它可能有效,但数据可能会乱序。 d3.range 会做同样的事情。

将密钥添加到上述数据中:

var dataModel = d3.range(0, data.length).map(function(d) {
  return {
     key: +d,
     ...data[d]
   };
});

现在数据看起来像

[
  {
    key: 0,
    signal1: 2,
    signal2: 2,
    signal3: 2
  },
  // ...

您可以像这样定义维度和组:

signalCF = crossfilter(dataModel);
signalDim = signalCF.dimension(dc.pluck("key"));
signalGroup1 = signalDim.group().reduceSum(function(d) {
  return d.signal1; });
signalGroup2 = signalDim.group().reduceSum(function(d) {
  return d.signal2; });

显然,您也可以编写代码将数据结构扁平化为此类数据。我想表达的唯一一点是 crossfilter 的本机格式是扁平化数组;可以压缩其他格式的数据,但过滤只有在数据是数组时才有效。

这里是一些进行转换的代码。将 column-major 数据旋转为 row-major 数据是一项常见任务,我通常使用 d3.range and Array.reduce,尽管还有其他方法。

function rotate_data(odata) {
  const keys = Object.keys(odata),
    len = d3.max(keys, key => odata[key].data.length); // 1
  if(keys.some(key => odata[key].data.length < len))
    console.warn('warning: data not same length; padding with zeros'); // 2
  return d3.range(0, len).map(i => keys.reduce(
    (p, k) => (p[k] = odata[k].data[i] || 0, p), {key: i}); // 3
}

rotate_data 计算对象 (1) 的所有值的 maximum-length data 字段。如果这些数组中的任何一个不是该长度 (2),它就会发出警告。最后 (3) 它将整数 [1, length) 映射到一个新的对象数组

  • 整数i的字段key
  • 原始对象的每个键的字段,包含相应数组的第 i个值

我测试了函数here。请注意,您的示例数据中存在拼写错误 - 数组应括在方括号中而不是花括号中。