이동 평균 또는 평균


195

특정 창에서 주어진 1D 배열의 평균을 계산하는 Python 용 SciPy 함수 또는 NumPy 함수 또는 모듈이 있습니까?

답변:


24

종속성없이 하나의 루프에서 모든 것을 수행하는 짧고 빠른 솔루션의 경우 아래 코드가 훌륭하게 작동합니다.

mylist = [1, 2, 3, 4, 5, 6, 7]
N = 3
cumsum, moving_aves = [0], []

for i, x in enumerate(mylist, 1):
    cumsum.append(cumsum[i-1] + x)
    if i>=N:
        moving_ave = (cumsum[i] - cumsum[i-N])/N
        #can do stuff with moving_ave here
        moving_aves.append(moving_ave)

46
빠른?! 이 솔루션은 Numpy 솔루션보다 훨씬 느립니다.
Bart

3
이 기본 솔루션은 훌륭하지만 OP는 numpy / scipy 기능을 요청했습니다. 아마도이 기능이 훨씬 빠를 것입니다.
Demis

255

UPD : Alleojasaarim 이보다 효율적인 솔루션을 제안했습니다 .


당신은 np.convolve그것을 위해 사용할 수 있습니다 :

np.convolve(x, np.ones((N,))/N, mode='valid')

설명

연속 평균은 컨볼 루션 의 수학 연산의 경우입니다 . 연속 평균의 경우 입력을 따라 창을 밀고 창 내용의 평균을 계산합니다. 불연속 1D 신호의 경우 임의의 선형 조합을 계산하는 평균 대신 각 요소에 해당 계수를 곱하고 결과를 더하는 것을 제외하고 컨볼 루션은 동일합니다. 창의 각 위치마다 하나씩 해당 계수를 컨볼 루션 커널 이라고합니다 . 이제 N 값의 산술 평균은입니다 (x_1 + x_2 + ... + x_N) / N. 따라서 해당 커널은 입니다. (1/N, 1/N, ..., 1/N)이것이 바로 우리가 사용하는 것 np.ones((N,))/N입니다.

가장자리

mode의 인수 np.convolve를 지정하는 방법 가장자리를 처리합니다. 나는 valid대부분의 사람들이 달리기 평균이 작동하는 것을 기대한다고 생각하기 때문에 여기 에서 모드를 선택 했지만 다른 우선 순위가있을 수 있습니다. 다음은 모드 간의 차이점을 보여주는 그림입니다.

import numpy as np
import matplotlib.pyplot as plt
modes = ['full', 'same', 'valid']
for m in modes:
    plt.plot(np.convolve(np.ones((200,)), np.ones((50,))/50, mode=m));
plt.axis([-10, 251, -.1, 1.1]);
plt.legend(modes, loc='lower center');
plt.show()

평균 회전 모드 실행


5
나는이 솔루션이 깨끗하기 때문에 (한 줄) 비교적 효율적입니다 (numpy 내부에서 수행 된 작업). 그러나 Alleo의 "효율적인 솔루션"을 사용하면 numpy.cumsum더 복잡해집니다.
Ulrich Stern

2
@denfromufa, 나는 문서가 충분히 구현을 다루고 있으며 수학을 설명하는 Wikipedia에 링크되어 있다고 생각합니다. 질문의 초점을 고려할 때이 답변이 그 내용을 복사해야한다고 생각합니까?
lapis

@lapis 이동 평균에 대한 convolve의 사용은 매우 이례적이고 명백하지 않습니다. 내가 찾은 최고의 시각적 설명은 다음과 같습니다. matlabtricks.com/post-11/moving-average-by-convolution
denfromufa

플로팅 및 관련 작업의 경우 없음 값으로 채우는 것이 좋습니다. 내 (너무 아름답 지 않고 짧은) 제안 :```def moving_average (x, N, fill = True) : return np.concatenate ([x의 [[없음] * (N // 2 + N % 2)) * fill, np.convolve (x, np.ones ((N,)) / N, mode = 'valid'), [None] * (N // 2) * fill,] if len (x)])` ``SO 의견 xD에서 코드가 너무 추해 보입니다 .xD가 너무 많기 때문에 다른 답변을 추가하고 싶지 않지만 IDE에 복사하여 붙여 넣을 수 있습니다.
Chaoste

146

효율적인 솔루션

컨볼 루션은 간단한 접근법보다 훨씬 낫지 만 FFT를 사용하므로 상당히 느립니다. 그러나 달리기 계산을 위해 특별히 다음과 같은 접근 방식이 잘 작동합니다.

def running_mean(x, N):
    cumsum = numpy.cumsum(numpy.insert(x, 0, 0)) 
    return (cumsum[N:] - cumsum[:-N]) / float(N)

확인할 코드

In[3]: x = numpy.random.random(100000)
In[4]: N = 1000
In[5]: %timeit result1 = numpy.convolve(x, numpy.ones((N,))/N, mode='valid')
10 loops, best of 3: 41.4 ms per loop
In[6]: %timeit result2 = running_mean(x, N)
1000 loops, best of 3: 1.04 ms per loop

참고 numpy.allclose(result1, result2)되어 True, 두 가지 방법은 동일하다. N이 클수록 시간의 차이가 큽니다.

경고 : cumsum이 빠르더라도 부동 소수점 오류가 증가하여 결과가 유효하지 않거나 잘못되거나 허용되지 않을 수 있습니다

의견은 여기서이 부동 소수점 오류 문제를 지적했지만 대답에서 더 명확하게하고 있습니다. .

# demonstrate loss of precision with only 100,000 points
np.random.seed(42)
x = np.random.randn(100000)+1e6
y1 = running_mean_convolve(x, 10)
y2 = running_mean_cumsum(x, 10)
assert np.allclose(y1, y2, rtol=1e-12, atol=0)
  • 부동 소수점 오차가 커질수록 더 많은 포인트가 누적됩니다 (따라서 1e5 포인트가 눈에 띄고 1e6 포인트가 더 중요하고 1e6보다 높으며 누산기를 재설정 할 수 있음)
  • 당신은 사용하여 부정 np.longdouble행위 를 할 수 있지만 부동 소수점 오류는 비교적 많은 수의 포인트에서 여전히 중요합니다 (약 1e5 이상이지만 데이터에 따라 다름)
  • 오류를 플로팅하고 상대적으로 빠르게 증가하는 것을 볼 수 있습니다
  • convolve 솔루션 은 느리지 만이 부동 소수점 정밀도 손실은 없습니다
  • uniform_filter1d 솔루션 은이 cumsum 솔루션보다 빠르며 부동 소수점 정밀도 손실이 없습니다.

