NumPy kullanarak hareketli ortalama nasıl hesaplanır?


111

Uyuşuk / scipy'deki hareketli ortalamayı basitçe hesaplayan ve kıvrımlı çözümlere yol açan bir işlev yok gibi görünüyor .

Sorum iki yönlü:

  • Numpy ile hareketli bir ortalamayı (doğru) uygulamanın en kolay yolu nedir?
  • Bu önemsiz ve hataya açık göründüğünden, pillerin bu durumda dahil edilmemesi için iyi bir neden var mı?

19
Evrişim çözümü bana o kadar karmaşık görünmüyor!
2013

4
Hareketli ortalama sadece bir alçak geçiren filtre değil midir (yani 'bulanıklık')? Eminim ki bu, evrişimin tam olarak kastedildiği türden bir şey ...
user541686

@mmgp Sanırım yanılmayı umuyordum ya da iyi, bariz bir neden vardı.
goncalopp

3
@wim Yarım bir kelime oyunu olarak kastedildi. Ancak sorunun var olduğu gerçeği, numpy.convolute'tan hareketli bir ortalama oluşturmanın kolay olmadığı anlamına gelir.
goncalopp

Yanıtlar:


167

Sadece basit istiyorsanız ortalama hareketli olmayan ağırlıklı, kolayca ile bunu uygulayabilirsiniz np.cumsumhangi olabilir olan hızlı FFT daha temelli yöntemler:

DÜZENLE Bean tarafından kodda tespit edilen bir defaya mahsus yanlış indeksleme düzeltildi. DÜZENLE

def moving_average(a, n=3) :
    ret = np.cumsum(a, dtype=float)
    ret[n:] = ret[n:] - ret[:-n]
    return ret[n - 1:] / n

>>> a = np.arange(20)
>>> moving_average(a)
array([  1.,   2.,   3.,   4.,   5.,   6.,   7.,   8.,   9.,  10.,  11.,
        12.,  13.,  14.,  15.,  16.,  17.,  18.])
>>> moving_average(a, n=4)
array([  1.5,   2.5,   3.5,   4.5,   5.5,   6.5,   7.5,   8.5,   9.5,
        10.5,  11.5,  12.5,  13.5,  14.5,  15.5,  16.5,  17.5])

Bu yüzden sanırım cevap şu: Uygulanması gerçekten çok kolay ve belki de numpy zaten özel işlevlerle biraz şişirilmiş durumda.


11
Bu kod yanlış. Örneğin, hareketli_ortalama ([1,2,5,10], n = 2) [1., 3.5, 8.5] verir. Yanıtlayanın, 0'dan 19'a kadar olan değerlerin hareketli ortalaması için test durumu bile yanlıştır ve 0, 1 ve 2 ortalamasının 0,5 olduğunu iddia eder. Nasıl 6 olumlu oy aldı?
JeremyKun

2
Hata kontrolü için teşekkürler, şimdi iyi çalışıyor gibi görünüyor. Olumlu oylara gelince, cevabın arkasındaki genel fikrin uygulamadaki bir defaya mahsus bir hatadan daha ağır olduğunu tahmin ediyorum, ancak kim bilir.
Jaime

3
Sorunu buldum. ret[n:] -= ret[:-n]AYNI DEĞİLDİR ret[n:] = ret[n:] - ret[:-n]. Bu cevaptaki kodu düzelttim. Düzenleme: Hayır, başka biri beni dövdü.
Timmmm

8
@Timmmm Yaptım, gerçekten sorun buydu. Bu cevabın arkasındaki genel ilke, görüntü işlemede yaygın olarak kullanılmaktadır (buna toplanmış alan tabloları dedikleri), bu nedenle konu uygulamada olmalıydı. Erken optimizasyon tarafından ısırılmanın iyi bir örneği, çünkü işlemi yerinde yaptığımı hatırlıyorum çünkü "daha verimli olacak." İşin iyi tarafı, muhtemelen yanlış cevabı daha hızlı verdi ...
Jaime

