Bagaimana cara menghitung rata-rata bergerak menggunakan NumPy?


109

Tampaknya tidak ada fungsi yang hanya menghitung rata-rata bergerak pada numpy / scipy, yang mengarah ke solusi berbelit - belit .

Pertanyaan saya ada dua:

  • Apa cara termudah untuk (benar) menerapkan rata-rata bergerak dengan numpy?
  • Karena hal ini tampaknya tidak sepele dan rawan kesalahan, adakah alasan yang baik untuk tidak menyertakan baterai dalam casing ini?

19
Solusi konvolusi tampaknya tidak terlalu berbelit-belit bagi saya!
wim

4
Bukankah rata-rata bergerak hanyalah filter low-pass (yaitu 'blur')? Cukup yakin itulah konvolusi yang dimaksudkan untuk ...
user541686

@mmgp Saya rasa saya berharap salah, atau ada alasan yang bagus dan jelas.
goncalopp

3
@wim Itu setengah dimaksudkan sebagai pelesetan. Tetapi fakta bahwa pertanyaan itu ada berarti tidak mudah untuk membuat rata-rata bergerak dari numpy.convolute.
goncalopp

3
Kemungkinan duplikat Moving average atau running mean
pppery

Jawaban:


165

Jika Anda hanya ingin langsung non-tertimbang rata-rata bergerak, Anda dapat dengan mudah menerapkannya dengan np.cumsum, yang mungkin merupakan metode cepat dari FFT berdasarkan:

EDIT Memperbaiki kesalahan pengindeksan off-by-one yang ditemukan oleh Bean dalam kode. EDIT

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

Jadi saya kira jawabannya adalah: sangat mudah untuk diterapkan, dan mungkin numpy sudah sedikit membengkak dengan fungsi khusus.


10
Kode ini salah. misalnya moving_average ([1,2,5,10], n = 2) menghasilkan [1., 3.5, 8.5]. Bahkan kasus uji penjawab untuk rata-rata bergerak nilai dari 0 hingga 19 tidak benar, mengklaim bahwa rata-rata 0, 1, dan 2 adalah 0,5. Bagaimana cara mendapatkan 6 suara positif?
JeremyKun

2
Terima kasih atas pemeriksaan bugnya, sekarang tampaknya berfungsi dengan baik. Adapun upvote, saya menebak ide umum di balik jawaban itu lebih berat daripada kesalahan off-by-one dalam implementasi, tapi siapa yang tahu.
Jaime

2
Saya telah menemukan masalahnya. ret[n:] -= ret[:-n]TIDAK SAMA dengan ret[n:] = ret[n:] - ret[:-n]. Saya telah memperbaiki kode dalam jawaban ini. Edit: Tidak ada orang lain yang mengalahkan saya untuk itu.
Timmmm

7
@Timmmm saya lakukan, itu memang masalahnya. Prinsip umum di balik jawaban ini banyak digunakan dalam pemrosesan gambar (mereka menyebutnya tabel area yang dijumlahkan), jadi masalah harus ada dalam implementasi. Sebuah contoh yang baik tentang sedikit optimasi prematur, karena saya ingat melakukan operasi di tempat "karena akan lebih efisien." Sisi baiknya, itu mungkin menghasilkan jawaban yang salah lebih cepat ...
Jaime

43
Hmmm, tampaknya fungsi "mudah diimplementasikan" ini sebenarnya cukup mudah untuk salah dan telah mendorong diskusi yang baik tentang efisiensi memori. Saya senang merasa gembung jika itu berarti mengetahui bahwa sesuatu telah dilakukan dengan benar.
Richard

81

Kurangnya NumPy pada fungsi spesifik domain tertentu mungkin karena disiplin Tim Inti dan ketaatan pada arahan utama NumPy: menyediakan tipe array dimensi-N , serta fungsi untuk membuat, dan mengindeks array tersebut. Seperti banyak tujuan dasar, yang satu ini tidak kecil, dan NumPy melakukannya dengan cemerlang.