3
좋은 해결책! 나의 직감은 numpy.convolveO (mn)입니다. 그 문서는 언급 scipy.signal.fftconvolve사용 FFT를.
Ulrich Stern

3
이 방법은 배열의 가장자리를 다루지 않습니다.
JoVe

6
좋은 해결책이지만 배열의 끝쪽으로 작은 결과를 얻기 위해 큰 숫자 두 개를 빼면 큰 배열의 숫자 오류가 발생할 수 있습니다.
Bas Swinckels

1
이것은 float 나누기 대신 integer 나누기를 사용합니다 : running_mean([1,2,3], 2)gives array([1, 2]). 에 x의해 교체 [float(value) for value in x]트릭 을 수행합니다.
ChrisW

4
x부유물 이 포함 된 경우이 솔루션의 수치 안정성이 문제가 될 수 있습니다 . 예 : 기대하는 동안 running_mean(np.arange(int(1e7))[::-1] + 0.2, 1)[-1] - 0.2반환합니다 . 추가 정보 : en.wikipedia.org/wiki/Loss_of_significance0.0031250.0
밀라노

80

업데이트 : 아래 예 pandas.rolling_mean는 최신 버전의 팬더에서 제거 된 이전 기능을 보여줍니다 . 아래의 함수 호출과 같은 현대식은

In [8]: pd.Series(x).rolling(window=N).mean().iloc[N-1:].values
Out[8]: 
array([ 0.49815397,  0.49844183,  0.49840518, ...,  0.49488191,
        0.49456679,  0.49427121])

팬더 는 NumPy 또는 SciPy보다 더 적합합니다. rolling_mean 함수 는 작업을 편리하게 수행합니다. 또한 입력이 배열 인 경우 NumPy 배열을 반환합니다.

rolling_mean사용자 정의 순수 Python 구현으로 성능 을 능가하는 것은 어렵습니다 . 제안 된 두 가지 솔루션에 대한 성능 예는 다음과 같습니다.

In [1]: import numpy as np

In [2]: import pandas as pd

In [3]: def running_mean(x, N):
   ...:     cumsum = np.cumsum(np.insert(x, 0, 0)) 
   ...:     return (cumsum[N:] - cumsum[:-N]) / N
   ...:

In [4]: x = np.random.random(100000)

In [5]: N = 1000

In [6]: %timeit np.convolve(x, np.ones((N,))/N, mode='valid')
10 loops, best of 3: 172 ms per loop

In [7]: %timeit running_mean(x, N)
100 loops, best of 3: 6.72 ms per loop

In [8]: %timeit pd.rolling_mean(x, N)[N-1:]
100 loops, best of 3: 4.74 ms per loop

In [9]: np.allclose(pd.rolling_mean(x, N)[N-1:], running_mean(x, N))
Out[9]: True

가장자리 값을 처리하는 방법에 대한 멋진 옵션도 있습니다.


6
Pandas rolling_mean은 작업에 유용한 도구이지만 ndarray에서는 더 이상 사용되지 않습니다. 향후 Pandas 릴리스에서는 Pandas 시리즈에서만 작동합니다. 팬더가 아닌 어레이 데이터를 위해 지금 어디로 향합니까?
Mike

5
@Mike rolling_mean ()은 더 이상 사용되지 않지만 이제는 롤링을 사용하고 별도로 의미 할 수 있습니다. df.rolling(windowsize).mean()이제 대신 작동합니다 (매우 빨리 추가 할 수 있음). 6,000 개의 행 시리즈에서 1000 개의 루프를%timeit test1.rolling(20).mean() 반환 , 루프 당 3 :
1.16ms 최고

5
@Vlox df.rolling()는 충분히 잘 작동하지만 문제는이 양식조차도 앞으로 ndarray를 지원하지 않을 것입니다. 이를 사용하려면 먼저 Pandas Dataframe에 데이터를로드해야합니다. 이 기능이 numpy또는에 추가 된 것을보고 싶습니다 scipy.signal.
Mike

1
@Mike는 완전히 동의합니다. 특히 내 배열에 pandas .ewm (). mean () 속도를 맞추기 위해 고심하고 있습니다 (먼저 df로로드하는 대신). 내 말은, 그것이 빠르다는 것은 좋지만 데이터 프레임 안팎으로 약간의 움직임이 너무 자주 느껴집니다.
Vlox

6
%timeit bottleneck.move_mean(x, N)내 PC의 cumsum 및 pandas 방법보다 3 ~ 15 배 빠릅니다. 리포지토리의 README 에서 벤치 마크를 살펴보십시오 .
mab

50

다음을 사용하여 누적 평균을 계산할 수 있습니다.

import numpy as np

def runningMean(x, N):
    y = np.zeros((len(x),))
    for ctr in range(len(x)):
         y[ctr] = np.sum(x[ctr:(ctr+N)])
    return y/N

그러나 느리다.

다행히 numpy에는 속도를 높이기 위해 사용할 수 있는 convolve 함수가 포함되어 있습니다. 연속 평균은 길이 xN긴 벡터를 사용 하여 모든 구성원 이 균등화 하는 것과 같습니다 1/N. numpy convolve 구현에는 시작 과도가 포함되므로 첫 번째 N-1 점을 제거해야합니다.

def runningMeanFast(x, N):
    return np.convolve(x, np.ones((N,))/N)[(N-1):]

내 컴퓨터에서 빠른 버전은 입력 벡터의 길이와 평균화 창의 크기에 따라 20-30 배 빠릅니다.

convolve에는 'same'시작 일시적 문제를 해결해야하는 것처럼 보이는 모드 가 포함되어 있지만 시작과 끝으로 분할됩니다.


첫 번째 N-1 점을 제거하면 마지막 점에서 여전히 경계 효과가 남습니다. 이 문제를 해결하는 쉬운 방법은 사용하는 것입니다 mode='valid'convolve사후 처리를 필요로하지 않는다.
lapis

1
@Psycho- mode='valid'양쪽 끝에서 과도를 제거합니다. len(x)=10and N=4, 실행 평균의 경우 10 개의 결과를 원하지만 valid7을 반환합니다.
mtrw

1
끝에서 과도를 제거하며 시작 부분에는 과도가 없습니다. 글쎄, 그것은 우선 순위의 문제라고 생각합니다. 데이터에없는 0으로 기울기를 얻는 비용에 대해 동일한 수의 결과가 필요하지 않습니다. BTW, 다음은 모드 간의 차이를 표시하는 명령입니다 modes = ('full', 'same', 'valid'); [plot(convolve(ones((200,)), ones((50,))/50, mode=m)) for m in modes]; axis([-10, 251, -.1, 1.1]); legend(modes, loc='lower center')(pyplot 및 numpy를 가져온 상태).
lapis