45
Hmmm, görünen o ki, bu "uygulaması kolay" işlev aslında yanlış anlaşılması oldukça kolay ve bellek verimliliği konusunda iyi bir tartışma başlattı. Bir şeyin doğru yapıldığını bilmek anlamına geliyorsa şişkinliğe sahip olduğum için mutluyum.
Richard

81

NumPy'nin belirli bir alana özgü işlev eksikliği, belki de Çekirdek Ekibin disiplini ve NumPy'nin ana direktifine olan sadakatinden kaynaklanmaktadır: N boyutlu bir dizi türü ve bu dizileri oluşturma ve indeksleme işlevleri sağlayın . Birçok temel amaç gibi, bu da küçük değildir ve NumPy bunu zekice yapar.

(Çok) daha büyük olan SciPy , çok daha geniş bir alana özgü kitaplık koleksiyonunu içerir ( SciPy geliştiricileri tarafından alt paketler olarak adlandırılır ) - örneğin, sayısal optimizasyon ( optimize ), sinyal işleme ( sinyal ) ve integral hesaplama ( integral ).

Benim tahminim, peşinde olduğunuz işlev SciPy alt paketlerinden en az birinde ( belki scipy.signal ); ancak, önce SciPy scikitlerinin koleksiyonuna bakar , ilgili scikit (ler) i tanımlar ve oradaki ilgili işlevi arardım.

Scikits bağımsız belirli bir teknik disiplin (örneğin üzere NumPy / SciPy dayalı paketleri geliştirdi ve yönlendirilir scikits-görüntü , scikits-öğrenme (özellikle müthiş Bunlardan birkaç vs.) OpenOpt , sayısal optimizasyon için) saygın edildi nispeten yeni scikits değerlendirme tablosu altında kalmayı seçmeden çok önce olgun projeler . Scikits 30 tür yaklaşık Yukarıdaki listelere sevdim Anasayfa scikits en az olanların birkaç artık aktif geliştirilme aşamasındadır olsa.

Bu tavsiyeye uymanız sizi scikits zaman serilerine götürecektir ; ancak, bu paket artık aktif geliştirme aşamasındadır; Aslında Pandalar , AFAIK, fiilen NumPy tabanlı zaman serisi kitaplığı haline geldi .

Pandaların hareketli ortalamayı hesaplamak için kullanılabilecek çeşitli işlevleri vardır ; bunların en basiti muhtemelen şu şekilde kullandığınız yuvarlanan_ortadır :

>>> # the recommended syntax to import pandas
>>> import pandas as PD
>>> import numpy as NP

>>> # prepare some fake data:
>>> # the date-time indices:
>>> t = PD.date_range('1/1/2010', '12/31/2012', freq='D')

>>> # the data:
>>> x = NP.arange(0, t.shape[0])

>>> # combine the data & index into a Pandas 'Series' object
>>> D = PD.Series(x, t)

Şimdi, sadece Series nesnesinde geçen rolling_mean işlevini ve aşağıdaki örneğimde 10 gün olan bir pencere boyutunu çağırın .

>>> d_mva = PD.rolling_mean(D, 10)

>>> # d_mva is the same size as the original Series
>>> d_mva.shape
    (1096,)

>>> # though obviously the first w values are NaN where w is the window size
>>> d_mva[:3]
    2010-01-01         NaN
    2010-01-02         NaN
    2010-01-03         NaN

çalıştığını doğrulayın - örneğin, orijinal serideki 10 - 15 arasındaki değerleri yuvarlanan ortalama ile yumuşatılmış yeni Seri ile karşılaştırdı

>>> D[10:15]
     2010-01-11    2.041076
     2010-01-12    2.041076
     2010-01-13    2.720585
     2010-01-14    2.720585
     2010-01-15    3.656987
     Freq: D

