Rata-rata bergerak atau rata-rata berjalan


195

Apakah ada fungsi SciPy atau fungsi NumPy atau modul untuk Python yang menghitung rata-rata berjalan dari array 1D yang diberikan jendela tertentu?

Jawaban:


24

Untuk solusi singkat dan cepat yang melakukan semuanya dalam satu lingkaran, tanpa dependensi, kode di bawah ini berfungsi dengan baik.

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
Cepat?! Solusi ini adalah urutan besarnya lebih lambat daripada solusi dengan Numpy.
Bart

3
Meskipun solusi asli ini keren, OP meminta fungsi numpy / scipy - mungkin itu akan jauh lebih cepat.
Demis

256

UPD: solusi yang lebih efisien telah diusulkan oleh Alleo dan jasaarim .


Anda dapat menggunakannya np.convolveuntuk itu:

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

Penjelasan

Mean berjalan adalah kasus operasi matematika konvolusi . Untuk mean yang sedang berjalan, Anda menggeser jendela di sepanjang input dan menghitung rata-rata dari isi jendela. Untuk sinyal 1D diskrit, konvolusi adalah hal yang sama, kecuali alih-alih berarti Anda menghitung kombinasi linear sewenang-wenang, yaitu mengalikan setiap elemen dengan koefisien yang sesuai dan menjumlahkan hasilnya. Koefisien-koefisien tersebut, satu untuk setiap posisi di jendela, kadang-kadang disebut kernel konvolusi . Sekarang, rata-rata aritmatika dari nilai-nilai N adalah (x_1 + x_2 + ... + x_N) / N, jadi kernel yang sesuai adalah (1/N, 1/N, ..., 1/N), dan itulah yang kita dapatkan dengan menggunakan np.ones((N,))/N.

Tepi

The modeargumen np.convolvemenspesifikasikan bagaimana menangani tepi. Saya memilih validmode di sini karena saya pikir itulah cara kebanyakan orang berharap menjalankan berarti bekerja, tetapi Anda mungkin memiliki prioritas lain. Berikut adalah plot yang menggambarkan perbedaan antara mode:

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

Menjalankan mode convolve rata-rata


5
Saya suka solusi ini karena bersih (satu baris) dan relatif efisien (pekerjaan dilakukan di dalam numpy). Tetapi penggunaan "solusi efisien" Alleo numpy.cumsummemiliki kompleksitas yang lebih baik.
Ulrich Stern

2
@denfromufa, saya percaya dokumentasi mencakup implementasi dengan cukup baik, dan juga tautan ke Wikipedia yang menjelaskan matematika. Mempertimbangkan fokus pertanyaan, apakah Anda pikir jawaban ini perlu menyalinnya?
lapis

@lapis penggunaan convolve untuk moving average sangat tidak biasa dan tidak jelas. Berikut adalah penjelasan visual terbaik yang saya temukan: matlabtricks.com/post-11/moving-average-by-convolution
denfromufa

Untuk memplot dan tugas terkait akan sangat membantu untuk mengisinya dengan nilai Tidak Ada. Saran saya (tidak terlalu cantik tapi singkat): `` `def moving_average (x, N, fill = True): return np.concatenate ([x untuk x dalam [[Tidak Ada] * (N // 2 + N% 2) * isi, np.convolve (x, np.ones ((N,)) / N, mode = 'valid'), [Tidak Ada] * (N // 2) * isi,] jika len (x)]) ` `` Kode terlihat sangat jelek di komentar SO xD Saya tidak ingin menambahkan jawaban lain karena ada begitu banyak tetapi Anda mungkin hanya menyalin dan menempelkannya ke IDE Anda.
Chaoste

146

Solusi yang efisien

Konvolusi jauh lebih baik daripada pendekatan langsung, tetapi (saya kira) menggunakan FFT dan karenanya sangat lambat. Namun khusus untuk komputasi yang sedang berjalan berarti pendekatan berikut berfungsi dengan baik

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

Kode untuk diperiksa

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