runningMean배열의 x[ctr:(ctr+N)]오른쪽에 대해 배열을 벗어날 때 0으로 평균화하는 부작용이 있습니까?
mrgloom

runningMeanFast이 테두리 효과 문제도 있습니다.
mrgloom

22

또는 파이썬을위한 모듈

Tradewave.net의 테스트에서 TA-lib는 항상 다음과 같이 승리합니다.

import talib as ta
import numpy as np
import pandas as pd
import scipy
from scipy import signal
import time as t

PAIR = info.primary_pair
PERIOD = 30

def initialize():
    storage.reset()
    storage.elapsed = storage.get('elapsed', [0,0,0,0,0,0])

def cumsum_sma(array, period):
    ret = np.cumsum(array, dtype=float)
    ret[period:] = ret[period:] - ret[:-period]
    return ret[period - 1:] / period

def pandas_sma(array, period):
    return pd.rolling_mean(array, period)

def api_sma(array, period):
    # this method is native to Tradewave and does NOT return an array
    return (data[PAIR].ma(PERIOD))

def talib_sma(array, period):
    return ta.MA(array, period)

def convolve_sma(array, period):
    return np.convolve(array, np.ones((period,))/period, mode='valid')

def fftconvolve_sma(array, period):    
    return scipy.signal.fftconvolve(
        array, np.ones((period,))/period, mode='valid')    

def tick():

    close = data[PAIR].warmup_period('close')

    t1 = t.time()
    sma_api = api_sma(close, PERIOD)
    t2 = t.time()
    sma_cumsum = cumsum_sma(close, PERIOD)
    t3 = t.time()
    sma_pandas = pandas_sma(close, PERIOD)
    t4 = t.time()
    sma_talib = talib_sma(close, PERIOD)
    t5 = t.time()
    sma_convolve = convolve_sma(close, PERIOD)
    t6 = t.time()
    sma_fftconvolve = fftconvolve_sma(close, PERIOD)
    t7 = t.time()

    storage.elapsed[-1] = storage.elapsed[-1] + t2-t1
    storage.elapsed[-2] = storage.elapsed[-2] + t3-t2
    storage.elapsed[-3] = storage.elapsed[-3] + t4-t3
    storage.elapsed[-4] = storage.elapsed[-4] + t5-t4
    storage.elapsed[-5] = storage.elapsed[-5] + t6-t5    
    storage.elapsed[-6] = storage.elapsed[-6] + t7-t6        

    plot('sma_api', sma_api)  
    plot('sma_cumsum', sma_cumsum[-5])
    plot('sma_pandas', sma_pandas[-10])
    plot('sma_talib', sma_talib[-15])
    plot('sma_convolve', sma_convolve[-20])    
    plot('sma_fftconvolve', sma_fftconvolve[-25])

def stop():

    log('ticks....: %s' % info.max_ticks)

    log('api......: %.5f' % storage.elapsed[-1])
    log('cumsum...: %.5f' % storage.elapsed[-2])
    log('pandas...: %.5f' % storage.elapsed[-3])
    log('talib....: %.5f' % storage.elapsed[-4])
    log('convolve.: %.5f' % storage.elapsed[-5])    
    log('fft......: %.5f' % storage.elapsed[-6])

결과 :

[2015-01-31 23:00:00] ticks....: 744
[2015-01-31 23:00:00] api......: 0.16445
[2015-01-31 23:00:00] cumsum...: 0.03189
[2015-01-31 23:00:00] pandas...: 0.03677
[2015-01-31 23:00:00] talib....: 0.00700  # <<< Winner!
[2015-01-31 23:00:00] convolve.: 0.04871
[2015-01-31 23:00:00] fft......: 0.22306

여기에 이미지 설명을 입력하십시오


NameError: name 'info' is not defined. 이 오류가 발생합니다.
Md. Rezwanul Haque

1
스무딩 후 시계열이 이동 한 것처럼 보입니다. 원하는 효과입니까?
mrgloom

시각화 목적으로 @mrgloom 그렇습니다. 그렇지 않으면 차트에서 한 줄로 나타납니다. Ms. Rezwanul Haque PAIR 및 정보에 대한 모든 참조를 제거 할 수 있습니다. 그것들은 현재 소멸 된 tradewave.net에 대한 내부 샌드 박스 방법이었습니다
litepresence

21

바로 사용 가능한 솔루션은 https://scipy-cookbook.readthedocs.io/items/SignalSmooth.html을 참조 하십시오 . flat창 유형에 평균을 제공 합니다. 이것은 간단한 do-it-self-convolve-method보다 약간 더 정교합니다. 데이터를 처음부터 끝까지 반영하여 데이터를 반영하려고하기 때문에 (귀하의 경우에는 작동하지 않을 수도 있습니다.) ..).

우선 다음을 시도해보십시오.

a = np.random.random(100)
plt.plot(a)
b = smooth(a, window='flat')
plt.plot(b)

1
이 방법은 numpy.convolve순서를 변경하는 것만 다른 점 에 의존 합니다.
Alleo

10
나는 입력과 출력이 모두 같은 성질을 가질 때 입력 신호와 다른 모양의 출력 신호를 반환하는 신호 처리 기능에 항상 짜증이납니다 (예 : 두 시간 신호). 플로팅이나 비교가 직접적인 문제가 아닌 관련 독립 변수 (예 : 시간, 빈도)와의 일치를 끊습니다 ... 어쨌든 느낌을 공유하면 제안 된 함수의 마지막 줄을 y = np로 변경하고 싶을 수 있습니다 .convolve (w / w.sum (), s, mode = '같은'); y [window_len-1 :-( window_len-1)] 반환
Christian O'Reilly

@ ChristianO'Reilly, 당신은 그것을 별도의 답변으로 게시해야합니다. 매끄럽게 된 데이터의 길이와 일치 해야하는 두 개의 다른 배열이 있기 때문에 내가 찾고있는 것과 정확히 일치해야합니다. 정확히 어떻게 했 습니까? w창 크기와 s데이터입니까?
Demis

@Demis 댓글이 도움이되어 기쁘다. numpy convolve 함수에 대한 자세한 내용은 여기 docs.scipy.org/doc/numpy-1.15.0/reference/generated/… convolution 함수 ( en.wikipedia.org/wiki/Convolution )는 두 신호를 서로 연결합니다. 이 경우 신호가 정규화 된 (즉, 단일 영역) 창 (w / w.sum ())과 연결됩니다.
Christian O'Reilly

21

scipy.ndimage.filters.uniform_filter1d 를 사용할 수 있습니다 .

