流中的标准差计算

40 投票
3 回答
22330 浏览
提问于 2025-04-16 15:04

假设我在用Python处理一堆已知数量的物品I,我可以记录处理每个物品所需的时间t,还有到目前为止处理这些物品总共花费的时间T和已经处理的物品数量c。我现在是通过计算平均值A = T / c来得到每个物品的平均处理时间,但如果有一个物品处理得特别慢,比如花了几秒钟,而其他的只花了几毫秒,这样就会影响到我的平均值。

我想要显示一个实时的标准差,但我该如何做到这一点,而不需要记录每个t的值呢?

3 个回答

29

根据Welford算法

import numpy as np

class OnlineVariance(object):
    """
    Welford's algorithm computes the sample variance incrementally.
    """

    def __init__(self, iterable=None, ddof=1):
        self.ddof, self.n, self.mean, self.M2 = ddof, 0, 0.0, 0.0
        if iterable is not None:
            for datum in iterable:
                self.include(datum)

    def include(self, datum):
        self.n += 1
        self.delta = datum - self.mean
        self.mean += self.delta / self.n
        self.M2 += self.delta * (datum - self.mean)

    @property
    def variance(self):
        return self.M2 / (self.n - self.ddof)

    @property
    def std(self):
        return np.sqrt(self.variance)

每当有新的数据进来时,就更新方差:

N = 100
data = np.random.random(N)
ov = OnlineVariance(ddof=0)
for d in data:
    ov.include(d)
std = ov.std
print(std)

用numpy计算的标准差来检查我们的结果:

assert np.allclose(std, data.std())
52

维基百科关于标准差的文章中提到,我们只需要关注以下三个总和:

s0 = sum(1 for x in samples)
s1 = sum(x for x in samples)
s2 = sum(x*x for x in samples)

这些总和在新数据到来时很容易更新。标准差可以通过以下公式计算:

std_dev = math.sqrt((s0 * s2 - s1 * s1)/(s0 * (s0 - 1)))

需要注意的是,如果你的样本是浮点数,并且标准差相对于样本的平均值来说很小,那么这种计算标准差的方法可能会出现数值不稳定的问题。如果你预计会有这样的样本,建议使用Welford的方法(请参见被接受的答案)。

17

我使用的是Welford的方法,这个方法能给出更准确的结果。这个链接指向了John D. Cook的概述。下面是其中一段,简单总结了为什么这个方法更受欢迎:

这种更好的计算方差的方法可以追溯到1962年B. P. Welford的一篇论文,并在Donald Knuth的《计算机程序设计艺术》第2卷第232页(第三版)中介绍。虽然这个解决方案已经被知道了几十年,但还是有很多人不知道它。大多数人可能在第一次计算标准差时才意识到,计算样本方差可能会很困难,尤其是当他们遇到负数开平方的异常情况时。

撰写回答