【问题标题】:Reduce number of datapoints using crossfilter使用交叉过滤器减少数据点的数量
【发布时间】:2019-04-17 16:26:02
【问题描述】:

假设我有 100 年的月度数据,共有 1200 个数据点,见底部。

要绘制一个很小的概览折线图(例如只有 100 个数据点),我必须通过分组手动完成。例如,按年份对数据进行分组,然后取 12 个月的平均值,遍历每组,最后将数据点减少到 100 个。

除了这种方法,有没有使用 crossfilter 或任何其他库的便捷方法?

[
    { date: 1900-01, value: 72000000000},
    { date: 1900-02, value: 58000000000},
    { date: 1900-03, value:  2900000000},
    { date: 1900-04, value: 31000000000},
    { date: 1900-05, value: 33000000000},
    ...
    { date: 1999-11, value: 30000000000},
    { date: 1999-12, value: 10000000000},
]

【问题讨论】:

  • 这个问题已经够具体了。近距离投票是不恰当的(并且对新用户很粗鲁)。

标签: d3.js dc.js crossfilter


【解决方案1】:

无论您使用哪个库,它都将是相同的算法,只是指定它的方式不同。在这种情况下,d3.nest 可能是最简单的方法,但如果你想快速过滤,crossfilter 方法也不错。

使用 d3.nest 和 crossfilter 的区别在于我们不是在构造一个值数组,而只是一个值。所以我们将同时维护 sum 和 count。

我们还需要指定从 bin 中删除一行时会发生什么。

var parse = d3.timeParse("%Y-%m");
data.forEach(function(d) {
  // it's best to convert fields before passing to crossfilter
  // because crossfilter will look at them many times
  d.date = parse(d.key);
});
var cf = crossfilter(data);
var yearDim = cf.dimension(d => d3.timeYear(d.date));
var yearAvgGroup = yearDim.group().reduce(
  function(p, v) { // add
    p.sum += v.value;
    ++p.count;
    p.avg = p.sum/p.count;
    return p;
  },
  function(p, v) { // remove
    p.sum -= v.value;
    --p.count;
    p.avg = p.count ? p.sum/p.count : 0;
    return p;
  },
  function() { // init
    return {sum: 0, count: 0, avg: 0};
  }
);

现在yearAvgGroup.all() 将返回一个键/值对数组,其中键是年份,值包含sumcountavg

Crossfilter 并没有让这个问题特别方便解决,但是reductio 有一个辅助函数来解决这个问题:

var yearAvgGroup = yearDim.group();
reductio().avg(d => d.value);

注意:除非您拥有大量数据,否则这无关紧要,但仅计算组中的总和和计数并在需要时计算平均值会更有效。

如果您使用的是 dc.js,您可以为此使用 valueAccessor

// remove avg lines from the above, and
chart.dimension(yearDim)
  .group(yearAvgGroup)
  .valueAccessor(kv => kv.value.sum / kv.value.count);

【讨论】:

    【解决方案2】:

    假设您的问题仅与生成数据有关,您可以使用 d3-nest(不使用交叉过滤器)来平均每年:

    解析日期值,然后您可以将日期格式化为年份以创建密钥。这会按键对值进行分组,然后我们使用函数汇总这些值以计算给定年份的平均值:

    var parse = d3.timeParse("%Y-%m");  // takes: "1900-01"
    var format = d3.timeFormat("%Y");   // gives: "1900"
    
    var means = d3.nest()
      .key(function(d) { return format(parse(d.date)); })
      .rollup(function(values) { return d3.mean(values, function(d) {return d.value; }) })
      .entries(data);
    

    这给了我们以下结构:

    [
      {
        "key": "1900",
        "value": 39380000000
      },
      {
        "key": "1999",
        "value": 20000000000
      }
    ]
    

    var data = [
        { date: "1900-01", value: 72000000000},
        { date: "1900-02", value: 58000000000},
        { date: "1900-03", value:  2900000000},
        { date: "1900-04", value: 31000000000},
        { date: "1900-05", value: 33000000000},
        { date: "1999-11", value: 30000000000},
        { date: "1999-12", value: 10000000000},
    ];
    
    var parse = d3.timeParse("%Y-%m");
    var format = d3.timeFormat("%Y");
    
    var means = d3.nest()
        .key(function(d) { return format(parse(d.date)); })
        .rollup(function(values) { return d3.mean(values, function(d) {return d.value; }) })
        .entries(data);
        
    console.log(means);
    <script src="https://cdnjs.cloudflare.com/ajax/libs/d3/5.7.0/d3.min.js"></script>

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2015-02-05
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2023-03-07
      相关资源
      最近更新 更多