개수와 데이터 합계를 유지하지 않고 이동 평균을 계산하는 방법은 무엇입니까?


118

지금까지받은 수와 총 데이터를 저장하지 않고 이동 누적 평균을 계산하는 방법을 찾으려고합니다.

두 가지 알고리즘을 생각해 냈지만 둘 다 개수를 저장해야합니다.

  • 새 평균 = ((이전 개수 * 이전 데이터) + 다음 데이터) / 다음 개수
  • 새 평균 = 이전 평균 + (다음 데이터-이전 평균) / 다음 개수

이러한 방법의 문제점은 개수가 점점 커져 결과 평균의 정밀도를 잃는다는 것입니다.

첫 번째 방법은 분명히 1로 떨어져있는 이전 개수와 다음 개수를 사용합니다. 이것은 아마도 카운트를 제거하는 방법이 있다고 생각하게했지만 불행히도 아직 찾지 못했습니다. 그래도 조금 더 나아서 두 번째 방법을 얻었지만 여전히 카운트가 존재합니다.

가능합니까, 아니면 불가능한 것을 찾고 있습니까?


1
NB는 수치 적으로 현재 합계와 현재 카운트를 저장하는 것이 가장 안정적인 방법입니다. 그렇지 않으면 더 높은 카운트의 경우 다음 / (다음 카운트)가 언더 플로되기 시작합니다. 따라서 정밀도 손실이 정말로 걱정된다면 합계를 유지하십시오!
AlexR

답변:


91

간단하게 다음을 수행 할 수 있습니다.

double approxRollingAverage (double avg, double new_sample) {

    avg -= avg / N;
    avg += new_sample / N;

    return avg;
}

N평균화하려는 샘플 수는 어디에 있습니까 ? 이 근사는 지수 이동 평균과 동일합니다. 참조 : C ++에서 롤링 / 이동 평균 계산


3
이 줄 앞에 N에 1을 더할 필요가 없습니까? 평균 + = new_sample / N;
Damian

20
이것은 전적으로 정확하지 않습니다. @Muis가 설명하는 것은 지수 가중치가 적용된 이동 평균으로, 때로는 적절하지만 OP가 요청한 것과는 다릅니다. 예를 들어, 대부분의 포인트가 2 ~ 4 범위에 있지만 하나의 값이 백만 이상일 때 예상되는 동작을 고려하십시오. EWMA (여기)는 그 백만의 흔적을 꽤 오랫동안 보유 할 것입니다. OP로 표시되는 유한 회선은 N 단계 후 즉시 손실됩니다. 지속적인 저장의 이점이 있습니다.
JMA

9
그것은 이동 평균이 아닙니다. 설명하는 것은 신호의 점프에 대한 지수 응답을 생성하는 단극 필터입니다. 이동 평균은 길이 N. 선형 응답을 생성
ruhig 브라우

3
이것은 평균의 일반적인 정의와는 거리가 멀다는 점에 유의하십시오. N = 5로 설정하고 5 개의 5샘플을 입력 하면 평균은 0.67이됩니다.
Dan Dascalescu

2
@DanDascalescu 이것이 실제로 롤링 평균이 아니라는 것은 맞지만, 명시된 값은 몇 배 차이가 있습니다. 로 avg초기화하면 5 초 후, 10 초 후에 0종료됩니다 . cpp.sh/2ryql 긴 평균의 경우 이것은 확실히 유용한 근사치입니다. 3.3654.46
cincodenada

80
New average = old average * (n-1)/n + new value /n

이것은 카운트가 하나의 값만큼만 변경되었다고 가정합니다. M 값으로 변경된 경우 :

new average = old average * (n-len(M))/n + (sum of values in M)/n).

이것은 수학적 공식입니다 (가장 효율적인 공식이라고 생각합니다). 스스로 추가 코드를 작성할 수 있다고 믿습니다.


새로운 가치의 합계는 무엇입니까? 원래 공식의 "새로운 가치"와는 어떻게 다른가요?
Mikhail

두 번째 예의 @Mikhail에서는 m새로운 평균에 새로운 값이 반영됩니다. 저는 sum of new value여기가 m새로운 평균을 계산하는 데 사용되는 새로운 값 의 합계를 의미 한다고 믿습니다 .
패트릭 Goley

