如何有效地计算运行标准偏差?

Ale*_*lds 80 python statistics perl

我有一系列数字列表,例如:

[0] (0.01, 0.01, 0.02, 0.04, 0.03)
[1] (0.00, 0.02, 0.02, 0.03, 0.02)
[2] (0.01, 0.02, 0.02, 0.03, 0.02)
     ...
[n] (0.01, 0.00, 0.01, 0.05, 0.03)
Run Code Online (Sandbox Code Playgroud)

我想要做的是有效地计算所有数组元素的列表的每个索引的平均值和标准差.

为了做到这一点,我一直循环遍历数组并将列表的给定索引处的值相加.最后,我将"平均值列表"中的每个值除以n.

为了做标准偏差,我再次循环,现在我已经计算了平均值.

我想避免两次通过阵列,一次为平均值,然后一次为SD(在我有一个意思之后).

是否有一种有效的方法来计算两个值,只通过一次数组?解释语言(例如Perl或Python)或伪代码中的任何代码都可以.

Bob*_*ter 106

答案是使用Welford算法,该算法在"天真方法"之后非常明确地定义:

它在数值上比在其他响应中建议的两遍或在线简单平方和收集器更稳定.只有当你有很多彼此接近的值时,稳定性才真正重要,因为它们会导致浮点文献中所谓的" 灾难性消除 ".

您可能还想要在方差计算中除以样本数(N)和N-1之间的差异(平方偏差).除以N-1导致对样本的方差的无偏估计,而除以N平均低估方差(因为它没有考虑样本均值和真实均值之间的方差).

我在这个主题上写了两篇关于更多细节的博客文章,包括如何在线删除以前的值:

您还可以查看我的Java工具; javadoc,源和单元测试都在线:

  • 不错的答案,+ 1是为了提醒读者注意总体stddev与样本stddev之间的差异。 (2认同)

Jon*_*ler 71

基本的答案是在你去的时候积累x(称之为'sum_x1')和x 2(称之为'sum_x2')之和.那么标准差的值是:

stdev = sqrt((sum_x2 / n) - (mean * mean)) 
Run Code Online (Sandbox Code Playgroud)

哪里

mean = sum_x / n
Run Code Online (Sandbox Code Playgroud)

这是样本标准差; 你使用'n'而不是'n - 1'作为除数得到总体标准差.