import numpy as np
from scipy.ndimage.filters import uniform_filter1d
N = 1000
x = np.random.random(100000)
y = uniform_filter1d(x, size=N)

uniform_filter1d:

  • 동일한 numpy 모양 (즉, 포인트 수)으로 출력을 제공합니다.
  • 여러 가지 방법으로 테두리를 'reflect'기본값 으로 처리 할 수 있지만 제 경우에는 오히려 원했습니다.'nearest'

또한 다소 빠릅니다 ( 위의 cumsum 접근법 보다 거의 50 배 빠르며 np.convolve2-5 배 빠름 ).

%timeit y1 = np.convolve(x, np.ones((N,))/N, mode='same')
100 loops, best of 3: 9.28 ms per loop

%timeit y2 = uniform_filter1d(x, size=N)
10000 loops, best of 3: 191 µs per loop

다음은 다양한 구현의 오류 / 속도를 비교할 수있는 3 가지 기능입니다.

from __future__ import division
import numpy as np
import scipy.ndimage.filters as ndif
def running_mean_convolve(x, N):
    return np.convolve(x, np.ones(N) / float(N), 'valid')
def running_mean_cumsum(x, N):
    cumsum = np.cumsum(np.insert(x, 0, 0))
    return (cumsum[N:] - cumsum[:-N]) / float(N)
