이동 평균 또는 평균 특정 창에서 주어진 1D 배열의

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



답변

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

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)

답변

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


답변

효율적인 솔루션

컨볼 루션은 간단한 접근법보다 훨씬 낫지 만 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 솔루션보다 빠르며 부동 소수점 정밀도 손실이 없습니다.

답변

업데이트 : 아래 예 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

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


답변

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

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'시작 일시적 문제를 해결해야하는 것처럼 보이는 모드 가 포함되어 있지만 시작과 끝으로 분할됩니다.


답변

또는 파이썬을위한 모듈

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


답변

바로 사용 가능한 솔루션은 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)