Perhatikan bahwa numpy.allclose(result1, result2)adalah True, dua metode yang setara. Semakin besar N, semakin besar pula perbedaan waktu.

peringatan: meskipun cumsum lebih cepat akan ada peningkatan kesalahan floating point yang dapat menyebabkan hasil Anda tidak valid / salah / tidak dapat diterima

komentar menunjukkan masalah kesalahan floating point di sini tapi saya membuatnya lebih jelas di sini di jawabannya. .

# 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)
  • semakin banyak poin yang Anda akumulasi, semakin besar kesalahan floating point (jadi 1e5 poin terlihat, 1e6 poin lebih signifikan, lebih dari 1e6 dan Anda mungkin ingin mengatur ulang akumulator)
  • Anda dapat menipu dengan menggunakan np.longdoubletetapi kesalahan floating point Anda akan tetap signifikan untuk jumlah poin yang relatif besar (sekitar> 1e5 tetapi tergantung pada data Anda)
  • Anda dapat merencanakan kesalahan dan melihatnya meningkat relatif cepat
  • solusi yang berbelit-belit lebih lambat tetapi tidak memiliki kehilangan presisi floating point ini
  • solusi uniform_filter1d lebih cepat dari solusi cumsum ini DAN tidak memiliki kehilangan presisi floating point ini

3
Solusi bagus! Firasat saya numpy.convolveadalah O (mn); yang docs menyebutkan bahwa scipy.signal.fftconvolvepenggunaan FFT.
Ulrich Stern

3
Metode ini tidak berurusan dengan tepi array, bukan?
JoVe

6
Solusi yang bagus, tetapi perhatikan bahwa itu mungkin menderita kesalahan numerik untuk array besar, karena menjelang akhir array, Anda mungkin mengurangi dua angka besar untuk mendapatkan hasil yang kecil.
Bas Swinckels

1
Ini menggunakan pembagian integer bukannya divisi float: running_mean([1,2,3], 2)give array([1, 2]). Mengganti xdengan [float(value) for value in x]melakukan trik.
ChrisW

4
Stabilitas numerik dari solusi ini dapat menjadi masalah jika xmengandung pelampung. Contoh: running_mean(np.arange(int(1e7))[::-1] + 0.2, 1)[-1] - 0.2kembali 0.003125sementara yang diharapkan 0.0. Informasi lebih lanjut: en.wikipedia.org/wiki/Loss_of_significance
Milan

80

Pembaruan: Contoh di bawah ini menunjukkan pandas.rolling_meanfungsi lama yang telah dihapus dalam versi panda terbaru. Setara modern dengan panggilan fungsi di bawah ini adalah

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

panda lebih cocok untuk ini daripada NumPy atau SciPy. Fungsinya rolling_mean melakukan pekerjaan dengan nyaman. Itu juga mengembalikan array NumPy ketika input adalah sebuah array.

Sulit untuk mengalahkan rolling_meandalam kinerja dengan implementasi Python murni khusus. Berikut adalah contoh kinerja terhadap dua solusi yang diusulkan:

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

Ada juga opsi bagus untuk bagaimana menangani nilai-nilai tepi.


6
Rolling_mean Pandas adalah alat yang bagus untuk pekerjaan itu tetapi telah ditinggalkan karena ndarrays. Dalam rilis Pandas masa depan itu hanya akan berfungsi pada seri Pandas. Di mana kita beralih sekarang ke data array non-Pandas?
Mike

5
@Mike rolling_mean () sudah usang, tetapi sekarang Anda dapat menggunakan rolling dan berarti secara terpisah: df.rolling(windowsize).mean()sekarang berfungsi sebagai gantinya (sangat cepat saya dapat menambahkan). untuk seri 6.000 baris %timeit test1.rolling(20).mean()menghasilkan 1000 loop, terbaik 3: 1,16 ms per loop
Vlox

5
@Vlox df.rolling()berfungsi dengan cukup baik, masalahnya adalah bahkan formulir ini tidak akan mendukung ndarrays di masa mendatang. Untuk menggunakannya, kita harus memuat data kita ke dalam Bingkai Data Pandas terlebih dahulu. Saya ingin melihat fungsi ini ditambahkan ke salah satu numpyatau scipy.signal.
Mike

