计算与上一个元素和一个新元素(累积/增量SD)的标准偏差(σ)

时间:2019-06-21 22:10:45

标签: javascript typescript statistics benchmarking

正在对一个正在运行的进程进行基准测试,其中“累计”计算以下数字(从以前的值+新元素开始):

  • 最短持续时间
  • 最长持续时间
  • 平均持续时间

持续时间的 Standard Deviation(σ)也应该计算出来,因为它表示statistical dispersion

基于应用程序,存储每个元素/数字很不方便,因此有必要针对先前的值和新元素进行计算。

示例:

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),但结果数字不正确。

1 个答案:

答案 0 :(得分:2)

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

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

  

如果要总体方差或标准差,请用N替换N-1,用N-1替换N-2。


注意:添加元素时,精度会有所降低,精度会有所提高。我建议:

  • 将方差与stats一起存储在sd中;现在,我正在计算方差的平方根以获取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