>>> d_mva[10:20]
      2010-01-11    3.131125
      2010-01-12    3.035232
      2010-01-13    2.923144
      2010-01-14    2.811055
      2010-01-15    2.785824
      Freq: D

Rolling_mean işlevi, yaklaşık bir düzine kadar başka işlevle birlikte Pandas belgelerinde, rubrik hareketli pencere işlevleri altında gayri resmi olarak gruplandırılmıştır ; Pandalar'daki ikinci, ilgili işlevler grubu üstel ağırlıklı işlevler olarak adlandırılır (örneğin, üssel olarak hareketli ağırlıklı ortalamayı hesaplayan ewma ). Bu ikinci grubun birinci gruba ( hareketli pencere fonksiyonları) dahil olmaması , belki de üstel ağırlıklı dönüşümlerin sabit uzunlukta bir pencereye dayanmamasından kaynaklanmaktadır.


6
Pandaların güçlü bir hareketli pencere işlevleri dizisi vardır. Ama bana basit bir hareketli ortalama için biraz fazla ek yük gibi görünüyor.
Jaime

6
Pekala, hareketli bir ortalamanın hesaplanmasının OP için veya hemen hemen herkes için izole bir gereklilik olduğundan şüpheliyim. Hareketli bir ortalamayı hesaplamanız gerekiyorsa, neredeyse kesinlikle bir zaman serisine sahip olursunuz; bu, verilerinize bir tarih-saat indeksini uydurmanıza izin veren bir veri yapısına ihtiyacınız olduğu anlamına gelir ve bu, bahsettiğiniz 'ek yük'dür.
doug

2
Öncelikle, bu son derece bilgilendirici cevabı yazmaya zaman ayırdığınız için teşekkür ederiz. Aslında, bir zaman serisini içermeyen hareketli bir ortalamanın bir kullanımını göremiyorum. Ancak bu, kişinin bir tarih
saatine

3
Pandalar bir bağımlılık olarak çok ağır görünüyorsa , hareketli ortalama fonksiyonunun Darboğaz kütüphanesine çıkarıldığını eklemek istedim .
robochat

4
'rolling_mean' artık pf pandaların bir parçası değil, lütfen bunun yerine 'yuvarlanma' seçeneğini kullanarak
yanıta bakın

66

Bunu başarmanın basit bir yolu kullanmaktır np.convolve. Bunun arkasındaki fikir, ayrık evrişimin hesaplanma şeklinden yararlanmak ve onu bir yuvarlanan ortalama döndürmek için kullanmaktır . Bu, np.onesistediğimiz sürgülü pencere uzunluğuna eşit bir uzunlukta bir dizi ile bükülerek yapılabilir .

Bunu yapmak için aşağıdaki işlevi tanımlayabiliriz:

def moving_average(x, w):
    return np.convolve(x, np.ones(w), 'valid') / w

Bu fonksiyon, dizinin evrişimini xve uzunluktaki bir diziyi alacak w. Seçilen Not modeolan validkonvolüsyon ürün sadece sekanslar tamamen üst üste puan verilir, böylece.


Bazı örnekler:

x = np.array([5,3,8,10,2,1,5,1,0,2])

Bir pencere uzunluğuna sahip hareketli bir ortalama için sahip 2oluruz:

moving_average(x, 2)
# array([4. , 5.5, 9. , 6. , 1.5, 3. , 3. , 0.5, 1. ])

Ve uzun bir pencere için 4:

moving_average(x, 4)
# array([6.5 , 5.75, 5.25, 4.5 , 2.25, 1.75, 2.  ])

Nasıl convolveçalışır?

Ayrık evrişimin hesaplanma şekline daha derinlemesine bakalım. Aşağıdaki işlev np.convolve, çıktı değerlerini hesaplamanın yolunu kopyalamayı amaçlamaktadır :