SciPy (yang jauh) lebih besar berisi koleksi perpustakaan khusus domain yang jauh lebih besar (disebut subpaket oleh SciPy devs) - misalnya, pengoptimalan numerik ( optimalkan ), pemrosesan sinyal ( sinyal ), dan kalkulus integral ( integrasi ).

Dugaan saya adalah bahwa fungsi yang Anda cari setidaknya ada di salah satu subpaket SciPy ( mungkin scipy.signal ); Namun, saya akan melihat pertama di koleksi scikits SciPy , mengidentifikasi scikit yang relevan dan mencari fungsi yang menarik di sana.

Scikits adalah paket yang dikembangkan secara independen berdasarkan NumPy / SciPy dan diarahkan ke disiplin teknis tertentu (misalnya, scikits-image , scikits-learn , dll.) Beberapa di antaranya (khususnya, OpenOpt yang mengagumkan untuk pengoptimalan numerik) sangat dihargai, proyek matang jauh sebelum memilih untuk berada di bawah rubrik scikits yang relatif baru . The Scikits homepage suka daftar di atas sekitar 30 seperti scikits , meskipun setidaknya beberapa dari mereka tidak lagi di bawah pengembangan aktif.

Mengikuti saran ini akan membawa Anda ke seri waktu scikits ; namun, paket tersebut tidak lagi aktif dalam pengembangan; Akibatnya, Panda telah menjadi, AFAIK, perpustakaan deret waktu berbasis NumPy de facto .

Panda memiliki beberapa fungsi yang dapat digunakan untuk menghitung rata-rata bergerak ; yang paling sederhana mungkin adalah rolling_mean , yang Anda gunakan seperti ini:

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

Sekarang, panggil saja fungsi rolling_mean yang lewat di objek Seri dan ukuran jendela , yang dalam contoh saya di bawah ini adalah 10 hari .

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

verifikasi bahwa itu berfungsi - misalnya, membandingkan nilai 10 - 15 dalam seri asli versus Seri baru yang dihaluskan dengan rata-rata bergulir

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

Fungsi rolling_mean, bersama dengan sekitar selusin fungsi lainnya secara informal dikelompokkan dalam dokumentasi Pandas di bawah fungsi rubrik jendela bergerak ; kedua, grup fungsi terkait di Pandas disebut sebagai fungsi dengan bobot eksponensial (misalnya, ewma , yang menghitung rata-rata bergerak tertimbang bergerak secara eksponensial). Fakta bahwa grup kedua ini tidak termasuk dalam yang pertama ( fungsi jendela bergerak ) mungkin karena transformasi dengan pembobotan eksponensial tidak bergantung pada jendela dengan panjang tetap


6
Panda memang memiliki deretan fungsi jendela bergerak yang kuat. Tapi bagi saya sepertinya terlalu banyak overhead untuk rata-rata bergerak sederhana.
Jaime

6
baik saya ragu bahwa menghitung rata-rata bergerak merupakan persyaratan terisolasi untuk OP atau untuk orang lain. Jika Anda perlu menghitung rata-rata bergerak maka Anda hampir pasti memiliki deret waktu, yang berarti Anda memerlukan struktur data yang memungkinkan Anda menyesuaikan indeks tanggal-waktu dengan data Anda dan itulah 'overhead' yang Anda maksud.
doug

2
Pertama, terima kasih telah meluangkan waktu untuk menulis jawaban yang sangat informatif ini. Memang, saya tidak bisa melihat penggunaan rata-rata bergerak yang tidak melibatkan deret waktu. Tetapi itu tidak berarti bahwa seseorang perlu menyesuaikannya dengan waktu waktu, atau bahkan interval pengambilan sampel tertentu (mungkin tidak diketahui)
goncalopp

3
Hanya ingin menambahkan bahwa fungsi rata-rata bergerak telah diekstraksi ke pustaka Bottleneck jika panda tampak terlalu kelas berat sebagai dependensi.
robochat

