[python] 실행 표준 편차를 효율적으로 계산하는 방법은 무엇입니까?

예를 들어 다음과 같은 숫자 목록이 있습니다.

[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)

내가하고 싶은 것은 모든 배열 요소에서 목록의 각 인덱스에서 평균과 표준 편차를 효율적으로 계산하는 것입니다.

평균을 내기 위해 배열을 반복하고 목록의 지정된 인덱스에서 값을 합산했습니다. 마지막으로 “평균 목록”의 각 값을 n(집단의 표본이 아닌 모집단으로 작업하고 있습니다)로 나눕니다 .

표준 편차를 수행하기 위해 평균을 계산 했으므로 다시 반복합니다.

나는 배열을 두 번, 평균을 위해 한 번, SD를 위해 한 번 (평균을 얻은 후) 피하고 싶습니다.

두 값을 계산하는 효율적인 방법이 있습니까? 배열을 한 번만 통과합니까? 해석 된 언어 (예 : Perl 또는 Python) 또는 의사 코드로 된 코드는 괜찮습니다.



답변

대답은 Welford의 알고리즘을 사용하는 것입니다. 이는 다음에서 “순진한 방법”뒤에 매우 명확하게 정의되어 있습니다.

다른 응답에서 제안 된 2- 패스 또는 온라인 단순 제곱합 수집기보다 수치 적으로 더 안정적입니다. 안정성 은 부동 소수점 문헌에서 ” 치명적 취소 ” 로 알려진 결과로 이어지기 때문에 서로 가까운 값이 많은 경우에만 실제로 중요합니다 .

분산 계산 (제곱 편차)에서 샘플 수 (N)로 나누는 것과 N-1 사이의 차이를 살펴볼 수도 있습니다. N-1로 나누면 표본에서 편향되지 않은 분산 추정값이 생성되는 반면, 평균 N으로 나누면 분산이 과소 평가됩니다 (표본 평균과 실제 평균 사이의 분산을 고려하지 않기 때문).

온라인에서 이전 값을 삭제하는 방법을 포함하여 더 자세한 내용을 다루는 주제에 대한 두 개의 블로그 항목을 작성했습니다.

내 Java 구현을 살펴볼 수도 있습니다. javadoc, 소스 및 단위 테스트는 모두 온라인입니다.


답변

기본적인 대답은 x ( ‘sum_x1’이라고 부름)와 x 2 ( ‘sum_x2’라고 부름)의 합계를 모으는 것입니다. 표준 편차의 값은 다음과 같습니다.

stdev = sqrt((sum_x2 / n) - (mean * mean))

어디

mean = sum_x / n

이것은 표본 표준 편차입니다. 제수로 ‘n-1’대신 ‘n’을 사용하여 모집단 표준 편차를 얻습니다.

큰 표본을 다루는 경우 두 큰 숫자 간의 차이를 가져 오는 수치 안정성에 대해 걱정해야 할 수도 있습니다. 자세한 내용은 다른 답변 (Wikipedia 등)의 외부 참조로 이동하십시오.


답변

다음은 http://www.johndcook.com/standard_deviation.html 에서 Welford 알고리즘 구현의 문자 그대로 순수한 Python 번역입니다 . .

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

import math

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())

용법:

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

mean = rs.mean()
variance = rs.variance()
stdev = rs.standard_deviation()

print(f'Mean: {mean}, Variance: {variance}, Std. Dev.: {stdev}')


답변

아마도 당신이 요청한 것은 아니지만 … 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 ]

그건 그렇고,이 블로그 게시물에는 평균과 분산을 계산하기위한 원 패스 방법에 대한 몇 가지 흥미로운 토론이 있습니다.


답변

파이썬 RUNSTATS 모듈 것은 단지 이런 종류입니다. PyPI에서 runstats설치합니다 .

pip install runstats

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()

통계 요약은 Art of Computer Programming, Vol 2, p에 설명 된대로 한 번의 패스로 표준 편차를 계산하기위한 Knuth 및 Welford 방법을 기반으로합니다. 232, 3 판. 이것의 이점은 수치 적으로 안정적이고 정확한 결과입니다.

면책 조항 : 저는 Python runstats 모듈의 작성자입니다.


답변

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__

산출:

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


답변

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;

다음을 생성합니다.

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]

통계 에 대한 자세한 내용
PDL :: Primitive 를 참조하십시오. 함수 . 이것은 ADEV가 “표준 편차”임을 시사하는 것 같습니다.

그러나 PRMS (Sinan의 Statistics :: Descriptive 예제가 표시됨) 또는 RMS (ars의 NumPy 예제가 표시됨) 일 수 있습니다. 이 세 가지 중 하나가 맞을 것 같아요 😉

자세한 PDL 정보는 다음을 참조하십시오.