def mov_avg(x, w):
    for m in range(len(x)-(w-1)):
        yield sum(np.ones(w) * x[m:m+w]) / w 

Yukarıdaki aynı örnek için aynı zamanda şunu da verir:

list(mov_avg(x, 2))
# [4.0, 5.5, 9.0, 6.0, 1.5, 3.0, 3.0, 0.5, 1.0]

Yani her adımda yapılan şey, iç çarpımı birler dizisi ile mevcut pencere arasına almaktır . Bu durumda, np.ones(w)doğrudan sumdiziyi aldığımız için çarpma işlemi gereksizdir .

Körük, biraz daha net olması için ilk çıktıların nasıl hesaplandığına bir örnektir. Bir pencere istediğimizi varsayalım w=4:

[1,1,1,1]
[5,3,8,10,2,1,5,1,0,2]
= (1*5 + 1*3 + 1*8 + 1*10) / w = 6.5

Ve aşağıdaki çıktı şu şekilde hesaplanacaktır:

  [1,1,1,1]
[5,3,8,10,2,1,5,1,0,2]
= (1*3 + 1*8 + 1*10 + 1*2) / w = 5.75

Ve böylece, tüm örtüşmeler gerçekleştirildikten sonra dizinin hareketli bir ortalamasını döndürür.


1
Bu güzel bir fikir! @ Jaime'nin küçük n cevabından daha hızlıdır, ancak daha büyük n için daha yavaş olur.
Felipe Gerard

Teşekkürler @FelipeGerard! Evet, yorumlarda da belirtildiği gibi, bu yaklaşım diğer bazı uyuşuk çözümler kadar verimli olmayabilir, ancak sadeliği ve
özlülüğü

1
Bazen çıktı dizisinin girdi ile aynı boyutta olması yararlıdır. Bunun için mode='valid'değiştirilebilir 'same'. Sadece bu durumda kenar noktaları sıfıra doğru çekilecektir.
Ilia Barahovski

İşlevin 'x' dizisinin bazı öğelerinin Yok veya sıfır olabileceği bir durumda, bu işlevden döndürülen değerlerin karşılık gelen 'x' değerlerini nasıl elde edersiniz? Bu işlevden döndürülen dizinin boyutu, kendisine sağlanan 'x' dizisinden daha küçük olabilir.
Güneş Ayı

17

İşte bunu yapmanın çeşitli yolları ve bazı kriterler. En iyi yöntemler, diğer kitaplıklardan optimize edilmiş kod kullanan sürümlerdir. bottleneck.move_meanYöntem muhtemelen en iyi her yerde. scipy.convolveYaklaşım aynı zamanda çok hızlı, genişletilebilir ve sözdizimsel ve kavramsal basit, ama çok büyük pencere değerleri için iyi ölçek değildir. numpy.cumsumSaf bir gerekiyorsa yöntem iyidir numpyyaklaşımı.

Not: Bunlardan bazıları (örneğin bottleneck.move_mean) ortalanmamıştır ve verilerinizi değiştirecektir.

import numpy as np
import scipy as sci
import scipy.signal as sig
import pandas as pd
import bottleneck as bn
import time as time