4
'rolling_mean' bukan lagi bagian pf panda, silakan lihat balasan menggunakan 'rolling' sebagai gantinya
Vladtn

62

Cara sederhana untuk mencapai ini adalah dengan menggunakan np.convolve. Ide di balik ini adalah untuk memanfaatkan cara konvolusi diskrit dihitung dan menggunakannya untuk mengembalikan rata-rata bergulir . Hal ini dapat dilakukan dengan cara berbelit-belit dengan urutan np.onesyang panjangnya sama dengan panjang jendela geser yang kita inginkan.

Untuk melakukannya, kita dapat menentukan fungsi berikut:

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

Fungsi ini akan mengambil konvolusi dari urutan xdan urutan panjangnya w. Perhatikan bahwa yang dipilih modeadalah validagar hasil kali konvolusi hanya diberikan untuk titik-titik di mana urutannya tumpang tindih sepenuhnya.


Beberapa contoh:

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

Untuk rata-rata bergerak dengan panjang jendela, 2kita akan memiliki:

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

Dan untuk jendela panjang 4:

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

Bagaimana cara convolvekerjanya?

Mari kita lihat lebih mendalam cara penghitungan konvolusi diskrit. Fungsi berikut bertujuan untuk mereplikasi cara np.convolvemenghitung nilai keluaran:

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

Yang mana, untuk contoh yang sama di atas juga akan menghasilkan:

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

Jadi apa yang dilakukan pada setiap langkah adalah mengambil produk dalam antara array satu dan jendela saat ini . Dalam hal ini perkalian dengan np.ones(w)berlebihan karena kita langsung mengambil sumurutannya.

Di bawah ini adalah contoh bagaimana keluaran pertama dihitung sehingga sedikit lebih jelas. Misalkan kita menginginkan jendela 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

Dan keluaran berikut akan dihitung sebagai:

  [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

Dan seterusnya, mengembalikan rata-rata bergerak dari urutan setelah semua tumpang tindih dilakukan.


Ini ide yang bagus! Ini lebih cepat dari jawaban @ Jaime untuk n kecil, tetapi menjadi lebih lambat untuk n yang lebih besar.
Felipe Gerard

Terima kasih @FelipeGerard! Ya seperti yang ditunjukkan dalam komentar, sementara pendekatan ini mungkin tidak seefisien beberapa solusi numpy lainnya, saya senang memiliki alternatif untuk pengunjung masa depan karena kesederhanaan dan keringkasannya
yatu

Terkadang berguna untuk memiliki larik keluaran dengan ukuran yang sama dengan masukan. Untuk ini mode='valid'kaleng diganti dengan 'same'. Hanya dalam kasus ini, titik tepi akan tertarik ke arah nol.
Ilia Barahovski

15

Berikut ini berbagai cara untuk melakukan ini, bersama dengan beberapa tolok ukur. Metode terbaik adalah versi yang menggunakan kode yang dioptimalkan dari pustaka lain. The bottleneck.move_meanMetode mungkin terbaik di sekitar. The scipy.convolvePendekatan ini juga sangat cepat, extensible, dan sintaksis dan konseptual sederhana, tetapi tidak baik skala untuk nilai-nilai jendela yang sangat besar. The numpy.cumsumMetode baik jika Anda membutuhkan murni numpypendekatan.

Catatan: Beberapa di antaranya (misalnya bottleneck.move_mean) tidak berada di tengah, dan akan menggeser data Anda.

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)

Pengaturan waktu, Jendela kecil (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)

Pengaturan waktu, Jendela besar (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)