1
@ Mike sepenuhnya setuju. Saya berjuang khususnya untuk mencocokkan panda .ewm (). Mean () kecepatan untuk array saya sendiri (daripada harus memuatnya ke df terlebih dahulu). Maksud saya, ini bagus karena cepat, tetapi hanya merasa agak kikuk terlalu sering masuk dan keluar dari kerangka data.
Vlox

6
%timeit bottleneck.move_mean(x, N)3 sampai 15 kali lebih cepat daripada metode cumsum dan panda di komputer saya. Lihatlah benchmark mereka di README repo .
mab

50

Anda dapat menghitung rata-rata berjalan dengan:

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

Tapi lambat.

Untungnya, numpy menyertakan fungsi belok yang dapat kita gunakan untuk mempercepat. Mean berjalan setara dengan berbelit x- belit dengan vektor yang Npanjang, dengan semua anggota sama dengan 1/N. Implementasi convolve yang numpy termasuk transien awal, jadi Anda harus menghapus poin N-1 pertama:

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

Di komputer saya, versi cepat adalah 20-30 kali lebih cepat, tergantung pada panjang vektor input dan ukuran jendela rata-rata.

Perhatikan bahwa convolve memang menyertakan 'same'mode yang sepertinya harus mengatasi masalah transien awal, tetapi membelahnya antara awal dan akhir.


Perhatikan bahwa menghapus poin N-1 pertama masih meninggalkan efek batas di poin terakhir. Cara yang lebih mudah untuk memecahkan masalah ini adalah dengan menggunakan mode='valid'di convolvemana tidak memerlukan post-processing.
lapis

1
@ Psycho - mode='valid'menghapus transien dari kedua ujungnya, kan? Jika len(x)=10dan N=4, untuk rata-rata berlari, saya ingin 10 hasil tetapi validmengembalikan 7.
mtrw

1
Ini menghilangkan transient dari akhir, dan awalnya tidak memilikinya. Yah, saya kira ini masalah prioritas, saya tidak perlu jumlah hasil yang sama dengan biaya mendapatkan kemiringan menuju nol yang tidak ada dalam data. BTW, berikut adalah perintah untuk menunjukkan perbedaan antara mode: 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')(dengan pyplot dan numpy diimpor).
lapis

runningMeanApakah saya memiliki efek samping rata-rata dengan nol, ketika Anda keluar dari array dengan x[ctr:(ctr+N)]untuk sisi kanan array.
mrgloom

runningMeanFastjuga memiliki masalah efek perbatasan ini.
mrgloom

22

atau modul untuk python yang menghitung

dalam pengujian saya di Tradewave.net TA-lib selalu menang:

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

hasil:

[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

masukkan deskripsi gambar di sini


NameError: name 'info' is not defined. Saya mendapatkan kesalahan ini, Pak.
Md. Rezwanul Haque

1
Sepertinya Anda time series digeser setelah dihaluskan, apakah itu efek yang diinginkan?
mrgloom

@rgloom ya, untuk tujuan visualisasi; kalau tidak mereka akan muncul sebagai satu baris pada grafik; Md. Rezwanul Haque Anda dapat menghapus semua referensi untuk PAIR dan info; itu adalah metode sandbox internal untuk tradewave.net yang sudah tidak aktif
kehadiran

21

Untuk solusi yang siap digunakan, lihat https://scipy-cookbook.readthedocs.io/items/SignalSmooth.html . Ini memberikan rata-rata berjalan dengan flatjenis jendela. Perhatikan bahwa ini sedikit lebih canggih daripada metode konvolusi do-it-yourself yang sederhana, karena metode ini mencoba menangani masalah di awal dan akhir data dengan merefleksikannya (yang mungkin atau mungkin tidak berfungsi dalam kasus Anda. ..)

Untuk memulainya, Anda dapat mencoba:

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

1
Metode ini mengandalkan numpy.convolve, bedanya hanya dalam mengubah urutan.
Alleo

10
Saya selalu terganggu oleh fungsi pemrosesan sinyal yang mengembalikan sinyal output dengan bentuk yang berbeda dari sinyal input ketika input dan output memiliki sifat yang sama (misalnya, kedua sinyal temporal). Itu memecah korespondensi dengan variabel independen terkait (misalnya, waktu, frekuensi) yang membuat plot atau perbandingan bukan masalah langsung ... Lagi pula, jika Anda berbagi perasaan, Anda mungkin ingin mengubah baris terakhir dari fungsi yang diusulkan sebagai y = np .convolve (w / w.sum (), s, mode = 'same'); return y [window_len-1 :-( window_len-1)]
Christian O'Reilly

@ ChristianO'Reilly, Anda harus mempostingnya sebagai jawaban terpisah - itulah yang saya cari, karena saya memang memiliki dua array lain yang harus cocok dengan panjang data yang dihaluskan, untuk diplot dll. Saya ingin tahu persis bagaimana Anda melakukannya - adalah wukuran jendela, dan sdata?
Demis

@Demis Senang komentar membantu. Info lebih lanjut tentang fungsi numpy convolve di sini docs.scipy.org/doc/numpy-1.15.0/reference/generated/… Fungsi konvolusi ( en.wikipedia.org/wiki/Convolution ) menggabungkan dua sinyal satu sama lain. Dalam hal ini, ia melingkupi sinyal Anda dengan jendela yang dinormalisasi (yaitu area kesatuan) (w / w.sum ()).
Christian O'Reilly

21

Anda dapat menggunakan 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:

  • memberikan output dengan bentuk numpy yang sama (yaitu jumlah titik)
  • memungkinkan banyak cara untuk menangani perbatasan di mana 'reflect'default, tetapi dalam kasus saya, saya lebih suka'nearest'

Ini juga agak cepat (hampir 50 kali lebih cepat dari np.convolvedan 2-5 kali lebih cepat daripada pendekatan cumsum yang diberikan di atas ):

%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

inilah 3 fungsi yang memungkinkan Anda membandingkan kesalahan / kecepatan implementasi yang berbeda:

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
Ini adalah satu-satunya jawaban yang tampaknya mempertimbangkan masalah perbatasan (agak penting, terutama ketika merencanakan). Terima kasih!
Gabriel

1
saya profil uniform_filter1d, np.convolvedengan persegi panjang, dan np.cumsumdiikuti oleh np.subtract. hasil saya: (1.) convolve adalah yang paling lambat. (2.) cumsum / kurangi sekitar 20-30x lebih cepat. (3.) uniform_filter1d sekitar 2-3x lebih cepat dari cumsum / kurangi. pemenang pasti uniform_filter1d.
Trevor Boyd Smith

menggunakan uniform_filter1dadalah lebih cepat dari cumsumsolusi (sekitar 2-5x). dan uniform_filter1d tidak mendapatkan kesalahan floating point besar seperticumsum solusinya.
Trevor Boyd Smith

15

Saya tahu ini adalah pertanyaan lama, tetapi di sini ada solusi yang tidak menggunakan struktur data atau pustaka tambahan. Itu linier dalam jumlah elemen daftar input dan saya tidak bisa memikirkan cara lain untuk membuatnya lebih efisien (sebenarnya jika ada yang tahu cara yang lebih baik untuk mengalokasikan hasilnya, tolong beri tahu saya).

CATATAN: ini akan jauh lebih cepat menggunakan array numpy daripada daftar, tapi saya ingin menghilangkan semua dependensi. Mungkin juga untuk meningkatkan kinerja dengan eksekusi multi-threaded

Fungsi ini mengasumsikan bahwa daftar input adalah satu dimensi, jadi berhati-hatilah.

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

Contoh

Asumsikan bahwa kita memiliki daftar data = [ 1, 2, 3, 4, 5, 6 ]yang ingin kita hitung rata-rata bergulir dengan periode 3, dan bahwa Anda juga menginginkan daftar keluaran dengan ukuran yang sama dengan input (yang paling sering terjadi).

Elemen pertama memiliki indeks 0, sehingga rata-rata bergulir harus dihitung pada elemen indeks -2, -1 dan 0. Jelas kami tidak memiliki data [-2] dan data [-1] (kecuali jika Anda ingin menggunakan khusus syarat batas), jadi kami mengasumsikan bahwa elemen-elemen tersebut adalah 0. Ini setara dengan nol-padding daftar, kecuali kami tidak benar-benar pad itu, hanya melacak indeks yang membutuhkan padding (dari 0 hingga N-1).

Jadi, untuk elemen N pertama kami terus menambahkan elemen dalam akumulator.

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

Dari elemen N + 1 ke depan akumulasi sederhana tidak berfungsi. kami harapkan result[3] = (2 + 3 + 4)/3 = 3tetapi ini berbeda dari (sum + 4)/3 = 3.333.

Cara untuk menghitung nilai yang benar adalah untuk mengurangi data[0] = 1dari sum+4, sehingga memberikan sum + 4 - 1 = 9.

Ini terjadi karena saat ini sum = data[0] + data[1] + data[2], tetapi itu juga berlaku untuk setiap i >= Nsebab, sebelum pengurangan, sumadalah data[i-N] + ... + data[i-2] + data[i-1].


12

Saya merasa ini bisa diselesaikan dengan elegan menggunakan bottleneck

Lihat contoh dasar di bawah ini:

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" adalah rata-rata bergerak untuk "a".

  • "window" adalah jumlah maksimum entri yang perlu dipertimbangkan untuk moving average.

  • "min_count" adalah min jumlah entri yang perlu dipertimbangkan untuk memindahkan rata-rata (misalnya untuk beberapa elemen pertama atau jika array memiliki nilai nan).

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


Lib ini sangat cepat. Fungsi rata-rata bergerak Python murni lambat. Bootleneck adalah pustaka PyData, yang menurut saya stabil dan dapat memperoleh dukungan terus menerus dari komunitas Python, jadi mengapa tidak menggunakannya?
GoingMyWay

6

Saya belum memeriksa seberapa cepat ini, tetapi Anda dapat mencoba:

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
Inilah yang akan saya lakukan. Adakah yang bisa meminta kritik mengapa ini cara yang buruk?
staggart

1
Solusi python sederhana ini bekerja dengan baik untuk saya tanpa perlu numpy. Saya akhirnya menggulungnya menjadi kelas untuk digunakan kembali.
Matthew Tschiegg

6

Jawaban ini berisi solusi menggunakan pustaka standar Python untuk tiga skenario yang berbeda.


Rata-rata berjalan dengan itertools.accumulate

Ini adalah solusi Python 3.2+ yang efisien memori, menghitung rata-rata yang berjalan di atas nilai yang dapat diubah dengan memanfaatkannya itertools.accumulate.

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

Catatan yang valuesbisa berupa iterable, termasuk generator atau objek lain yang menghasilkan nilai dengan cepat.

Pertama, malas membangun jumlah kumulatif dari nilai-nilai.

>>> cumu_sum = accumulate(value_stream)

Selanjutnya, enumeratejumlah kumulatif (mulai dari 1) dan membangun generator yang menghasilkan sebagian kecil dari nilai akumulasi dan indeks enumerasi saat ini.

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

Anda dapat mengeluarkan means = list(rolling_avg)jika Anda membutuhkan semua nilai dalam memori sekaligus atau menelepon nextsecara bertahap.
(Tentu saja, Anda juga dapat beralih rolling_avgdengan forloop, yang akan memanggil nextsecara implisit.)

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

Solusi ini dapat ditulis sebagai fungsi sebagai berikut.

from itertools import accumulate

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

Sebuah coroutine yang Anda dapat mengirim nilai-nilai setiap saat

Coroutine ini mengkonsumsi nilai yang Anda kirim dan menjaga rata-rata berjalan dari nilai yang terlihat sejauh ini.

Ini berguna ketika Anda tidak memiliki iterable nilai tetapi meminta nilai untuk dirata-rata satu per satu pada waktu yang berbeda sepanjang hidup program Anda.

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
        

Coroutine bekerja seperti ini:

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

Menghitung rata-rata di atas ukuran jendela geser N

Fungsi generator ini mengambil iterable dan ukuran jendela N dan menghasilkan rata-rata di atas nilai saat ini di dalam jendela. Ini menggunakan deque, yang merupakan struktur data yang mirip dengan daftar, tetapi dioptimalkan untuk modifikasi cepat ( pop, append) di kedua titik akhir .

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)
        