def running_mean_uniform_filter1d(x, N):
    return ndif.uniform_filter1d(x, N, mode='constant', origin=-(N//2))[:-(N-1)]

1
이것은 국경 문제를 고려하는 유일한 대답입니다 (특히 플롯 할 때 중요합니다). 감사합니다!
Gabriel

1
난 프로파일 uniform_filter1d, np.convolve직사각형으로 한 np.cumsum다음 np.subtract. 내 결과 : (1) convolve가 가장 느립니다. (2.) cumsum / subtract는 약 20-30 배 빠릅니다. (3.) uniform_filter1d는 cumsum / subtract보다 약 2-3 배 빠릅니다. 승자는 분명히 uniform_filter1d입니다.
Trevor Boyd Smith

사용하는 uniform_filter1d것이 솔루션 보다 빠릅니다cumsum (약 2-5x). 와 uniform_filter1d 같은 대규모 부동 소수점 오류가 발생하지 않는cumsum 솔루션 않습니다.
Trevor Boyd Smith

15

나는 이것이 오래된 질문이라는 것을 알고 있지만 여기에 여분의 데이터 구조 또는 라이브러리를 사용하지 않는 솔루션이 있습니다. 입력 목록의 요소 수는 선형이며 더 효율적으로 만드는 다른 방법은 생각할 수 없습니다 (실제로 누군가가 결과를 할당하는 더 좋은 방법을 알고 있다면 알려주십시오).

참고 : 이것은 목록 대신 numpy 배열을 사용하는 것이 훨씬 빠르지 만 모든 종속성을 제거하고 싶었습니다. 멀티 스레드 실행으로 성능을 향상시킬 수도 있습니다

이 함수는 입력 목록이 1 차원이라고 가정하므로주의하십시오.

### Running mean/Moving average
def running_mean(l, N):
    sum = 0
    result = list( 0 for x in l)

    for i in range( 0, N ):
        sum = sum + l[i]
        result[i] = sum / (i+1)

    for i in range( N, len(l) ):
        sum = sum - l[i-N] + l[i]
        result[i] = sum / N

    return result

우리는 목록이 있다고 가정 data = [ 1, 2, 3, 4, 5, 6 ]우리가 3의 기간 롤링 평균을 계산하고자하는, 그리고 당신은 또한 입력 한 같은 크기의 출력 목록을 원하는 (즉, 가장 빈번 경우).

첫 번째 요소에는 인덱스 0이 있으므로 롤링 평균은 인덱스 -2, -1 및 0의 요소에서 계산되어야합니다. 분명히 데이터 [-2] 및 데이터 [-1]이 없습니다 (특별하게 사용하지 않으려는 경우 제외) 경계 조건), 따라서 우리는 해당 요소가 0이라고 가정합니다. 이것은 실제로 패딩하지 않고 패딩이 필요한 인덱스 (0에서 N-1까지)를 추적하는 것을 제외하고는 목록을 0으로 채우는 것과 같습니다.

따라서 첫 번째 N 요소의 경우 누적기에 요소를 계속 추가합니다.

result[0] = (0 + 0 + 1) / 3  = 0.333    ==   (sum + 1) / 3
result[1] = (0 + 1 + 2) / 3  = 1        ==   (sum + 2) / 3
result[2] = (1 + 2 + 3) / 3  = 2        ==   (sum + 3) / 3

요소 N + 1부터 간단한 누적이 작동하지 않습니다. 우리는 기대 result[3] = (2 + 3 + 4)/3 = 3하지만 이것은와 다릅니다 (sum + 4)/3 = 3.333.

올바른 값을 계산하는 방법은 빼기하는 것입니다 data[0] = 1로부터 sum+4따라서 제공 sum + 4 - 1 = 9.

이것은 현재이므로 발생 sum = data[0] + data[1] + data[2]하지만 i >= N뺄셈 전에가이므로 모든 경우에도 마찬가지 sum입니다 data[i-N] + ... + data[i-2] + data[i-1].


12

병목 현상을 사용하여 우아하게 해결할 수 있다고 생각합니다.

아래의 기본 샘플을 참조하십시오.

import numpy as np
import bottleneck as bn

a = np.random.randint(4, 1000, size=100)
mm = bn.move_mean(a, window=5, min_count=1)
  • "mm"은 "a"의 이동 평균입니다.

  • "창"은 이동 평균에 대해 고려해야 할 최대 항목 수입니다.

  • "min_count"는 이동 평균 (예 : 처음 몇 요소 또는 배열에 nan 값이있는 경우)으로 고려해야 할 최소 항목 수입니다.

좋은 점은 병목 현상이 난 값을 처리하는 데 도움이되며 매우 효율적이라는 것입니다.


이 lib는 정말 빠릅니다. 순수한 파이썬 이동 평균 함수는 느립니다. Bootleneck은 PyData 라이브러리이며, 안정적이라고 생각하고 Python 커뮤니티의 지속적인 지원을받을 수 있습니다. 왜 사용하지 않습니까?
GoingMyWay

6

나는 이것이 얼마나 빠른지 아직 확인하지 않았지만 시도해 볼 수 있습니다.

from collections import deque

cache = deque() # keep track of seen values
n = 10          # window size
A = xrange(100) # some dummy iterable
cum_sum = 0     # initialize cumulative sum

for t, val in enumerate(A, 1):
    cache.append(val)
    cum_sum += val
    if t < n:
        avg = cum_sum / float(t)
    else:                           # if window is saturated,
        cum_sum -= cache.popleft()  # subtract oldest value
        avg = cum_sum / float(n)

1
이것이 내가 할 일입니다. 왜 이것이 나쁜 길인지 비판 할 수 있습니까?
staggart

1
이 간단한 파이썬 솔루션은 numpy를 요구하지 않고 나에게 잘 작동했습니다. 결국 재사용을 위해 클래스에 넣었습니다.
Matthew Tschiegg

6

이 답변에는 세 가지 시나리오에 Python 표준 라이브러리 를 사용하는 솔루션이 포함되어 있습니다.


와 평균을 실행 itertools.accumulate

이것은 메모리를 활용 한 Python 3.2+ 솔루션으로를 활용하여 반복 가능한 값에 대한 평균을 계산합니다 itertools.accumulate.

>>> from itertools import accumulate
>>> values = range(100)

values발전기 또는 즉시 값을 생성하는 다른 객체를 포함한 모든 반복 가능한 수있다.

먼저 값의 누적 합계를 느리게 구성하십시오.

>>> cumu_sum = accumulate(value_stream)

그런 다음 enumerate누적 합계 (1부터 시작)를 누적 값의 비율과 현재 열거 인덱스를 생성하는 생성기를 구성합니다.

>>> rolling_avg = (accu/i for i, accu in enumerate(cumu_sum, 1))

means = list(rolling_avg)메모리의 모든 값을 한 번에 필요하거나 next증 분식으로 호출하면 문제가 발생할 수 있습니다 .
(물론, 당신은 또한 반복 할 수 rolling_avgfor호출 루프, next암시.)

>>> next(rolling_avg) # 0/1
>>> 0.0
>>> next(rolling_avg) # (0 + 1)/2
>>> 0.5
>>> next(rolling_avg) # (0 + 1 + 2)/3
>>> 1.0

이 솔루션은 다음과 같이 함수로 작성할 수 있습니다.

from itertools import accumulate

def rolling_avg(iterable):
    cumu_sum = accumulate(iterable)
    yield from (accu/i for i, accu in enumerate(cumu_sum, 1))
    

언제든지 값을 보낼 수 있는 코 루틴

이 코 루틴은 전송 한 값을 소비하고 지금까지 본 값의 평균을 유지합니다.

반복 가능한 값이 없지만 프로그램 수명 동안 다른 시간에 하나씩 평균값을 구할 때 유용합니다.

def rolling_avg_coro():
    i = 0
    total = 0.0
    avg = None

    while True:
        next_value = yield avg
        i += 1
        total += next_value
        avg = total/i
        

코 루틴은 다음과 같이 작동합니다.

>>> averager = rolling_avg_coro() # instantiate coroutine
>>> next(averager) # get coroutine going (this is called priming)
>>>
>>> averager.send(5) # 5/1
>>> 5.0
>>> averager.send(3) # (5 + 3)/2
>>> 4.0
>>> print('doing something else...')
doing something else...
>>> averager.send(13) # (5 + 3 + 13)/3
>>> 7.0

슬라이딩 윈도우 크기의 평균 계산 N

이 생성기 함수는 iterable 및 창 크기 N 를 사용하여 창 내부의 현재 값에 대한 평균을 산출합니다. 그것은 사용 deque목록과 유사하지만, 고속 변형 (최적화 된 자료 구조 인 pop, append) 양자 모두가 끝점 .

from collections import deque
from itertools import islice

def sliding_avg(iterable, N):        
    it = iter(iterable)
    window = deque(islice(it, N))        
    num_vals = len(window)

    if num_vals < N:
        msg = 'window size {} exceeds total number of values {}'
        raise ValueError(msg.format(N, num_vals))

    N = float(N) # force floating point division if using Python 2
    s = sum(window)
    
    while True:
        yield s/N
        try:
            nxt = next(it)
        except StopIteration:
            break
        s = s - window.popleft() + nxt
        window.append(nxt)
        

작동하는 기능은 다음과 같습니다.

>>> values = range(100)
>>> N = 5
>>> window_avg = sliding_avg(values, N)
>>> 
>>> next(window_avg) # (0 + 1 + 2 + 3 + 4)/5
>>> 2.0
>>> next(window_avg) # (1 + 2 + 3 + 4 + 5)/5
>>> 3.0
>>> next(window_avg) # (2 + 3 + 4 + 5 + 6)/5
>>> 4.0

5

파티에 조금 늦었지만 끝이나 패드 주위를 감싸지 않는 제 자신의 작은 기능을 만들어 평균을 찾는 데 사용했습니다. 추가 처리는 선형 간격의 지점에서 신호를 다시 샘플링하는 것입니다. 다른 기능을 사용하려면 마음대로 코드를 사용자 정의하십시오.

이 방법은 정규화 된 가우스 커널을 사용한 간단한 행렬 곱셈입니다.

def running_mean(y_in, x_in, N_out=101, sigma=1):
    '''
    Returns running mean as a Bell-curve weighted average at evenly spaced
    points. Does NOT wrap signal around, or pad with zeros.

    Arguments:
    y_in -- y values, the values to be smoothed and re-sampled
    x_in -- x values for array

    Keyword arguments:
    N_out -- NoOf elements in resampled array.
    sigma -- 'Width' of Bell-curve in units of param x .
    '''
    N_in = size(y_in)

    # Gaussian kernel
    x_out = np.linspace(np.min(x_in), np.max(x_in), N_out)
    x_in_mesh, x_out_mesh = np.meshgrid(x_in, x_out)
    gauss_kernel = np.exp(-np.square(x_in_mesh - x_out_mesh) / (2 * sigma**2))
    # Normalize kernel, such that the sum is one along axis 1
    normalization = np.tile(np.reshape(sum(gauss_kernel, axis=1), (N_out, 1)), (1, N_in))
    gauss_kernel_normalized = gauss_kernel / normalization
    # Perform running average as a linear operation
    y_out = gauss_kernel_normalized @ y_in

    return y_out, x_out

정규 분포 노이즈가 추가 된 정현파 신호에 대한 간단한 사용법 : 여기에 이미지 설명을 입력하십시오


이것은 나를 위해 작동하지 않습니다 (파이썬 3.6). 1 라는 이름의 기능이 없다 sum사용 np.sum하는 대신 2@ 연산자 (뭔지 생각)에서 오류가 발생가. 나중에 살펴볼 수도 있지만 지금 시간이 부족합니다.
Bastian

@구현은 행렬 곱셈 연산자 np.matmul . y_in배열이 numpy 배열 인지 확인하면 문제가 될 수 있습니다.
xyzzyqed

5

numpy 또는 scipy 대신 팬더가 더 신속 하게이 작업을 수행하도록 권장합니다.

df['data'].rolling(3).mean()

이것은 "데이터"열의 3주기의 이동 평균 (MA)을 사용합니다. 시프트 된 버전을 계산할 수도 있습니다. 예를 들어 현재 셀을 제외하고 (뒤로 시프트 한) 버전은 다음과 같이 쉽게 계산할 수 있습니다.

df['data'].shift(periods=1).rolling(3).mean()

2016 년에 제안 된 솔루션 과 다른 점은 무엇입니까?
Mr. T

2
2016 년에 제안 된 솔루션은 pandas.rolling_mean내 사용 하는 동안 사용합니다 pandas.DataFrame.rolling. 이 방법 min(), max(), sum()으로뿐만 아니라 이동 등을 mean()쉽게 계산할 수도 있습니다.
Gursel Karacor 2016 년

전자에서 당신은 pandas.rolling_min, pandas.rolling_max등등 과 같은 다른 방법을 사용해야합니다 . 그것들은 비슷하지만 다릅니다.
Gursel Karacor 2016 년

4

이 방법이있는 위 의 답변 중 하나에 묻힌 mab 의 의견 이 있습니다. 다음은 단순한 이동 평균입니다.bottleneckmove_mean

import numpy as np
import bottleneck as bn

a = np.arange(10) + np.random.random(10)

mva = bn.move_mean(a, window=2, min_count=1)

min_count기본적으로 배열의 해당 지점까지 이동 평균을 취하는 편리한 매개 변수입니다. 설정하지 않으면 min_count같고 점 window까지 모든 것이됩니다 .windownan


3

numpy, panda 사용 하지 않고 이동 평균을 찾는 또 다른 방법

import itertools
sample = [2, 6, 10, 8, 11, 10]
list(itertools.starmap(lambda a,b: b/a, 
               enumerate(itertools.accumulate(sample), 1)))

인쇄합니다 [2.0, 4.0, 6.0, 6.5, 7.4, 7.833333333333333]


itertools.accumulate는 파이썬 2.7에 존재하지만, 파이썬 3.4에서와하지 않습니다
grayaii

3

이 질문은 NeXuS가 지난 달에 작성했을 때보 다 훨씬 오래 되었지만, 그의 코드가 엣지 케이스를 처리하는 방식이 마음에 듭니다. 그러나 "단순 이동 평균"이기 때문에 결과가 적용되는 데이터보다 뒤떨어집니다. 나는 NumPy와의 모드보다 더 만족하게 가장자리 경우 다루는 생각 valid, same그리고 fullA와 유사한 접근 방식을 적용함으로써 달성 될 수있는 convolution()기반 방법.

내 기여는 중앙 실행 평균을 사용하여 결과를 데이터와 일치시킵니다. 전체 크기 창을 사용할 수있는 포인트가 너무 적 으면 배열의 가장자리에있는 연속적으로 작은 창에서 실행 평균이 계산됩니다. [실제로 더 큰 창문에서 실제로는 구현 세부 사항입니다.]

import numpy as np

def running_mean(l, N):
    # Also works for the(strictly invalid) cases when N is even.
    if (N//2)*2 == N:
        N = N - 1
    front = np.zeros(N//2)
    back = np.zeros(N//2)

    for i in range(1, (N//2)*2, 2):
        front[i//2] = np.convolve(l[:i], np.ones((i,))/i, mode = 'valid')
    for i in range(1, (N//2)*2, 2):
        back[i//2] = np.convolve(l[-i:], np.ones((i,))/i, mode = 'valid')
    return np.concatenate([front, np.convolve(l, np.ones((N,))/N, mode = 'valid'), back[::-1]])

사용하기 때문에 상대적으로 느리며 convolve()진정한 Pythonista에 의해 상당히 많이 퍼질 수 있지만 아이디어는 유효하다고 생각합니다.


3

연속 평균 계산에 대한 위의 많은 답변이 있습니다. 내 대답은 두 가지 추가 기능을 추가합니다.

  1. nan 값을 무시합니다
  2. 관심 값 자체를 포함하지 않는 N 개의 주변 값에 대한 평균을 계산합니다.

이 두 번째 특징은 어떤 값이 일반적인 추세와 어떤 값으로 다른지 결정하는 데 특히 유용합니다.

numpy.cumsum이 가장 시간 효율적인 방법이므로 사용합니다 ( 위의 Alleo 's answer 참조 ).

N=10 # number of points to test on each side of point of interest, best if even
padded_x = np.insert(np.insert( np.insert(x, len(x), np.empty(int(N/2))*np.nan), 0, np.empty(int(N/2))*np.nan ),0,0)
n_nan = np.cumsum(np.isnan(padded_x))
cumsum = np.nancumsum(padded_x) 
window_sum = cumsum[N+1:] - cumsum[:-(N+1)] - x # subtract value of interest from sum of all values within window
window_n_nan = n_nan[N+1:] - n_nan[:-(N+1)] - np.isnan(x)
window_n_values = (N - window_n_nan)
movavg = (window_sum) / (window_n_values)

이 코드는 N까지만 작동합니다. padded_x 및 n_nan의 np.insert를 변경하여 홀수를 조정할 수 있습니다.

출력 예 (검정색으로, movavg는 파란색으로) : 각 값 주위의 원시 데이터 (검은 색) 및 이동 평균 (파란색)은 해당 값을 포함하지 않습니다.  nan 값은 무시됩니다.

이 코드는 컷오프 = 3이 아닌 값보다 작은 값으로 계산 된 모든 이동 평균 값을 제거하도록 쉽게 조정할 수 있습니다.

window_n_values = (N - window_n_nan).astype(float) # dtype must be float to set some values to nan
cutoff = 3
window_n_values[window_n_values<cutoff] = np.nan
movavg = (window_sum) / (window_n_values)

난이 아닌 값이 3보다 작은 창은 무시하고 원시 데이터 (검은 색) 및 이동 평균 (파란색)


2

Python 표준 라이브러리 만 사용 (메모리 효율적)

표준 라이브러리 deque만 사용하는 다른 버전을 제공하십시오 . 대부분의 답변이 pandas또는을 사용하고 있다는 것은 놀랍습니다 numpy.

def moving_average(iterable, n=3):
    d = deque(maxlen=n)
    for i in iterable:
        d.append(i)
        if len(d) == n:
            yield sum(d)/n

r = moving_average([40, 30, 50, 46, 39, 44])
assert list(r) == [40.0, 42.0, 45.0, 43.0]

실제로 파이썬 문서 에서 다른 구현을 찾았습니다.

def moving_average(iterable, n=3):
    # moving_average([40, 30, 50, 46, 39, 44]) --> 40.0 42.0 45.0 43.0
    # http://en.wikipedia.org/wiki/Moving_average
    it = iter(iterable)
    d = deque(itertools.islice(it, n-1))
    d.appendleft(0)
    s = sum(d)
    for elem in it:
        s += elem - d.popleft()
        d.append(elem)
        yield s / n

그러나 구현은 나에게 좀 더 복잡해 보입니다. 그러나 이유는 표준 파이썬 문서에 있어야합니다. 누군가가 내 표준 및 표준 문서의 구현에 대해 언급 할 수 있습니까?


2
반복 할 때마다 창 멤버를 합산하는 한 가지 큰 차이점은 효율적으로 합계를 업데이트합니다 (한 멤버를 제거하고 다른 멤버를 추가). 복잡성 측면에서 O(n*d) 계산 ( dn크기, 반복 가능 크기)을 수행하고 있습니다.O(n)
Iftah

@Iftah, 설명해 주셔서 감사합니다. 맞습니다.
MaThMaX

2

@ Aikude의 변수를 사용하여 하나의 라이너를 작성했습니다.

import numpy as np

mylist = [1, 2, 3, 4, 5, 6, 7]
N = 3

mean = [np.mean(mylist[x:x+N]) for x in range(len(mylist)-N+1)]
print(mean)

>>> [2.0, 3.0, 4.0, 5.0, 6.0]

1

이 질문에 대한 해결책이 있지만 내 해결책을 살펴보십시오. 매우 간단하고 잘 작동합니다.

import numpy as np
dataset = np.asarray([1, 2, 3, 4, 5, 6, 7])
ma = list()
window = 3
for t in range(0, len(dataset)):
    if t+window <= len(dataset):
        indices = range(t, t+window)
        ma.append(np.average(np.take(dataset, indices)))
else:
    ma = np.asarray(ma)

1

다른 답변을 읽음으로써 나는 이것이 질문에 대한 것이라고 생각하지 않지만 크기가 커지고있는 값 목록의 평균을 유지할 필요가 있습니다.

따라서 사이트, 측정 장치 등에서 수집 한 값 목록과 마지막으로 n업데이트 된 평균 값을 유지하려는 경우 새 코드 추가 노력을 최소화하는 다음 코드를 사용할 수 있습니다. 집단:

class Running_Average(object):
    def __init__(self, buffer_size=10):
        """
        Create a new Running_Average object.

        This object allows the efficient calculation of the average of the last
        `buffer_size` numbers added to it.

        Examples
        --------
        >>> a = Running_Average(2)
        >>> a.add(1)
        >>> a.get()
        1.0
        >>> a.add(1)  # there are two 1 in buffer
        >>> a.get()
        1.0
        >>> a.add(2)  # there's a 1 and a 2 in the buffer
        >>> a.get()
        1.5
        >>> a.add(2)
        >>> a.get()  # now there's only two 2 in the buffer
        2.0
        """
        self._buffer_size = int(buffer_size)  # make sure it's an int
        self.reset()

    def add(self, new):
        """
        Add a new number to the buffer, or replaces the oldest one there.
        """
        new = float(new)  # make sure it's a float
        n = len(self._buffer)
        if n < self.buffer_size:  # still have to had numbers to the buffer.
            self._buffer.append(new)
            if self._average != self._average:  # ~ if isNaN().
                self._average = new  # no previous numbers, so it's new.
            else:
                self._average *= n  # so it's only the sum of numbers.
                self._average += new  # add new number.
                self._average /= (n+1)  # divide by new number of numbers.
        else:  # buffer full, replace oldest value.
            old = self._buffer[self._index]  # the previous oldest number.
            self._buffer[self._index] = new  # replace with new one.
            self._index += 1  # update the index and make sure it's...
            self._index %= self.buffer_size  # ... smaller than buffer_size.
            self._average -= old/self.buffer_size  # remove old one...
            self._average += new/self.buffer_size  # ...and add new one...
            # ... weighted by the number of elements.

    def __call__(self):
        """
        Return the moving average value, for the lazy ones who don't want
        to write .get .
        """
        return self._average

    def get(self):
        """
        Return the moving average value.
        """
        return self()

    def reset(self):
        """
        Reset the moving average.

        If for some reason you don't want to just create a new one.
        """
        self._buffer = []  # could use np.empty(self.buffer_size)...
        self._index = 0  # and use this to keep track of how many numbers.
        self._average = float('nan')  # could use np.NaN .

    def get_buffer_size(self):
        """
        Return current buffer_size.
        """
        return self._buffer_size

    def set_buffer_size(self, buffer_size):
        """
        >>> a = Running_Average(10)
        >>> for i in range(15):
        ...     a.add(i)
        ...
        >>> a()
        9.5
        >>> a._buffer  # should not access this!!
        [10.0, 11.0, 12.0, 13.0, 14.0, 5.0, 6.0, 7.0, 8.0, 9.0]

        Decreasing buffer size:
        >>> a.buffer_size = 6
        >>> a._buffer  # should not access this!!
        [9.0, 10.0, 11.0, 12.0, 13.0, 14.0]
        >>> a.buffer_size = 2
        >>> a._buffer
        [13.0, 14.0]

        Increasing buffer size:
        >>> a.buffer_size = 5
        Warning: no older data available!
        >>> a._buffer
        [13.0, 14.0]

        Keeping buffer size:
        >>> a = Running_Average(10)
        >>> for i in range(15):
        ...     a.add(i)
        ...
        >>> a()
        9.5
        >>> a._buffer  # should not access this!!
        [10.0, 11.0, 12.0, 13.0, 14.0, 5.0, 6.0, 7.0, 8.0, 9.0]
        >>> a.buffer_size = 10  # reorders buffer!
        >>> a._buffer
        [5.0, 6.0, 7.0, 8.0, 9.0, 10.0, 11.0, 12.0, 13.0, 14.0]
        """
        buffer_size = int(buffer_size)
        # order the buffer so index is zero again:
        new_buffer = self._buffer[self._index:]
        new_buffer.extend(self._buffer[:self._index])
        self._index = 0
        if self._buffer_size < buffer_size:
            print('Warning: no older data available!')  # should use Warnings!
        else:
            diff = self._buffer_size - buffer_size
            print(diff)
            new_buffer = new_buffer[diff:]
        self._buffer_size = buffer_size
        self._buffer = new_buffer

    buffer_size = property(get_buffer_size, set_buffer_size)

예를 들어 다음과 같이 테스트 할 수 있습니다.

def graph_test(N=200):
    import matplotlib.pyplot as plt
    values = list(range(N))
    values_average_calculator = Running_Average(N/2)
    values_averages = []
    for value in values:
        values_average_calculator.add(value)
        values_averages.append(values_average_calculator())
    fig, ax = plt.subplots(1, 1)
    ax.plot(values, label='values')
    ax.plot(values_averages, label='averages')
    ax.grid()
    ax.set_xlim(0, N)
    ax.set_ylim(0, N)
    fig.show()

다음을 제공합니다.

값과 값의 함수로서의 평균


1

표준 라이브러리를 사용하고 deque를 사용하는 또 다른 솔루션 :

from collections import deque
import itertools

def moving_average(iterable, n=3):
    # http://en.wikipedia.org/wiki/Moving_average
    it = iter(iterable) 
    # create an iterable object from input argument
    d = deque(itertools.islice(it, n-1))  
    # create deque object by slicing iterable
    d.appendleft(0)
    s = sum(d)
    for elem in it:
        s += elem - d.popleft()
        d.append(elem)
        yield s / n

# example on how to use it
for i in  moving_average([40, 30, 50, 46, 39, 44]):
    print(i)

# 40.0
# 42.0
# 45.0
# 43.0

1

교육 목적으로 Numpy 솔루션을 두 개 더 추가합니다 (cumsum 솔루션보다 느림).

import numpy as np
from numpy.lib.stride_tricks import as_strided

def ra_strides(arr, window):
    ''' Running average using as_strided'''
    n = arr.shape[0] - window + 1
    arr_strided = as_strided(arr, shape=[n, window], strides=2*arr.strides)
    return arr_strided.mean(axis=1)

def ra_add(arr, window):
    ''' Running average using add.reduceat'''
    n = arr.shape[0] - window + 1
    indices = np.array([0, window]*n) + np.repeat(np.arange(n), 2)
    arr = np.append(arr, 0)
    return np.add.reduceat(arr, indices )[::2]/window

사용 된 함수 : as_strided , add.reduceat


1

위에서 언급 한 모든 솔루션은 부족하기 때문에 좋지 않습니다.

  • numpy 벡터화 구현 대신 기본 Python으로 인한 속도
  • 을 잘못 사용하여 수치 적 안정성 numpy.cumsum또는
  • O(len(x) * w)컨볼 루션으로서의 구현으로 인한 속도 .

주어진

import numpy
m = 10000
x = numpy.random.rand(m)
w = 1000

이 (가) x_[:w].sum()같습니다 x[:w-1].sum(). 따라서 첫 번째 평균의 경우 numpy.cumsum(...)더하기 x[w] / w(via x_[w+1] / w) 및 빼기 0(from x_[0] / w). 결과x[0:w].mean()

cumsum을 통해을 x[w+1] / w더하고 빼서 두 번째 평균을 업데이트하여 x[0] / w결과를 얻습니다 x[1:w+1].mean().

이것은 x[-w:].mean()도달 할 때까지 계속됩니다 .

x_ = numpy.insert(x, 0, 0)
sliding_average = x_[:w].sum() / w + numpy.cumsum(x_[w:] - x_[:-w]) / w

이 솔루션은 벡터화되고 O(m)읽기 쉽고 수치 적으로 안정적입니다.


1

방법에 대한 이동 평균 필터 ? 또한 하나의 라이너이며 직사각형 이외의 다른 것이 필요한 경우 창 유형을 쉽게 조작 할 수 있다는 이점이 있습니다. 배열의 N- 길이 간단한 이동 평균 a :

lfilter(np.ones(N)/N, [1], a)[N:]

그리고 삼각형 창이 적용된 상태에서 :

lfilter(np.ones(N)*scipy.signal.triang(N)/N, [1], a)[N:]

참고 : 나는 보통 처음 N 샘플을 가짜로 버립니다. [N:]그러나 그것은 필요하지 않으며 개인적인 선택의 문제 일뿐입니다.


-7

기존 라이브러리를 사용하지 않고 자신의 롤을 선택하는 경우 부동 소수점 오류를 염두에두고 그 효과를 최소화하십시오.

class SumAccumulator:
    def __init__(self):
        self.values = [0]
        self.count = 0

    def add( self, val ):
        self.values.append( val )
        self.count = self.count + 1
        i = self.count
        while i & 0x01:
            i = i >> 1
            v0 = self.values.pop()
            v1 = self.values.pop()
            self.values.append( v0 + v1 )

    def get_total(self):
        return sum( reversed(self.values) )

    def get_size( self ):
        return self.count

모든 값이 대략 같은 크기의 순서 인 경우, 거의 비슷한 크기의 값을 항상 추가하여 정밀도를 유지하는 데 도움이됩니다.


15
이것은 코드에서 약간의 주석이나 이것이 부동 소수점 오류에 도움이되는 이유에 대한 설명 중 아주 분명하지 않은 대답입니다.
Gabe

마지막 문장에서 부동 소수점 오류에 도움이되는 이유를 나타내려고했습니다. 두 값의 크기가 거의 동일한 경우, 값을 추가하면 아주 작은 숫자를 추가 할 때보 다 정밀도가 떨어집니다. 이 코드는 "인접한"값을 결합하여 부동 소수점 오류를 최소화하기 위해 중간 합도 항상 합당한 크기를 갖도록해야합니다. 아무 것도 확실한 증거는 아니지만이 방법을 사용하면 프로덕션 환경에서 제대로 구현되지 않은 몇 가지 프로젝트를 구할 수 있습니다.
Mayur Patel

1. 원래 문제에 적용되고, 이것은 매우 느리게 (컴퓨팅 평균), 따라서 이것은 관련이 없습니다 2. 64 비트 숫자의 정밀도 문제로 고통 받기 위해서는 거의 2 >> 30 같은 숫자.
Alleo

@Alleo : 가치 당 하나의 덧셈을하는 대신 두 가지를하게됩니다. 증거는 비트 크 래핑 문제와 동일합니다. 그러나이 답변의 핵심은 반드시 성능이 아니라 정확성입니다. 64 비트 값을 평균화하기위한 메모리 사용량은 캐시에서 64 개 요소를 초과하지 않으므로 메모리 사용량에도 친숙합니다.
Mayur Patel

예, 이것이 단순한 합계보다 2 배 더 많은 작업이 필요하다는 것은 옳습니다. 그러나 원래 문제는 합계뿐만 아니라 계산 실행 평균 입니다. O (n)에서 수행 할 수 있지만 대답에는 O (mn)이 필요합니다. 여기서 m은 창 크기입니다.
Alleo
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.