【问题标题】:d3.js moving average with previous and next data valuesd3.js 具有上一个和下一个数据值的移动平均值
【发布时间】:2013-11-12 21:41:35
【问题描述】:

我是 D3 的新手,我试图对我的数据进行上一个和下一个值的移动平均,以使其平滑。

目前,我使用前 2 个值 + 当前值使其工作。它确实有效,但是 1)我将如何使用下一个值,以及 2)如果我想使用 15 个前一个值和 15 个下一个值怎么办? (如果有 30 个单独的 var 来存储它们,那就太疯狂了)

我习惯了传统的 javascript,但不知道如何在 D3 中以这种方式遍历数据。 希望有人能赐教,谢谢。

在 bl.ocks.org 上查看完整代码: http://bl.ocks.org/jcnesci/7439277

或者只是这里的数据解析代码:

d3.json("by_date_mod.json", function(error, data) {

// Setup each row of data by formatting the Date for X, and by converting to a number for Y.
data = data.rows;
data.forEach(function(d) {
  d.key = parseDate(String(d.key));
  d.value = +d.value;
});

x.domain(d3.extent(data, function(d) { return d.key; }));
y.domain([0, d3.max(data, function(d) { return d.value; })]);

// Setup the moving average calculation.
// Currently is a hacky way of doing it by manually storing and using the previous 3 values for averaging.
// Looking for another way to address previous values so we can make the averaging window much larger (like 15 previous values).
var prevPrevVal = 0;
var prevVal = 0;
var curVal = 0
var movingAverageLine = d3.svg.line()
.x(function(d,i) { return x(d.key); })
.y(function(d,i) {
    if (i == 0) {
        prevPrevVal  = y(d.value);
        prevVal = y(d.value);
        curVal =  y(d.value);
    } else if (i == 1) {
        prevPrevVal = prevVal;
        prevVal = curVal;
        curVal = (prevVal + y(d.value)) / 2.0;
    } else {
        prevPrevVal = prevVal;
        prevVal = curVal;
        curVal = (prevPrevVal + prevVal + y(d.value)) / 3.0;
    }
    return curVal;
})
.interpolate("basis");

// Draw the moving average version of the data, as a line.
graph1.append("path")
  .attr("class", "average")
  .attr("d", movingAverageLine(data));

// Draw the raw data as an area.
graph1.append("path")
    .datum(data)
    .attr("class", "area")
    .attr("d", area);

// Draw the X-axis of the graph.
graph1.append("g")
    .attr("class", "x axis")
    .attr("transform", "translate(0," + height + ")")
    .call(xAxis);

// Draw the Y-axis of the graph.
graph1.append("g")
    .attr("class", "y axis")
    .call(yAxis)
  .append("text")
    .attr("transform", "rotate(-90)")
    .attr("y", 6)
    .attr("dy", ".71em")
    .style("text-anchor", "end")
    .text("Value");
});

【问题讨论】:

  • 我会分别计算移动平均线的值,然后将该数据传递给 D3。
  • @LarsKotthoff 如果是这样,那当然。我只是想排除 D3 是否有遍历方法首先执行此操作。谢谢,

标签: javascript math d3.js


【解决方案1】:

你需要一个函数来计算移动平均线:

var movingWindowAvg = function (arr, step) {  // Window size = 2 * step + 1
    return arr.map(function (_, idx) { 
        var wnd = arr.slice(idx - step, idx + step + 1); 
        var result = d3.sum(wnd) / wnd.length;

        // Check for isNaN, the javascript way
        result = (result == result) ? result : _;

        return result; 
    });
};

var avgData = movingWindowAvg(avg, 7); // 15 step moving window.

请注意,当无法提取完整窗口时,此函数会fudges原始数组边界处的值。

更新:如果结果为NaN,则将结果转换为开头的当前数字。检查result == result is the recommended way of testing for NaNs in Javascript

【讨论】:

  • 很好的答案!这很好用。关于数组边缘的“捏造”,我注意到的一件事是,在开始时产生的值将是 NaN(因为如果 n=0,它在 n-3 处找不到项目)但在end 实际上是有效数字(即使 n=array length 时找不到 n+3)。我不确定为什么两个边缘的反应不同,但对于第一种情况,我添加了这个,如果结果是 NaN,它只使用原始值:var result = d3.sum(wnd) / wnd.length;if (isNaN(result)) { result = _; }
【解决方案2】:

如果您确实不需要可变大小的窗口,则此累积平均值可能是一个更快的选择,无需切片开销:

    function cumAvg(objects, accessor) {
      return objects.reduce(
          function(avgs, currObj, i) { 
            if (i == 1) {
              return [ accessor(currObj) ];
            } else {
              var lastAvg = avgs[i - 2]; // reduce idxs are 1-based, arrays are 0
              avgs.push( lastAvg + ( (accessor(currObj) - lastAvg) / i) );
              return avgs;
            }
          }
    }

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2018-01-29
    • 2018-06-14
    • 2021-12-26
    • 2023-03-23
    • 2021-04-28
    • 1970-01-01
    • 2021-04-19
    • 2018-06-10
    相关资源
    最近更新 更多