Berikut ini fungsinya:

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

Agak terlambat ke pesta, tapi saya sudah membuat fungsi kecil saya sendiri yang TIDAK membungkus ujung atau bantalan dengan nol yang kemudian digunakan untuk menemukan rata-rata juga. Sebagai perlakuan lebih lanjut adalah, bahwa itu juga sampel ulang sinyal pada titik-titik spasi linear. Kustomisasi kode sesuka hati untuk mendapatkan fitur lainnya.

Metode ini adalah perkalian matriks sederhana dengan kernel Gaussian yang dinormalisasi.

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

Penggunaan sederhana pada sinyal sinusoidal dengan tambahan noise terdistribusi normal: masukkan deskripsi gambar di sini


Ini tidak berfungsi untuk saya (python 3.6). 1 Tidak ada fungsi bernama sum, menggunakan np.sumbukannya 2 The @operator (tidak tahu apa itu) melempar kesalahan. Saya mungkin memeriksanya nanti tetapi saya kekurangan waktu sekarang
Bastian

Ini @adalah operator perkalian matriks yang mengimplementasikan np.matmul . Periksa apakah y_inarray Anda adalah array numpy, itu mungkin masalahnya.
xyzzyqed

5

Alih-alih numpy atau scipy, saya akan merekomendasikan panda untuk melakukan ini lebih cepat:

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