Memori, Jendela kecil (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

Memori, Jendela besar (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

Jawaban yang menggunakan Pandas ini diadaptasi dari atas, karena rolling_meansudah bukan bagian dari Panda lagi

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

Sekarang, panggil saja fungsi rollingpada dataframe dengan ukuran jendela, yang dalam contoh saya di bawah ini adalah 10 hari.

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

Saya rasa ini bisa diselesaikan dengan mudah dengan menggunakan bottleneck

Lihat contoh dasar di bawah ini:

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)

Ini memberikan rata-rata pergerakan di sepanjang setiap sumbu.

  • "mm" adalah arti bergerak untuk "a".

  • "jendela" adalah jumlah entri maksimal yang perlu dipertimbangkan untuk mean bergerak.

  • "min_count" adalah jumlah minimum entri yang perlu dipertimbangkan untuk memindahkan mean (misalnya untuk elemen pertama atau jika array memiliki nilai nan).

Bagian baiknya adalah Bottleneck membantu menangani nilai-nilai nan dan juga sangat efisien.


2

Jika Anda ingin menjaga kondisi tepi dengan hati-hati ( menghitung rata-rata hanya dari elemen yang tersedia di tepi ), fungsi berikut akan membantu.

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

Coba kode ini. Saya pikir ini lebih sederhana dan melakukan pekerjaan itu. lookback adalah jendela rata-rata bergerak.

Di bagian Data[i-lookback:i, 0].sum()Saya telah meletakkan 0untuk merujuk ke kolom pertama dari kumpulan data tetapi Anda dapat meletakkan kolom apa pun yang Anda suka jika Anda memiliki lebih dari satu kolom.


0

Saya sebenarnya menginginkan perilaku yang sedikit berbeda dari jawaban yang diterima. Saya sedang membangun ekstraktor fitur rata-rata bergerak untuk sklearnpipa, jadi saya mensyaratkan bahwa keluaran rata-rata bergerak memiliki dimensi yang sama dengan masukan. Yang saya inginkan adalah rata-rata bergerak mengasumsikan rangkaian tetap konstan, yaitu rata-rata bergerak [1,2,3,4,5]dengan jendela 2 akan memberikan [1.5,2.5,3.5,4.5,5.0].

Untuk vektor kolom (kasus penggunaan saya) kita dapatkan

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

Dan untuk array

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

Tentu saja, seseorang tidak harus mengasumsikan nilai konstan untuk padding, tetapi melakukannya harus cukup dalam banyak kasus.


0

talib berisi alat rata-rata bergerak sederhana, serta alat rata-rata serupa lainnya (yaitu rata-rata bergerak eksponensial). Di bawah ini membandingkan metode dengan beberapa solusi lainnya.


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

Satu peringatan adalah bahwa yang nyata harus memiliki elemen dtype = float. Jika tidak, kesalahan berikut dimunculkan

Pengecualian: nyata bukan ganda


0

Berikut ini adalah implementasi cepat menggunakan numba (perhatikan jenisnya). Perhatikan itu memang mengandung nans di mana digeser.

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

Ini mengembalikan nans di awal.
Adam Erickson

0

rata-rata bergerak

  • membalikkan array pada i, dan mengambil mean dari i ke n.

  • menggunakan pemahaman daftar untuk menghasilkan larik mini dengan cepat.

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

Saya menggunakan baik solusi jawaban yang diterima , sedikit dimodifikasi agar memiliki panjang yang sama untuk keluaran sebagai masukan, atau pandas'versi seperti yang disebutkan dalam komentar jawaban lain. Saya meringkas keduanya di sini dengan contoh yang dapat direproduksi untuk referensi di masa mendatang:

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

Dengan membandingkan solusi di bawah ini dengan solusi yang menggunakan cumsum numpy, solusi ini memakan waktu hampir separuh . Ini karena tidak perlu melalui seluruh larik untuk melakukan cumsum dan kemudian melakukan semua pengurangan. Selain itu, cumsum bisa menjadi " berbahaya " jika arraynya besar dan jumlahnya besar ( kemungkinan meluap ). Tentu saja, di sini juga ada bahaya tetapi setidaknya dijumlahkan hanya angka-angka penting.

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
Dengan menggunakan situs kami, Anda mengakui telah membaca dan memahami Kebijakan Cookie dan Kebijakan Privasi kami.
Licensed under cc by-sa 3.0 with attribution required.