9
첫 번째 경우에 대해 약간 더 효율적입니다 new_average = (old_average * (n-1) + new_value) / n.-- 분할 중 하나를 제거합니다.
Pixelstix 2017 년

6,0,0,9로 평균 3 개 요소를 실행하는 것은 어떻습니까?
Roshan Mehta

1
이 방정식을 구현하면 값 또는 실행 평균이 항상 천천히 증가합니다. 절대 내려 가지 않고 올라갈뿐입니다.
anon58192932

30

에서 블로그 평균도 계산됩니다 표본 분산의 계산, 실행에 대한 Welford의 방법 :

여기에 이미지 설명 입력

안타깝게도 SVG 이미지를 업로드 할 수 없습니다.


3
이것은 나누기가 공통 요소로 사용된다는 점을 제외하면 Muis가 구현 한 것과 유사합니다. 따라서 단 하나의 부서.
Flip

Muis가 N 증가를 설명하지 않는다는 점에서 실제로 @ Abdullah-Al-Ageel (본질적으로 교환 수학)에 더 가깝습니다. 복사-붙여 넣기 공식 참조 : [Avg at n] = [Avg at n-1] + (x-[Avg at n-1]) / n
drzaus

2
@Flip & drwaus : Muis와 Abdullah Al-Ageel 솔루션이 정확히 동일하지 않습니까? 동일한 계산이며 다르게 작성되었습니다. 저에게이 3 가지 답변은 동일합니다.이 답변은 더 시각적입니다 (너무 나쁩니다. MathJax를 사용할 수 없습니다).
user276648

21

Muis , Abdullah Al-AgeelFlip 의 답변이 다르게 쓰여진 것을 제외하고 는 모두 수학적으로 동일한 방법에 대한 주석을 제공하는 또 다른 답변 이 있습니다.

물론, 반올림 오류가 각각에 약간 다른 영향을 미치는 방식을 설명하는 José Manuel Ramos 의 분석이 있지만 이는 구현에 따라 다르며 각 답변이 코드에 어떻게 적용되었는지에 따라 변경됩니다.

그러나 다소 큰 차이가 있습니다

그것은 Muis 's N, Flip 's kAbdullah Al-Ageel 's에 n있습니다. 압둘라 알 - Ageel은 매우 무엇인지 설명하지 않습니다 n해야하지만 N하고 k있다는 점에서 차이가 N있다 " 당신이 평균 이상으로 원하는 샘플 수는 "동안은 k샘플링 값의 수입니다. ( N 샘플 수를 호출 하는 것이 정확한지 의심 스럽지만 )

그리고 여기에서 우리는 아래의 답을 얻습니다. 본질적으로 다른 것들 과 동일한 오래된 지수 가중 이동 평균 이므로 대안을 찾고 있다면 여기에서 중지하십시오.

지수 가중 이동 평균

처음에는 :

average = 0
counter = 0

각 값에 대해 :

counter += 1
average = average + (value - average) / min(counter, FACTOR)

차이점은 min(counter, FACTOR)부분입니다. 이것은 말하는 것과 같습니다 min(Flip's k, Muis's N).

FACTOR평균이 최신 추세를 "따라 잡는"속도에 영향을주는 상수입니다. 숫자가 작을수록 빠릅니다. ( 1더 이상 평균이 아니며 단지 최신 값이됩니다.)

이 대답에는 실행 카운터가 필요합니다 counter. 문제가있는 경우 min(counter, FACTOR)를로 대체하여 Muis 의 대답 FACTOR으로 바꿀 수 있습니다 . 이 작업의 문제는 이동 평균 이 초기화되는 항목의 영향을 받는다는 것입니다. 로 초기화 된 경우 해당 0이 평균을 벗어나는 데 오랜 시간이 걸릴 수 있습니다.average0

어떻게 보이는지

지수 이동 평균


3
잘 설명했습니다. OP가 요청한 것이기 때문에 그래프에서 평범한 평균을 놓쳤습니다.
xmedeko

아마도 내가 뭔가를 놓치고 있을지도 모르지만 당신은 우연히 max(counter, FACTOR). min(counter, FACTOR)항상 FACTOR를 반환합니다.
WebWanderer