Ini mengambil rata-rata bergerak (MA) dari 3 periode kolom "data". Anda juga dapat menghitung versi bergeser, misalnya yang mengecualikan sel saat ini (bergeser satu kembali) dapat dihitung dengan mudah sebagai:

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

Bagaimana ini berbeda dari solusi yang diusulkan pada tahun 2016 ?
Tn. T

2
Solusi yang diusulkan pada 2016 digunakan pandas.rolling_meansaat menggunakan tambang pandas.DataFrame.rolling. Anda juga dapat menghitung bergerak min(), max(), sum()dll. Serta mean()dengan metode ini dengan mudah.
Gursel Karacor

Dalam yang pertama Anda perlu menggunakan metode yang berbeda seperti pandas.rolling_min, pandas.rolling_maxdll. Mereka serupa namun berbeda.
Gursel Karacor

4

Ada komentar oleh mab yang dimakamkan di salah satu jawaban di atas yang memiliki metode ini. bottleneckmemiliki move_meanyang merupakan rata-rata bergerak sederhana:

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_countadalah parameter praktis yang pada dasarnya akan membawa rata-rata bergerak ke titik itu dalam array Anda. Jika Anda tidak menyetel min_count, itu akan sama window, dan semuanya akan mencapai windowpoin nan.


3

Pendekatan lain untuk menemukan moving average tanpa menggunakan nanda, panda

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

akan mencetak [2.0, 4.0, 6.0, 6.5, 7.4, 7.8333333333333]


itertools.accumulate tidak ada di python 2.7, tetapi di python 3.4
grayaii

3

Pertanyaan ini sekarang bahkan lebih tua daripada ketika NeXuS menulis tentang hal itu bulan lalu, TETAPI saya suka bagaimana kodenya berurusan dengan kasus tepi. Namun, karena ini adalah "rata-rata bergerak sederhana," hasilnya tertinggal di belakang data yang mereka terapkan. Saya berpikir bahwa berurusan dengan kasus tepi dengan cara yang lebih memuaskan daripada mode NumPy ini valid, samedanfull dapat dicapai dengan menerapkan pendekatan yang sama denganconvolution() metode berbasis.