如果处理大样本,您可能需要担心两个大数之间取差异的数值稳定性.有关更多信息,请转到其他答案(维基百科等)中的外部参考.

  • 我决定使用Welford的算法,因为它在相同的计算开销下可靠地执行. (2认同)
  • 这是答案的简化版本,并且可能根据输入给出非实际结果(即,当sum_x2 <sum_x1*sum_x1时).为了确保有效的实际结果,请使用`sd = sqrt(((n*sum_x2) - (sum_x1*sum_x1))/(n*(n - 1))) (2认同)
  • @Dan指出了一个有效的问题 - 上面的公式因x> 1而分解,因为你最终得到负数的sqrt.Knuth方法是:sqrt((sum_x2/n) - (mean*mean))其中mean =(sum_x/n). (2认同)

ars*_*ars 26

也许不是你问的问题,但是......如果你使用一个numpy数组,它会为你做有效的工作:

from numpy import array

nums = array(((0.01, 0.01, 0.02, 0.04, 0.03),
              (0.00, 0.02, 0.02, 0.03, 0.02),
              (0.01, 0.02, 0.02, 0.03, 0.02),
              (0.01, 0.00, 0.01, 0.05, 0.03)))

print nums.std(axis=1)
# [ 0.0116619   0.00979796  0.00632456  0.01788854]

print nums.mean(axis=1)
# [ 0.022  0.018  0.02   0.02 ]
Run Code Online (Sandbox Code Playgroud)

顺便说一句,在这篇博客文章中有一些有趣的讨论,以及对计算方法和差异的一次通过方法的评论:


Mar*_*age 26

以下是来自http://www.johndcook.com/standard_deviation.html的Welford算法实现的文字纯Python翻译:

https://github.com/liyanage/python-modules/blob/master/running_stats.py

class RunningStats:

    def __init__(self):
        self.n = 0
        self.old_m = 0
        self.new_m = 0
        self.old_s = 0
        self.new_s = 0

    def clear(self):
        self.n = 0

    def push(self, x):
        self.n += 1

        if self.n == 1:
            self.old_m = self.new_m = x
            self.old_s = 0
        else:
            self.new_m = self.old_m + (x - self.old_m) / self.n
            self.new_s = self.old_s + (x - self.old_m) * (x - self.new_m)

            self.old_m = self.new_m
            self.old_s = self.new_s

    def mean(self):
        return self.new_m if self.n else 0.0

    def variance(self):
        return self.new_s / (self.n - 1) if self.n > 1 else 0.0

    def standard_deviation(self):
        return math.sqrt(self.variance())
Run Code Online (Sandbox Code Playgroud)

用法:

rs = RunningStats()
rs.push(17.0);
rs.push(19.0);
rs.push(24.0);

mean = rs.mean();
variance = rs.variance();
stdev = rs.standard_deviation();
Run Code Online (Sandbox Code Playgroud)

  • 这应该是可接受的答案,因为它是唯一正确的并且显示算法,参考Knuth. (5认同)

Gra*_*ntJ 12

Python的RUNSTATS模块是只是这样的事情.从PyPI 安装runstats:

pip install runstats
Run Code Online (Sandbox Code Playgroud)

Runstats摘要可以在一次数据传递中产生均值,方差,标准差,偏度和峰度.我们可以用它来创建你的"运行"版本.

from runstats import Statistics

stats = [Statistics() for num in range(len(data[0]))]

for row in data:

    for index, val in enumerate(row):
        stats[index].push(val)

    for index, stat in enumerate(stats):
        print 'Index', index, 'mean:', stat.mean()
        print 'Index', index, 'standard deviation:', stat.stddev()
Run Code Online (Sandbox Code Playgroud)

统计概要基于用于计算一次通过中的标准偏差的Knuth和Welford方法,如计算机程序设计,第2卷,第7页中所述.232,第3版.这样做的好处是数值稳定和准确的结果.

免责声明:我是Python runstats模块的作者.


Sin*_*nür 8

Statistics :: Descriptive是一个非常适合这些类型计算的Perl模块:

#!/usr/bin/perl

use strict; use warnings;

use Statistics::Descriptive qw( :all );

my $data = [
    [ 0.01, 0.01, 0.02, 0.04, 0.03 ],
    [ 0.00, 0.02, 0.02, 0.03, 0.02 ],
    [ 0.01, 0.02, 0.02, 0.03, 0.02 ],
    [ 0.01, 0.00, 0.01, 0.05, 0.03 ],
];

my $stat = Statistics::Descriptive::Full->new;
# You also have the option of using sparse data structures

for my $ref ( @$data ) {
    $stat->add_data( @$ref );
    printf "Running mean: %f\n", $stat->mean;
    printf "Running stdev: %f\n", $stat->standard_deviation;
}
__END__
Run Code Online (Sandbox Code Playgroud)

输出:

C:\Temp> g
Running mean: 0.022000
Running stdev: 0.013038
Running mean: 0.020000
Running stdev: 0.011547
Running mean: 0.020000
Running stdev: 0.010000
Running mean: 0.020000
Running stdev: 0.012566
Run Code Online (Sandbox Code Playgroud)


dra*_*tun 8

看看PDL(发音为"piddle!").

这是Perl数据语言,专为高精度数学和科学计算而设计.

这是一个使用你的数字的例子....

use strict;
use warnings;
use PDL;

my $figs = pdl [
    [0.01, 0.01, 0.02, 0.04, 0.03],
    [0.00, 0.02, 0.02, 0.03, 0.02],
    [0.01, 0.02, 0.02, 0.03, 0.02],
    [0.01, 0.00, 0.01, 0.05, 0.03],
];

my ( $mean, $prms, $median, $min, $max, $adev, $rms ) = statsover( $figs );

say "Mean scores:     ", $mean;
say "Std dev? (adev): ", $adev;
say "Std dev? (prms): ", $prms;
say "Std dev? (rms):  ", $rms;
Run Code Online (Sandbox Code Playgroud)


哪个产生:

Mean scores:     [0.022 0.018 0.02 0.02]
Std dev? (adev): [0.0104 0.0072 0.004 0.016]
Std dev? (prms): [0.013038405 0.010954451 0.0070710678 0.02]
Std dev? (rms):  [0.011661904 0.009797959 0.0063245553 0.017888544]
Run Code Online (Sandbox Code Playgroud)


有关statsover函数的更多信息, 请查看PDL :: Primitive.这似乎表明ADEV是"标准偏差".

然而,它可能是PRMS(Sinan的统计数据::描述性示例显示)或RMS(ars的NumPy示例显示).我猜这三个中的一个一定是对的;-)

有关更多PDL信息,请查看:

  • 这不是一个运行计算。 (2认同)