1
핵심은 min(counter, FACTOR)워밍업 기간을 설명하는 것입니다. FACTOR (또는 N 또는 원하는 샘플 수)가 1000이면 정확한 결과를 얻기 전에 최소 1000 개의 샘플이 필요합니다. 20.
rharter

계수에 도달 한 후 계산을 중지하는 것이 좋을 것입니다. 아마도 그렇게하는 것이 더 빠를 것입니다.
inf3rno

8

Flip의 대답은 Muis보다 계산적으로 더 일관 적입니다.

이중 숫자 형식을 사용하면 Muis 접근 방식에서 반올림 문제를 볼 수 있습니다.

Muis 접근 방식

나누고 빼면 이전에 저장된 값에 반올림이 나타나 변경됩니다.

그러나 Flip 접근 방식은 저장된 값을 보존하고 분할 수를 줄여서 반올림을 줄이고 저장된 값으로 전파되는 오류를 최소화합니다. 추가 할 항목이있는 경우에만 반올림이 표시됩니다 (N이 크면 추가 할 항목이 없음).

Flip 접근 방식

큰 값의 평균을 만들 때 이러한 변화는 그 평균이 0이되는 경향이 있습니다.

스프레드 시트 프로그램을 사용하여 결과를 보여드립니다.

첫째, 결과는 다음과 같습니다. 결과

A 및 B 열은 각각 n 및 X_n 값입니다.

C 열은 Flip 접근 방식이고 D 열은 Muis 접근 방식이며 그 결과는 평균에 저장됩니다. E 열은 계산에 사용 된 중간 값에 해당합니다.

다음은 짝수 값의 평균을 보여주는 그래프입니다.

그래프

보시다시피 두 접근 방식에는 큰 차이가 있습니다.


2
답은 아니지만 유용한 정보입니다. n 과거 값 에 대한 실제 평균에 대해 그래프에 세 번째 선을 추가 하면 두 가지 접근 방식 중 가장 가까운 것을 볼 수 있습니다.
jpaugh

2
@jpaugh : B 열은 -1.00E + 15와 1.00E + 15 사이를 번갈아 가며 N이 짝수이면 실제 평균은 0이되어야합니다. 그래프의 제목은 "부분 평균 짝수"입니다. 이것은 당신이 묻는 세 번째 줄이 단순히 f (x) = 0임을 의미합니다. 그래프는 두 접근 방식 모두 계속 올라가고 올라가는 오류를 유발한다는 것을 보여줍니다.
desowin

맞습니다. 그래프는 두 접근법을 사용하는 계산에 포함 된 큰 숫자를 사용하여 전파 된 오류를 정확히 보여줍니다.
José Manuel Ramos

그래프의 범례에 잘못된 색상이 있습니다. Muis는 주황색, Flip은 파란색입니다.
xmedeko

6

비교를 위해 자바 스크립트를 사용하는 예 :

https://jsfiddle.net/drzaus/Lxsa4rpz/

function calcNormalAvg(list) {
    // sum(list) / len(list)
    return list.reduce(function(a, b) { return a + b; }) / list.length;
}
function calcRunningAvg(previousAverage, currentNumber, index) {
    // [ avg' * (n-1) + x ] / n
    return ( previousAverage * (index - 1) + currentNumber ) / index;
}


1

Java8에서 :

LongSummaryStatistics movingAverage = new LongSummaryStatistics();
movingAverage.accept(new data);
...
average = movingAverage.getAverage();

당신은 또한 가지고 IntSummaryStatistics, DoubleSummaryStatistics...


2
OP는 Java에서 이것을 계산하는 방법에 대한 포인터가 아니라 알고리즘을 요구합니다.
olq_plo

0

위의 답변을 기반으로 한 깔끔한 Python 솔루션 :

class RunningAverage():
    def __init__(self):
        self.average = 0
        self.n = 0
        
    def __call__(self, new_value):
        self.n += 1
        self.average = (self.average * (self.n-1) + new_value) / self.n 
        
    def __float__(self):
        return self.average
    
    def __repr__(self):
        return "average: " + str(self.average)

용법:

x = RunningAverage()
x(0)
x(2)
x(4)
print(x)
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.