Kontribusi saya menggunakan rata-rata berjalan tengah untuk menyelaraskan hasilnya dengan data mereka. Ketika ada terlalu sedikit titik yang tersedia untuk jendela ukuran penuh untuk digunakan, rata-rata berjalan dihitung dari jendela yang lebih kecil berturut-turut di tepi array. [Sebenarnya, dari jendela yang lebih besar berturut-turut, tapi itu detail implementasi.]

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

Ini relatif lambat karena menggunakan convolve(), dan mungkin bisa dirapikan cukup banyak oleh Pythonista yang sebenarnya, namun, saya percaya bahwa ide itu berlaku.


3

Ada banyak jawaban di atas tentang penghitungan rata-rata berjalan. Jawaban saya menambahkan dua fitur tambahan:

  1. mengabaikan nilai-nilai nan
  2. menghitung nilai rata-rata untuk N nilai tetangga TIDAK termasuk nilai bunga itu sendiri

Fitur kedua ini sangat berguna untuk menentukan nilai mana yang berbeda dari tren umum dengan jumlah tertentu.

Saya menggunakan numpy.cumsum karena ini adalah metode yang paling efisien waktu ( lihat jawaban Alleo di atas ).

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)

Kode ini hanya berfungsi untuk Ns saja. Itu dapat disesuaikan untuk angka ganjil dengan mengubah np.insert dari padded_x dan n_nan.

Contoh output (mentah dalam warna hitam, movavg dengan warna biru): data mentah (hitam) dan rata-rata bergerak (biru) 10 poin di sekitar setiap nilai, tidak termasuk nilai itu.  nilai nan diabaikan.

Kode ini dapat dengan mudah diadaptasi untuk menghapus semua nilai rata-rata bergerak yang dihitung dari kurang dari cutoff = 3 nilai non-nan.

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)

data mentah (hitam) dan moving average (biru) sambil mengabaikan jendela apa pun dengan kurang dari 3 nilai non-nan


2

Gunakan Hanya Pustaka Standar Python (Hemat Memori)

Berikan saja versi lain menggunakan perpustakaan standar dequesaja. Cukup mengejutkan bagi saya bahwa sebagian besar jawaban menggunakan pandasatau 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]

Sebenarnya saya menemukan implementasi lain dalam python docs

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

Namun implementasinya menurut saya sedikit lebih kompleks dari yang seharusnya. Tetapi harus dalam dokumen python standar karena suatu alasan, dapatkah seseorang mengomentari implementasi tambang dan dokumen standar?


2
Satu perbedaan besar bahwa Anda terus menjumlahkan anggota jendela setiap iterasi, dan mereka secara efisien memperbarui jumlahnya (menghapus satu anggota dan menambahkan yang lain). dalam hal kompleksitas Anda melakukan O(n*d) perhitungan ( dmenjadi ukuran jendela, nukuran iterable) dan mereka melakukanO(n)
Iftah

@Iftah, bagus, terima kasih atas penjelasannya, Anda benar.
MaThMaX

2

Dengan variabel @ Aikude, saya menulis satu baris.

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

Meskipun ada solusi untuk pertanyaan ini di sini, silakan lihat solusi saya. Ini sangat sederhana dan bekerja dengan baik.

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

Dari membaca jawaban-jawaban lain, saya kira ini bukan pertanyaan yang ditanyakan, tetapi saya tiba di sini dengan kebutuhan menjaga rata-rata daftar nilai yang terus bertambah.

Jadi, jika Anda ingin menyimpan daftar nilai yang Anda peroleh dari suatu tempat (situs, alat pengukur, dll.) Dan rata-rata dari nilai terakhir yang ndiperbarui, Anda dapat menggunakan kode di bawah ini, yang meminimalkan upaya penambahan baru elemen:

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)

Dan Anda dapat mengujinya dengan, misalnya:

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

Pemberian yang mana:

Nilai dan rata-rata sebagai fungsi dari nilai #


1

Solusi lain hanya menggunakan perpustakaan dan deque standar:

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