def rollavg_direct(a,n): 
    'Direct "for" loop'
    assert n%2==1
    b = a*0.0
    for i in range(len(a)) :
        b[i]=a[max(i-n//2,0):min(i+n//2+1,len(a))].mean()
    return b

def rollavg_comprehension(a,n):
    'List comprehension'
    assert n%2==1
    r,N = int(n/2),len(a)
    return np.array([a[max(i-r,0):min(i+r+1,N)].mean() for i in range(N)]) 

def rollavg_convolve(a,n):
    'scipy.convolve'
    assert n%2==1
    return sci.convolve(a,np.ones(n,dtype='float')/n, 'same')[n//2:-n//2+1]  

def rollavg_convolve_edges(a,n):
    'scipy.convolve, edge handling'
    assert n%2==1
    return sci.convolve(a,np.ones(n,dtype='float'), 'same')/sci.convolve(np.ones(len(a)),np.ones(n), 'same')  

def rollavg_cumsum(a,n):
    'numpy.cumsum'
    assert n%2==1
    cumsum_vec = np.cumsum(np.insert(a, 0, 0)) 
    return (cumsum_vec[n:] - cumsum_vec[:-n]) / n

def rollavg_cumsum_edges(a,n):
    'numpy.cumsum, edge handling'
    assert n%2==1
    N = len(a)
    cumsum_vec = np.cumsum(np.insert(np.pad(a,(n-1,n-1),'constant'), 0, 0)) 
    d = np.hstack((np.arange(n//2+1,n),np.ones(N-n)*n,np.arange(n,n//2,-1)))  
    return (cumsum_vec[n+n//2:-n//2+1] - cumsum_vec[n//2:-n-n//2]) / d

def rollavg_roll(a,n):
    'Numpy array rolling'
    assert n%2==1
    N = len(a)
    rolling_idx = np.mod((N-1)*np.arange(n)[:,None] + np.arange(N), N)
    return a[rolling_idx].mean(axis=0)[n-1:] 

def rollavg_roll_edges(a,n):
    # see /programming/42101082/fast-numpy-roll
    'Numpy array rolling, edge handling'
    assert n%2==1
    a = np.pad(a,(0,n-1-n//2), 'constant')*np.ones(n)[:,None]
    m = a.shape[1]
    idx = np.mod((m-1)*np.arange(n)[:,None] + np.arange(m), m) # Rolling index
    out = a[np.arange(-n//2,n//2)[:,None], idx]
    d = np.hstack((np.arange(1,n),np.ones(m-2*n+1+n//2)*n,np.arange(n,n//2,-1)))
    return (out.sum(axis=0)/d)[n//2:]

def rollavg_pandas(a,n):
    'Pandas rolling average'
    return pd.DataFrame(a).rolling(n, center=True, min_periods=1).mean().to_numpy()

def rollavg_bottlneck(a,n):
    'bottleneck.move_mean'
    return bn.move_mean(a, window=n, min_count=1)

N = 10**6
a = np.random.rand(N)
functions = [rollavg_direct, rollavg_comprehension, rollavg_convolve, 
        rollavg_convolve_edges, rollavg_cumsum, rollavg_cumsum_edges, 
        rollavg_pandas, rollavg_bottlneck, rollavg_roll, rollavg_roll_edges]

print('Small window (n=3)')
%load_ext memory_profiler
for f in functions : 
    print('\n'+f.__doc__+ ' : ')
    %timeit b=f(a,3)

print('\nLarge window (n=1001)')
for f in functions[0:-2] : 
    print('\n'+f.__doc__+ ' : ')
    %timeit b=f(a,1001)

print('\nMemory\n')
print('Small window (n=3)')
N = 10**7
a = np.random.rand(N)
%load_ext memory_profiler
for f in functions[2:] : 
    print('\n'+f.__doc__+ ' : ')
    %memit b=f(a,3)

print('\nLarge window (n=1001)')
for f in functions[2:-2] : 
    print('\n'+f.__doc__+ ' : ')
    %memit b=f(a,1001)

Zamanlama, Küçük pencere (n = 3)

Direct "for" loop : 

4.14 s ± 23.7 ms per loop (mean ± std. dev. of 7 runs, 1 loop each)

List comprehension : 
3.96 s ± 27.9 ms per loop (mean ± std. dev. of 7 runs, 1 loop each)

scipy.convolve : 
1.07 ms ± 26.7 µs per loop (mean ± std. dev. of 7 runs, 1000 loops each)

scipy.convolve, edge handling : 
4.68 ms ± 9.69 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)

numpy.cumsum : 
5.31 ms ± 5.11 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)

numpy.cumsum, edge handling : 
8.52 ms ± 11.1 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)

Pandas rolling average : 
9.85 ms ± 9.63 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)

bottleneck.move_mean : 
1.3 ms ± 12.2 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)

Numpy array rolling : 
31.3 ms ± 91.9 µs per loop (mean ± std. dev. of 7 runs, 10 loops each)

Numpy array rolling, edge handling : 
61.1 ms ± 55.9 µs per loop (mean ± std. dev. of 7 runs, 10 loops each)

Zamanlama, Büyük pencere (n = 1001)

Direct "for" loop : 
4.67 s ± 34 ms per loop (mean ± std. dev. of 7 runs, 1 loop each)

List comprehension : 
4.46 s ± 14.6 ms per loop (mean ± std. dev. of 7 runs, 1 loop each)

scipy.convolve : 
103 ms ± 165 µs per loop (mean ± std. dev. of 7 runs, 10 loops each)

scipy.convolve, edge handling : 
272 ms ± 1.23 ms per loop (mean ± std. dev. of 7 runs, 1 loop each)

numpy.cumsum : 
5.19 ms ± 12.4 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)

numpy.cumsum, edge handling : 
8.7 ms ± 11.5 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)

Pandas rolling average : 
9.67 ms ± 199 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)

bottleneck.move_mean : 
1.31 ms ± 15.7 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)

Bellek, Küçük pencere (n = 3)

The memory_profiler extension is already loaded. To reload it, use:
  %reload_ext memory_profiler

scipy.convolve : 
peak memory: 362.66 MiB, increment: 73.61 MiB

scipy.convolve, edge handling : 
peak memory: 510.24 MiB, increment: 221.19 MiB

numpy.cumsum : 
peak memory: 441.81 MiB, increment: 152.76 MiB

numpy.cumsum, edge handling : 
peak memory: 518.14 MiB, increment: 228.84 MiB

Pandas rolling average : 
peak memory: 449.34 MiB, increment: 160.02 MiB

bottleneck.move_mean : 
peak memory: 374.17 MiB, increment: 75.54 MiB

Numpy array rolling : 
peak memory: 661.29 MiB, increment: 362.65 MiB

Numpy array rolling, edge handling : 
peak memory: 1111.25 MiB, increment: 812.61 MiB

Bellek, Büyük pencere (n = 1001)

scipy.convolve : 
peak memory: 370.62 MiB, increment: 71.83 MiB

scipy.convolve, edge handling : 
peak memory: 521.98 MiB, increment: 223.18 MiB

numpy.cumsum : 
peak memory: 451.32 MiB, increment: 152.52 MiB

numpy.cumsum, edge handling : 
peak memory: 527.51 MiB, increment: 228.71 MiB

Pandas rolling average : 
peak memory: 451.25 MiB, increment: 152.50 MiB

bottleneck.move_mean : 
peak memory: 374.64 MiB, increment: 75.85 MiB

11

Pandaların kullanıldığı bu cevap, rolling_meanartık Pandaların bir parçası olmadığı için yukarıdan uyarlanmıştır.

# the recommended syntax to import pandas
import pandas as pd
import numpy as np

# prepare some fake data:
# the date-time indices:
t = pd.date_range('1/1/2010', '12/31/2012', freq='D')

# the data:
x = np.arange(0, t.shape[0])

# combine the data & index into a Pandas 'Series' object
D = pd.Series(x, t)

Şimdi, rollingaşağıdaki örneğimde 10 gün olan bir pencere boyutuyla veri çerçevesindeki işlevi çağırın .

d_mva10 = D.rolling(10).mean()

# d_mva is the same size as the original Series
# though obviously the first w values are NaN where w is the window size
d_mva10[:11]

2010-01-01    NaN
2010-01-02    NaN
2010-01-03    NaN
2010-01-04    NaN
2010-01-05    NaN
2010-01-06    NaN
2010-01-07    NaN
2010-01-08    NaN
2010-01-09    NaN
2010-01-10    4.5
2010-01-11    5.5
Freq: D, dtype: float64

5

Darboğaz kullanılarak bunun kolayca çözülebileceğini düşünüyorum

Aşağıdaki temel örneğe bakın:

import numpy as np
import bottleneck as bn

a = np.random.randint(4, 1000, size=(5, 7))
mm = bn.move_mean(a, window=2, min_count=1)

Bu, her eksen boyunca hareket ortalamasını verir.

  • "mm", "a" için hareketli ortalamadır.

  • "pencere", hareketli ortalama için dikkate alınacak maksimum girdi sayısıdır.

  • "min_count" hareketli ortalama için dikkate alınacak minimum girdi sayısıdır (örneğin, ilk öğe için veya dizinin nan değerleri varsa).

İşin iyi yanı, Darboğaz'ın nan değerleriyle başa çıkmaya yardımcı olması ve aynı zamanda çok verimli olması.


2

Kenar koşullarını dikkatli bir şekilde dikkate almak istemeniz durumunda ( ortalamayı yalnızca kenarlardaki mevcut öğelerden hesaplayın ), aşağıdaki işlev işe yarayacaktır.

import numpy as np

def running_mean(x, N):
    out = np.zeros_like(x, dtype=np.float64)
    dim_len = x.shape[0]
    for i in range(dim_len):
        if N%2 == 0:
            a, b = i - (N-1)//2, i + (N-1)//2 + 2
        else:
            a, b = i - (N-1)//2, i + (N-1)//2 + 1

        #cap indices to min and max indices
        a = max(0, a)
        b = min(dim_len, b)
        out[i] = np.mean(x[a:b])
    return out

>>> running_mean(np.array([1,2,3,4]), 2)
array([1.5, 2.5, 3.5, 4. ])

>>> running_mean(np.array([1,2,3,4]), 3)
array([1.5, 2. , 3. , 3.5])

1
for i in range(len(Data)):
    Data[i, 1] = Data[i-lookback:i, 0].sum() / lookback

Bu kod parçasını deneyin. Bence daha basit ve işi yapıyor. yeniden inceleme, hareketli ortalamanın penceresidir.

In Data[i-lookback:i, 0].sum()ben koydum 0veri kümesinin ilk sütuna başvurmak için ancak birden fazla sütuna sahip durumda istediğiniz herhangi bir sütun koyabilirsiniz.


0

Aslında kabul edilen cevaptan biraz farklı bir davranış istedim. Bir sklearnboru hattı için hareketli ortalama özellik çıkarıcı oluşturuyordum , bu nedenle hareketli ortalamanın çıktısının girdi ile aynı boyuta sahip olmasını istedim. İstediğim şey hareketli ortalamanın serinin sabit kaldığını varsayması, yani [1,2,3,4,5]pencere 2 ile hareketli bir ortalama verecektir [1.5,2.5,3.5,4.5,5.0].

Sütun vektörleri için (kullanım durumum)

def moving_average_col(X, n):
  z2 = np.cumsum(np.pad(X, ((n,0),(0,0)), 'constant', constant_values=0), axis=0)
  z1 = np.cumsum(np.pad(X, ((0,n),(0,0)), 'constant', constant_values=X[-1]), axis=0)
  return (z1-z2)[(n-1):-1]/n

Ve diziler için

def moving_average_array(X, n):
  z2 = np.cumsum(np.pad(X, (n,0), 'constant', constant_values=0))
  z1 = np.cumsum(np.pad(X, (0,n), 'constant', constant_values=X[-1]))
  return (z1-z2)[(n-1):-1]/n

Tabii ki, dolgu için sabit değerler varsaymak zorunda değilsiniz, ancak bunu yapmak çoğu durumda yeterli olmalıdır.


0

talib , basit bir hareketli ortalama aracı ve diğer benzer ortalama alma araçlarını (yani üstel hareketli ortalama) içerir. Aşağıda, yöntemi diğer bazı çözümlerle karşılaştırmaktadır.


%timeit pd.Series(np.arange(100000)).rolling(3).mean()
2.53 ms ± 40.5 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)

%timeit talib.SMA(real = np.arange(100000.), timeperiod = 3)
348 µs ± 3.5 µs per loop (mean ± std. dev. of 7 runs, 1000 loops each)

%timeit moving_average(np.arange(100000))
638 µs ± 45.1 µs per loop (mean ± std. dev. of 7 runs, 1000 loops each)

Bir uyarı, gerçeğin unsurlarına sahip olması gerektiğidir dtype = float. Aksi takdirde aşağıdaki hata ortaya çıkar

İstisna: gerçek çift değildir


0

İşte numba kullanan hızlı bir uygulama (türlere dikkat edin). Değiştirildiği yerde nans içerdiğini unutmayın.

import numpy as np
import numba as nb

@nb.jit(nb.float64[:](nb.float64[:],nb.int64),
        fastmath=True,nopython=True)
def moving_average( array, window ):    
    ret = np.cumsum(array)
    ret[window:] = ret[window:] - ret[:-window]
    ma = ret[window - 1:] / window
    n = np.empty(window-1); n.fill(np.nan)
    return np.concatenate((n.ravel(), ma.ravel())) 

Bu, başlangıçta nans döndürür.
Adam Erickson

0

hareketli ortalama

  • i'deki diziyi tersine çevirin ve i'den n'ye ortalamayı alın.

  • anında mini diziler oluşturmak için liste anlama özelliğini kullanın.

x = np.random.randint(10, size=20)

def moving_average(arr, n):
    return [ (arr[:i+1][::-1][:n]).mean() for i, ele in enumerate(arr) ]
n = 5

moving_average(x, n)

0

Ya kabul edilen cevabın çözümünü, girdi olarak çıktı olarak aynı uzunluğa sahip olacak şekilde biraz değiştirilmiş ya da pandasbaşka bir cevabın yorumunda belirtildiği gibi 'versiyonunu kullanıyorum. Her ikisini de gelecekte referans olması için tekrarlanabilir bir örnekle özetliyorum:

import numpy as np
import pandas as pd

def moving_average(a, n):
    ret = np.cumsum(a, dtype=float)
    ret[n:] = ret[n:] - ret[:-n]
    return ret / n

def moving_average_centered(a, n):
    return pd.Series(a).rolling(window=n, center=True).mean().to_numpy()

A = [0, 0, 1, 2, 4, 5, 4]
print(moving_average(A, 3))    
# [0.         0.         0.33333333 1.         2.33333333 3.66666667 4.33333333]
print(moving_average_centered(A, 3))
# [nan        0.33333333 1.         2.33333333 3.66666667 4.33333333 nan       ]

0

Aşağıdaki çözümü, cumsum of numpy kullanan çözümle karşılaştırarak, Bu neredeyse yarısı kadar zaman alıyor . Bunun nedeni, cumsum yapmak için tüm diziyi gözden geçirmesi ve ardından tüm çıkarma işlemini yapması gerekmemesidir. Dahası, dizi çok büyükse ve sayı çok büyükse ( olası taşma ) , cumsum " tehlikeli " olabilir . Tabii ki, burada da tehlike mevcuttur, ancak en azından yalnızca temel sayılar toplanır.

def moving_average(array_numbers, n):
    if n > len(array_numbers):
      return []
    temp_sum = sum(array_numbers[:n])
    averages = [temp_sum / float(n)]
    for first_index, item in enumerate(array_numbers[n:]):
        temp_sum += item - array_numbers[first_index]
        averages.append(temp_sum / float(n))
    return averages
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.