计算与前一个元素和一个新元素的标准差 (σ) (cumulative/incremental SD)

Calculate standard deviation (σ) from the previous one and a new element (cumulative/incremental SD)

正在对 运行 流程进行基准测试,其中计算了以下数字 "cumulatively"(根据以前的值 + 新元素):

Standard Deviation (σ) of the duration is supposed to be calculated as well, because it indicates the statistical dispersion.

基于应用,不方便逐个存储element/number,所以需要结合之前的值和新的元素进行计算

示例:

NewElement  Min  Max  Avg  StdDev           // AllElements (which are NOT meant to be stored)
1           1    1    1    -                   [1]
2           1    2    1.5  0.5                 [1,2]
3           1    3    2    0.8164965809277     [1,2,3]
4           1    4    2.5  1.1180339887499     [1,2,3,4]
0           0    4    2    1.4142135623731     [1,2,3,4,0]

(而this是SD的在线计算器,供参考)

目标的简化版本是:

const calculateNewStats = (stats, newElement) => {
  const newStats = {};
  newStats.count = stats.count + 1;
  newStats.min = Math.min(stats.min, newElement);
  newStats.max = Math.max(stats.max, newElement);
  newStats.avg = (stats.avg * stats.count + newElement) / newStats.count;

  // newStats.sd = ??? that's the problem

  return newStats;
};

// initial values
let stats = {
  count: 0,
  min: 0,
  max: 0,
  avg: 0,
  // initial SD is theoretically controversial (N/A), but that's not the point
  sd: 0,
};

// loopStart goes here ... an infinite one

    // many things goes here ... eventually, we have a `newElement`

    stats = calculateNewStats(stats, newElement);

// loopEnd goes here

搜索了一段时间,找到了一些数学方程式(如this)并仔细应用,但结果数字不正确。

the page you linked 上的算法确实有效,这是一个有效的实现:

const calculateNewStats = (stats, newElement) => {
  const newStats = {};

  newStats.count = stats.count + 1;
  newStats.min = Math.min(stats.min, newElement);
  newStats.max = Math.max(stats.max, newElement);
  newStats.avg = (stats.avg * stats.count + newElement) / newStats.count;

  newStats.sd = Math.sqrt(
    (
      (newStats.count - 1) * stats.sd * stats.sd +
      (newElement - newStats.avg) * (newElement - stats.avg)
    ) / (newStats.count)
  );

  return newStats;
};

// initial values
let stats = {
  count: 0,
  min: 0,
  max: 0,
  avg: 0,
  sd: 0
};

let newElements = [1, 2, 3, 4, 0];

for (let newElement of newElements) {
  stats = calculateNewStats(stats, newElement);
  console.log(stats);
}

Result on JSBin

也许你错过了最后一句话?

If you want the population variance or standard deviation replace N-1 with N and N-2 with N-1.


注意:精度会略有下降,随着元素的添加,精度会越来越大。我建议:

  • 将方差与sd一起存储在stats中;现在我正在计算方差的平方根以获得 SD,然后对 SD 求平方以获得下一次迭代的方差
  • 将总值存储在 stats 中,而不是在每次迭代时用 stats.avg * stats.count 重新计算它

您在 stats 中存储了 2 个以上的数字,但您的数字应该会更精确。

这是一个更好的实现:

const calculateNewStats = (stats, newElement) => {
  const newStats = {};
  newStats.count = stats.count + 1;
  newStats.total = stats.total + newElement;
  newStats.min = Math.min(stats.min, newElement);
  newStats.max = Math.max(stats.max, newElement);
  newStats.avg = (stats.total + newElement) / newStats.count;

  newStats.variance = (
    (newStats.count - 1) * stats.variance +
    (newElement - newStats.avg) * (newElement - stats.avg)
  ) / (newStats.count);

  newStats.sd = Math.sqrt(newStats.variance);

  return newStats;
};

// initial values
let stats = {
  count: 0,
  total: 0,
  min: 0,
  max: 0,
  avg: 0,
  sd: 0,
  variance: 0
};

let newElements = [1, 2, 3, 4, 0];

for (let newElement of newElements) {
  stats = calculateNewStats(stats, newElement);
  console.log(stats);
}

JSBin