Untuk tujuan pendidikan, izinkan saya menambahkan dua solusi Numpy (yang lebih lambat daripada solusi 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

Fungsi yang digunakan: as_strided , add.reduceat


1

Semua solusi tersebut buruk karena tidak ada

  • kecepatan karena python asli bukannya implementasi vektor numpy,
  • stabilitas numerik karena penggunaan yang buruk numpy.cumsum, atau
  • kecepatan karena O(len(x) * w)implementasi sebagai konvolusi.

Diberikan

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

Perhatikan bahwa x_[:w].sum()sama dengan x[:w-1].sum(). Jadi untuk rata-rata pertama numpy.cumsum(...)tambah x[w] / w(via x_[w+1] / w), dan kurangi 0(dari x_[0] / w). Ini menghasilkanx[0:w].mean()

Melalui cumsum, Anda akan memperbarui rata-rata kedua dengan menambah x[w+1] / wdan mengurangi tambahan x[0] / w, menghasilkan x[1:w+1].mean().

Ini berlangsung sampai x[-w:].mean()tercapai.

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

Solusi ini adalah vektor O(m),, dapat dibaca dan stabil secara numerik.


1

Bagaimana dengan filter rata-rata bergerak ? Ini juga satu-liner dan memiliki keuntungan, bahwa Anda dapat dengan mudah memanipulasi jenis jendela jika Anda memerlukan sesuatu selain persegi panjang, yaitu. rata-rata bergerak N-long sederhana dari sebuah:

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

Dan dengan jendela triangular diterapkan:

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

Catatan: Saya biasanya membuang sampel N pertama sebagai palsu karena itu [N:]pada akhirnya, tetapi itu tidak perlu dan masalah pilihan pribadi saja.


-7

Jika Anda memang memilih untuk roll sendiri, daripada menggunakan perpustakaan yang ada, harap menyadari kesalahan floating point dan mencoba untuk meminimalkan efeknya:

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

Jika semua nilai Anda kira-kira sama dengan besarnya, maka ini akan membantu menjaga presisi dengan selalu menambahkan nilai dengan besaran yang hampir sama.


15
Ini adalah jawaban yang sangat tidak jelas, setidaknya beberapa komentar dalam kode atau penjelasan mengapa ini membantu kesalahan floating point akan lebih baik.
Gabe

Dalam kalimat terakhir saya, saya mencoba menunjukkan mengapa itu membantu kesalahan floating point. Jika dua nilai kurang lebih sama urutan besarnya, kemudian menambahkan mereka kehilangan kurang presisi daripada jika Anda menambahkan angka yang sangat besar ke nilai yang sangat kecil. Kode menggabungkan nilai-nilai "berdekatan" dengan cara yang bahkan jumlah menengah harus selalu dekat besarnya, untuk meminimalkan kesalahan floating point. Tidak ada bukti yang bodoh, tetapi metode ini telah menyelamatkan beberapa proyek yang sangat kurang diimplementasikan dalam produksi.
Mayur Patel

1. diterapkan pada masalah asli, ini akan sangat lambat (rata-rata komputasi), jadi ini tidak relevan 2. untuk menderita masalah ketepatan angka 64-bit, kita harus meringkas >> 2 ^ 30 dari hampir angka yang sama.
Alleo

@Alleo: Alih-alih melakukan satu tambahan per nilai, Anda akan melakukan dua. Buktinya sama dengan masalah bit-flipping. Namun, inti dari jawaban ini belum tentu kinerja, tetapi presisi. Penggunaan memori untuk rata-rata nilai 64-bit tidak akan melebihi 64 elemen dalam cache, jadi itu ramah dalam penggunaan memori juga.
Mayur Patel

Ya, Anda benar bahwa ini membutuhkan operasi 2x lebih banyak dari jumlah sederhana, tetapi masalah aslinya adalah menghitung rata-rata berjalan , bukan hanya jumlah. Yang dapat dilakukan di O (n), tetapi jawaban Anda membutuhkan O (mn), di mana m adalah ukuran jendela.
Alleo
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.