Hareketli ortalama veya hareketli ortalama


195

Python için belirli bir pencere verilen 1D dizisinin çalışma ortalamasını hesaplayan bir SciPy işlevi veya NumPy işlevi veya modülü var mı?

Yanıtlar:


24

Her şeyi bağımlılık olmadan tek bir döngüde yapan kısa, hızlı bir çözüm için aşağıdaki kod harika çalışıyor.

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
Hızlı?! Bu çözüm Numpy çözümlerinden daha yavaş büyüklük sıralarıdır.
Bart

3
Bu yerel çözüm serin olmasına rağmen, OP bir numpy / scipy işlevi istedi - muhtemelen bunlar çok daha hızlı olacaktır.
Demis

255

UPD: Alleo ve jasaarim tarafından daha verimli çözümler önerildi .


Bunun için kullanabilirsiniz np.convolve:

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

açıklama

Akan ortalama, evrişimin matematiksel işleyişidir . Akan ortalama için, bir pencereyi giriş boyunca kaydırır ve pencerenin içeriğinin ortalamasını hesaplarsınız. Ayrık 1D sinyalleri için, evrişim aynı şeydir, ancak rastgele bir doğrusal kombinasyon hesapladığınız ortalama yerine, her bir elemanı karşılık gelen bir katsayı ile çarpın ve sonuçları toplayın. Penceredeki her konum için bir tane olan bu katsayılara bazen evrişim çekirdeği denir . Şimdi, N değerlerinin aritmetik ortalaması (x_1 + x_2 + ... + x_N) / N, yani karşılık gelen çekirdek (1/N, 1/N, ..., 1/N), ve tam olarak bunu kullanarak elde ediyoruz np.ones((N,))/N.

kenarlar

' modeArgümanı np.convolvekenarların nasıl ele alınacağını belirtir. validBurada modu seçtim çünkü çoğu insanın koşmanın bu şekilde çalışmasını beklediğini düşünüyorum, ancak başka öncelikleriniz de olabilir. Modlar arasındaki farkı gösteren bir çizim:

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

Koşu ortalama kıvrım modları


5
Bu çözümü seviyorum çünkü temiz (bir satır) ve nispeten verimli (numpy içinde yapılan iş). Ancak Alleo'nun "Verimli çözüm" numpy.cumsumkullanımı daha iyi karmaşıklığa sahiptir.
Ulrich Stern

2
@denfromufa, belgelerin uygulamayı yeterince iyi kapsadığına inanıyorum ve aynı zamanda matematiği açıklayan Wikipedia'ya da bağlanıyor. Sorunun odağı düşünüldüğünde, bu cevabın bunları kopyalaması gerektiğini düşünüyor musunuz?
lapis

@lapis hareketli ortalama için içbükey kullanımı oldukça sıradışı ve açık değil. Bulduğum en iyi görsel açıklama: matlabtricks.com/post-11/moving-average-by-convolution
denfromufa

Çizim ve ilgili görevler için, Yok değerleriyle doldurmak yararlı olacaktır. (Çok güzel değil ama kısa) önerim: `` def moving_average (x, N, fill = True): dönüş np.concatenate ([[Hiçbiri] * içindeki x için x (x // 2 + N% 2) * dolgu, np.convolve (x, np.ones ((N,)) / N, mod = 'geçerli'), [Yok] * (N // 2) * dolgu,] len (x) ise]) ` `` Kod SO yorumlarında çok çirkin görünüyor xD Çok fazla olduğu gibi başka bir cevap eklemek istemedim ama sadece IDE'nize kopyalayıp yapıştırabilirsiniz.
Chaoste

146

Verimli çözüm

Konvolüsyon basit bir yaklaşımdan çok daha iyidir, ancak (sanırım) FFT kullanır ve bu nedenle oldukça yavaştır. Bununla birlikte, özellikle, çalışanı hesaplamak için aşağıdaki yaklaşımın iyi çalıştığı anlamına gelir

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

Kontrol edilecek kod

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

Not numpy.allclose(result1, result2)olan True, iki yöntem eşdeğerdir. Daha büyük N, zaman farkı daha büyük.

uyarı: cumsum daha hızlı olmasına rağmen sonuçlarınızın geçersiz / yanlış / kabul edilemez olmasına neden olabilecek artan kayan nokta hatası olacaktır.

Yorumlar burada bu kayan nokta hata sorunu işaret ama cevap burada daha belirgin hale getiriyorum. .

# 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)
  • kayan nokta hatası üzerinde daha fazla puan biriktirirseniz (yani 1e5 noktası fark edilir, 1e6 noktası daha önemlidir, 1e6'dan fazladır ve akümülatörleri sıfırlamak isteyebilirsiniz)
  • kullanarak hile yapabilirsiniz np.longdoubleama kayan nokta hatası hala nispeten çok sayıda nokta için önemli olacaktır (yaklaşık> 1e5 ama verilerinize bağlıdır)
  • hatayı çizebilir ve nispeten hızlı arttığını görebilirsiniz
  • kıvrımlı çözüm daha yavaştır, ancak bu kayan nokta hassasiyet kaybına sahip değildir
  • uniform_filter1d çözümü bu cumsum çözeltisinden daha hızlıdır ve bu kayan nokta hassasiyet kaybına sahip değildir

3
Güzel çözüm! Benim önsezim numpy.convolveO (mn); onun dokümanlar söz scipy.signal.fftconvolvekullandığı FFT.
Ulrich Stern

3
Bu yöntem dizinin kenarlarıyla ilgilenmez, değil mi?
JoVe

6
Güzel bir çözüm, ancak büyük diziler için sayısal hatalardan muzdarip olabileceğine dikkat edin, çünkü dizinin sonuna doğru küçük bir sonuç elde etmek için iki büyük sayıyı çıkarıyor olabilirsiniz.
Bas Swinckels

1
Şamandıra bölümü yerine tamsayı bölümü kullanır: running_mean([1,2,3], 2)verir array([1, 2]). Yerine xgöre [float(value) for value in x]hile yok.
ChrisW

4
Bu çözeltinin sayısal kararlılığı xşamandıralar içeriyorsa sorun olabilir . Örnek: biri beklerken running_mean(np.arange(int(1e7))[::-1] + 0.2, 1)[-1] - 0.2döner . Daha fazla bilgi: en.wikipedia.org/wiki/Loss_of_significance0.0031250.0
Milan

80

Güncelleme: Aşağıdaki örnek pandas.rolling_mean, pandaların son sürümlerinde kaldırılan eski işlevi göstermektedir . Aşağıdaki işlev çağrısının modern bir eşdeğeri

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

pandalar bunun için NumPy veya SciPy'den daha uygundur. Onun işlevi rolling_mean işi rahatlıkla yapar. Giriş bir dizi olduğunda da bir NumPy dizisi döndürür.

rolling_meanHerhangi bir özel saf Python uygulaması ile performansta yenmek zordur . Önerilen çözümlerden ikisine karşı örnek bir performans:

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

Kenar değerleriyle nasıl başa çıkılacağı konusunda da güzel seçenekler var.


6
Pandalar rolling_mean iş için güzel bir araçtır, ancak ndarrays için kullanımdan kaldırılmıştır. Gelecekte Pandalar sürümlerinde sadece Pandalar serisinde çalışacak. Panda olmayan dizi verileri için şimdi nerelere dönüyoruz?
Mike

5
@Mike rolling_mean () kullanımdan kaldırıldı, ancak artık yuvarlama ve ortalama ayrı ayrı kullanabilirsiniz: df.rolling(windowsize).mean()şimdi bunun yerine çalışıyor (çok hızlı bir şekilde ekleyebilirim). 6.000 satır serisi için 1000 döngü%timeit test1.rolling(20).mean() döndürdü , döngü başına en iyi 3: 1,16 ms
Vlox

5
@Vlox df.rolling()yeterince iyi çalışıyor, sorun bu formun gelecekte ndarraları desteklemeyeceği. Bunu kullanmak için önce verilerimizi bir Pandas Veri Çerçevesine yüklememiz gerekecek. Ben de eklendi bu işlevi görmek isterdim numpyya scipy.signal.
Mike

1
@Mike tamamen katılıyorum. Özellikle kendi dizilerim için pandalar .ewm (). Mean () hızını eşleştirmek için mücadele ediyorum (önce bir df'ye yüklemek yerine). Yani, hızlı olması harika, ama veri çerçevelerine çok sık girip çıkarken biraz hantal hissediyor.
Vlox

6
%timeit bottleneck.move_mean(x, N)benim pc cumsum ve pandalar yöntemleri 3 ila 15 kat daha hızlı. Deponun README'sindeki kriterlerine bir göz atın .
mab

50

Koşu ortalamasını aşağıdakilerle hesaplayabilirsiniz:

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

Ama yavaş.

Neyse ki, numpy işleri hızlandırmak için kullanabileceğimiz kıvrımlı bir işlev içeriyor . Akan ortalama, tüm üyelerin eşit xolduğu Nuzun bir vektörle kıvrılmaya eşdeğerdir 1/N. Kıvrımın numpy uygulaması başlangıç ​​geçişini içerir, bu nedenle ilk N-1 noktalarını kaldırmanız gerekir:

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

Makinemdeki hızlı sürüm, giriş vektörünün uzunluğuna ve ortalama penceresinin boyutuna bağlı olarak 20-30 kat daha hızlıdır.

Konvolvenin 'same'başlangıç ​​geçici sorununu ele alması gereken bir modu içerdiğini, ancak onu başlangıç ​​ve bitiş arasında böldüğünü unutmayın.


İlk N-1 noktalarının kaldırılmasının hala son noktalarda bir sınır etkisi bıraktığını unutmayın. Sorunu çözmek için daha kolay bir yolu kullanmaktır mode='valid'içinde convolveherhangi sonrası işlem gerektirmez hangi.
lapis

1
@Psycho - mode='valid'geçici olanı her iki uçtan kaldırır, değil mi? Eğer len(x)=10ve N=4, çalışan bir ortalama için 10 sonuç isterdim ama valid7 döndürürse.
mtrw

1
Geçici olanı sondan kaldırır ve başlangıçta bir tane yoktur. Sanırım bu bir öncelik meselesi, verilerde olmayan sıfıra doğru bir eğim elde etme pahasına aynı sayıda sonuca ihtiyacım yok. BTW, modlar arasındaki farkı göstermek için bir komut: modes = ('full', 'same', 'valid'); [plot(convolve(ones((200,)), ones((50,))/50, mode=m)) for m in modes]; axis([-10, 251, -.1, 1.1]); legend(modes, loc='lower center')(pyplot ve numpy içe aktarılmış).
lapis

runningMeanDizinin x[ctr:(ctr+N)]sağ tarafı için diziden çıktığınızda sıfırlarla ortalamanın yan etkisi var mı ?
mrgloom

runningMeanFastAyrıca bu sınır efekti sorunu var.
mrgloom

22

veya python için hesaplayan modül

Tradewave.net'teki testlerimde TA-lib her zaman kazanır:

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

Sonuçlar:

[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

resim açıklamasını buraya girin


NameError: name 'info' is not defined. Bu hatayı alıyorum, efendim.
Md. Rezwanul Haque

1
Görünüşe göre zaman serileri yumuşatıldıktan sonra kayıyor, istenen etki mi?
mrgloom

@mrgloom evet, görselleştirme amacıyla; aksi takdirde grafikte bir satır olarak görünürler; Md. Rezwanul Haque tüm PAIR ve info referanslarını kaldırabilir; bunlar artık geçersiz olan tradewave.net için dahili korumalı yöntemlerdi
litepresence

21

Kullanıma hazır bir çözüm için bkz. Https://scipy-cookbook.readthedocs.io/items/SignalSmooth.html . flatPencere tipi ile çalışma ortalaması sağlar . Bunun basit do-it-yourself konvolve yönteminden biraz daha sofistike olduğunu unutmayın, çünkü verilerin başında ve sonunda problemleri yansıtarak (sizin durumunuzda çalışabilir veya çalışmayabilir) başa çıkmaya çalışır. ..).

Başlamak için şunları deneyebilirsiniz:

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

1
Bu yöntem numpy.convolve, sadece diziyi değiştirme farkına dayanır .
Alleo

10
Hem girişler hem de çıkışlar aynı nitelikte olduğunda (örn. Her iki geçici sinyal), giriş sinyallerinden farklı şekillerde çıkış sinyalleri veren sinyal işleme fonksiyonundan her zaman rahatsızım. İlişkili bağımsız değişkenle (ör. Zaman, frekans) doğrudan bir konu değil çizim veya karşılaştırma yaparak yazışmayı keser ... yine de, duyguyu paylaşırsanız, önerilen işlevin son satırlarını y = np olarak değiştirmek isteyebilirsiniz. .convolve (ağırlık / w.sum (), s, mod = 'aynı'); dönüş y [window_len-1 :-( window_len-1)]
Christian O'Reilly

@ ChristianO'Reilly, bunu ayrı bir cevap olarak yayınlamalısınız - tam olarak aradığım şey bu, çünkü düzgünleştirilmiş verilerin uzunluklarına uyması gereken, çizim vb. İçin iki dizim daha var. tam olarak bunu nasıl yaptınız - wpencere boyutu ves veri mi?
Demis

@ Demis Yorum yardımcı oldu sevindim. Burada numpy konvolve fonksiyonu hakkında daha fazla bilgi docs.scipy.org/doc/numpy-1.15.0/reference/generated/… Bir evrişim fonksiyonu ( en.wikipedia.org/wiki/Convolution ) iki sinyali birbiriyle evirir . Bu durumda, sinyal (ler) inizi normalleştirilmiş (üniter alan) bir pencere (w / w.sum ()) ile birleştirir.
Christian O'Reilly

21

Scipy.ndimage.filters.uniform_filter1d komutunu kullanabilirsiniz :

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:

  • çıktıya aynı numpy biçimini verir (yani nokta sayısı)
  • 'reflect'varsayılanın olduğu sınırın üstesinden gelmek için birden fazla yol sağlar , ancak benim durumumda,'nearest'

Ayrıca oldukça hızlıdır ( yukarıda verilen cumsum yaklaşımından yaklaşık 50 kat daha hızlı np.convolveve 2-5 kat daha hızlıdır ):

%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

farklı uygulamaların hata / hızını karşılaştırmanıza olanak tanıyan 3 işlev şunlardır:

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
Sınır konularını dikkate alan tek cevap budur (özellikle çizim yaparken oldukça önemlidir). Teşekkür ederim!
Gabriel

1
I profilli uniform_filter1d, np.convolvebir dikdörtgen ile ve np.cumsumbunu takiben np.subtract. sonuçlarım: (1.) kıvrım en yavaş olanıdır. (2.) cumsum / çıkarma yaklaşık 20-30x daha hızlıdır. (3.) uniform_filter1d cumsum / çıkarma işleminden yaklaşık 2-3 kat daha hızlıdır. kazanan kesinlikle uniform_filter1d.
Trevor Boyd Smith

kullanarak uniform_filter1dbir daha hızlı cumsumçözeltisi (2-5x yaklaşık olarak). ve çözüm uniform_filter1d gibi büyük kayan nokta hatasıcumsum almaz.
Trevor Boyd Smith

15

Bunun eski bir soru olduğunu biliyorum, ama burada ekstra veri yapıları veya kütüphaneleri kullanmayan bir çözüm var. Giriş listesindeki elemanların sayısında lineerdir ve daha verimli hale getirmenin başka bir yolunu düşünemiyorum (aslında sonucu tahsis etmenin daha iyi bir yolunu bilen biri varsa, lütfen bana bildirin).

NOT: Bu liste yerine numpy dizi kullanarak çok daha hızlı olurdu, ama tüm bağımlılıkları ortadan kaldırmak istedim. Çok iş parçacıklı yürütme ile performansı artırmak da mümkün olabilir

İşlev, giriş listesinin bir boyutlu olduğunu varsayar, bu yüzden dikkatli olun.

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

Misal

Bir listemiz olduğunu varsayın data = [ 1, 2, 3, 4, 5, 6 ]Üzerinde 3 periyodu olan bir yuvarlama ortalaması hesaplamak istediğimiz sahip olduğumuzu ve aynı zamanda girdi ile aynı boyutta bir çıktı listesi istediğinizi (çoğunlukla durum budur).

İlk elemanın indeksi 0'dır, dolayısıyla yuvarlama ortalaması -2, -1 ve 0 indeksleri üzerinde hesaplanmalıdır. Açıkçası verilerimiz [-2] ve verilerimiz [-1] (özel kullanmak istemiyorsanız) sınır koşulları), bu nedenle bu öğelerin 0 olduğunu varsayıyoruz. Bu, listeyi sıfır doldurmaya eşdeğerdir, ancak gerçekten doldurmuyoruz, sadece dolgu gerektiren indeksleri takip edin (0'dan N-1'e).

Yani, ilk N element için sadece bir akümülatördeki elementleri toplamaya devam ediyoruz.

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

N + 1 ileri elemanlarından basit birikim çalışmaz. bekliyoruz result[3] = (2 + 3 + 4)/3 = 3ama bu farklı (sum + 4)/3 = 3.333.

Doğru değeri hesaplamak için bir yol çıkarma için data[0] = 1den sum+4bu şekilde verilmesi, sum + 4 - 1 = 9.

Bunun nedeni şu anda gerçekleşir sum = data[0] + data[1] + data[2], ama her için de geçerlidir i >= N, çıkarma önce Çünkü sumolduğunu data[i-N] + ... + data[i-2] + data[i-1].


12

Bunun darboğaz kullanarak zarif bir şekilde çözülebileceğini hissediyorum

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

import numpy as np
import bottleneck as bn

a = np.random.randint(4, 1000, size=100)
mm = bn.move_mean(a, window=5, min_count=1)
  • "mm", "a" için hareketli ortalamadır.

  • "window", hareketli ortalama için dikkate alınacak maksimum giriş sayısıdır.

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

İyi kısmı, Darboğaz nan değerleri ile başa çıkmak için yardımcı olur ve aynı zamanda çok etkilidir.


Bu lib gerçekten hızlı. Saf Python hareketli ortalama işlevi yavaştır. Bootleneck, istikrarlı olduğunu ve Python topluluğundan sürekli destek alabileceğini düşündüğüm bir PyData kütüphanesidir, neden kullanmıyorsunuz?
GoingMyWay

6

Bunun ne kadar hızlı olduğunu henüz kontrol etmedim, ancak deneyebilirsiniz:

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
Yapacağım şey buydu. Herkes bunun neden kötü bir yol olduğunu eleştirebilir mi?
staggart

1
Bu basit python çözümü numpy gerektirmeden benim için iyi çalıştı. Yeniden kullanım için bir sınıfa yuvarladım.
Matthew Tschiegg

6

Bu yanıt, üç farklı senaryo için Python standart kitaplığını kullanan çözümler içerir .


İle çalışan ortalama itertools.accumulate

Bu, çalışan ortalamanın kaldıraçlı bir değer üzerinden yinelenen değerleri hesaplayan, bellek açısından verimli bir Python 3.2+ çözümüdür itertools.accumulate.

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

Not valuesjeneratörleri ya da anında değerleri üreten herhangi bir başka nesneye dahil olmak üzere herhangi bir iterable olabilir.

İlk olarak, değerlerin kümülatif toplamını tembel olarak oluşturun.

>>> cumu_sum = accumulate(value_stream)

Sonra enumeratekümülatif toplam (1'den başlayarak) ve biriken değerlerin oranını ve mevcut numaralandırma endeksini veren bir jeneratör oluşturun.

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

Sen verebilir means = list(rolling_avg)aramak kez ya da bellekte tüm değerleri gerekirse nextaşamalı.
(Tabii ki, örtük olarak çağıracak rolling_avgbir fordöngü ile de tekrarlayabilirsiniz next.)

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

Bu çözüm aşağıdaki gibi bir fonksiyon olarak yazılabilir.

from itertools import accumulate

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

Bir eşyordam istediğiniz zaman değerlerini gönderebilirsiniz hangi

Bu yardımcı program, gönderdiğiniz değerleri tüketir ve şimdiye kadar görülen değerlerin çalışma ortalamasını korur.

Yinelenebilir bir değeriniz olmadığında, ancak programınızın ömrü boyunca farklı zamanlarda ortalama değerlerin tek tek elde edilmesinde faydalıdır.

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
        

Ortak program şu şekilde çalışır:

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

Ortamı kayan bir boyut penceresi üzerinde hesaplama N

Bu jeneratör fonksiyonu tekrarlanabilir ve bir pencere boyutu alır ve pencerenin N içindeki geçerli değerlerin ortalamasını verir. dequeListeye benzer bir veri yapısı olan ancak her iki uç noktasında da hızlı değişiklikler ( pop, append) için optimize edilmiş a kullanır .

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)
        

İşte fonksiyon:

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

Partiye biraz geç, ama daha sonra ortalama bulmak için kullanılan sıfırlar ile uçları veya pedleri etrafında sarmaz kendi küçük işlevi yaptım. Daha ileri bir muamele olarak, sinyalin doğrusal aralıklı noktalarda da yeniden örneklenmesidir. Diğer özellikleri almak için kodu istediğiniz gibi özelleştirin.

Yöntem, normalleştirilmiş bir Gauss çekirdeği ile basit bir matris çarpımıdır.

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

Normal dağıtılmış gürültü eklenmiş sinüzoidal sinyalde basit kullanım: resim açıklamasını buraya girin


Bu benim için işe yaramıyor (python 3.6). 1 adında bir fonksiyon yoktur sumkullanarak np.sumyerine 2@ operatör (nedir hiçbir fikri) bir hata atar. Daha sonra bakabilirim ama şu anda zamanım yok
Bastian

@Uygular matris çarpım operatörüdür np.matmul . y_inDizinizin sayısal bir dizi olup olmadığını kontrol edin , sorun olabilir.
xyzzyqed

5

Numpy veya scipy yerine, pandaların bunu daha hızlı yapmasını tavsiye ederim:

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

Bu, "veri" kolonunun 3 periyodunun hareketli ortalamasını (MA) alır. Değiştirilmiş sürümleri de hesaplayabilirsiniz, örneğin geçerli hücreyi hariç tutan sürüm (bir geriye kaydırılmış) aşağıdaki gibi kolayca hesaplanabilir:

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

Bunun 2016'da önerilen çözümden farkı nedir?
Bay T

2
2016'da önerilen çözüm pandas.rolling_meanbenimki kullanırken kullanıyor pandas.DataFrame.rolling. Ayrıca hareket etmeyi min(), max(), sum()vb mean(). Bu yöntemle kolayca hesaplayabilirsiniz .
Gursel Karacor

İlkinde pandas.rolling_min, pandas.rolling_maxvb. Gibi farklı bir yöntem kullanmanız gerekiyor .
Gursel Karacor

4

Bu yönteme sahip yukarıdaki cevaplardan birine gömülü mab tarafından bir yorum var. sahip basit bir hareketli ortalama olduğu:bottleneckmove_mean

import numpy as np
import bottleneck as bn

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

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

min_counttemel olarak hareketli ortalamayı dizinizdeki o noktaya kadar götürecek kullanışlı bir parametredir. Ayarlamazsanız min_count, eşit olacaktır windowve windowpuanlara kadar her şey olacaktır nan.


3

Başka bir yaklaşım hareketli ortalama bulmak için olmadan numpy kullanarak, panda

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

yazdıracak [2.0, 4.0, 6.0, 6.5, 7.4, 7.833333333333333]


itertools.accumulate python 2.7'de mevcut değildir, ancak python 3.4'te mevcut değildir
grayaii

3

Bu soru, NeXuS'un geçen ay hakkında yazdığı zamandan daha eski , AMA Kodunun uç durumlarla nasıl başa çıktığını seviyorum. Ancak, "basit bir hareketli ortalama" olduğu için, sonuçları uygulandıkları verilerin gerisinde kalmaktadır. Ben numpy en modları daha tatmin edici bir şekilde uç örnekleri ele düşündük valid, sameve fullbir benzer bir yaklaşım uygulayarak elde edilebilir convolution()tabanlı yöntemle.

Katkım, sonuçlarını verileriyle hizalamak için merkezi bir çalışma ortalaması kullanıyor. Tam boyutlu pencerenin kullanılabilmesi için çok az nokta olduğunda, çalışma ortalamaları dizinin kenarlarındaki ardışık olarak daha küçük pencerelerden hesaplanır. [Aslında, arka arkaya daha büyük pencerelerden, ama bu bir uygulama detayı.]

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

Göreceli olarak yavaştır, çünkü kullanır convolve()ve muhtemelen gerçek bir Pythonista tarafından oldukça fazla vurgulanabilir, ancak fikrin ayakta olduğuna inanıyorum.


3

Yukarıda, çalışan bir ortalamanın hesaplanmasıyla ilgili birçok cevap vardır. Cevabım iki ekstra özellik ekliyor:

  1. nan değerlerini yok sayar
  2. ilgilenilen değeri içeren DEĞİL N komşu değerlerin ortalamasını hesaplar

Bu ikinci özellik, hangi değerlerin genel eğilimden belirli bir miktarda farklı olduğunu belirlemek için özellikle yararlıdır.

En verimli yöntem olduğu için numpy.cumsum kullanıyorum ( yukarıdaki Alleo'nun cevabına bakınız ).

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)

Bu kod yalnızca N'ler için bile geçerlidir. Padded_x ve n_nan'ın np.insert'i değiştirilerek tek sayılar için ayarlanabilir.

Örnek çıktı (siyah ham, mavi movavg): Ham değer (siyah) ve her değer etrafında 10 puanlık hareketli ortalama (mavi), bu değer dahil değil.  nan değerleri dikkate alınmaz.

Bu kod, cutoff = 3 nan olmayan değerlerden daha az hesaplanan tüm hareketli ortalama değerleri kaldıracak şekilde kolayca uyarlanabilir.

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)

ham veri (siyah) ve hareketli ortalama (mavi) 3 naneden az değer içeren herhangi bir pencereyi yok sayarken


2

Yalnızca Python Standart Kütüphanesini Kullan (Bellek Verimli)

Sadece standart kütüphaneyi kullanmanın başka bir versiyonunu verin deque. Cevapların çoğunun pandasya da kullanması benim için oldukça şaşırtıcı 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]

Aslında python belgelerinde başka bir uygulama buldum

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

Ancak uygulama bana göre olması gerekenden biraz daha karmaşık. Ama bir nedenden ötürü standart python belgelerinde olmalı, birisi benim ve standart dokümanın uygulanması hakkında yorum yapabilir mi?


2
Her yinelemede pencere üyelerini toplamaya devam ettiğiniz büyük bir fark ve toplamı verimli bir şekilde günceller (bir üyeyi kaldırın ve başka bir tane ekleyin). karmaşıklık açısından O(n*d) hesaplamalar yapıyorsunuz ( dpencerenin nboyutu, ölçülebilir boyut) ve yapıyorlarO(n)
Iftah

@İftah, güzel, açıklama için teşekkürler, haklısın.
MaThMaX

2

@ Aikude değişkenleri ile bir astar yazdım.

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

Burada bu sorunun çözümü olsa da, lütfen çözümüme bir göz atın. Çok basit ve iyi çalışıyor.

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

Diğer cevapları okuduğumdan, sorunun sorulmasının bu olduğunu düşünmüyorum, ancak buraya, boyut olarak büyüyen değerlerin bir listesini ortalama olarak tutmak ihtiyacım var.

Dolayısıyla, bir yerden edindiğiniz değerlerin bir listesini (bir site, bir ölçüm cihazı, vb.) Ve ngüncellenen son değerlerin ortalamasını saklamak istiyorsanız, yeni ekleme çabasını en aza indiren aşağıdaki kodu kullanabilirsiniz elementler:

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)

Ve bunu test edebilirsiniz, örneğin:

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

Hangi verir:

Değerlerin bir fonksiyonu olarak değerler ve ortalamaları #


1

Sadece standart bir kütüphane ve deque kullanan başka bir çözüm:

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

Eğitim amaçlı olarak, iki tane daha Numpy çözümü eklememe izin verin (bunlar cumsum çözeltisinden daha yavaştır):

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

Kullanılan işlevler: as_strided , add.reduceat


1

Yukarıda belirtilen tüm çözümler zayıftır çünkü

  • nümerik vektörize bir uygulama yerine yerel bir python nedeniyle hız,
  • kötü kullanımından dolayı sayısal kararlılık numpy.cumsumveya
  • O(len(x) * w)kıvrım olarak uygulamalar nedeniyle hız .

verilmiş

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

Not x_[:w].sum()eşittir x[:w-1].sum(). Yani ilk ortalama numpy.cumsum(...)ekler x[w] / w(via x_[w+1] / w) ve çıkarır 0(dan x_[0] / w). Bu sonuçx[0:w].mean()

Cumsum aracılığıyla, ekleyerek x[w+1] / wve çıkararak ikinci ortalamayı güncelleyeceksiniz x[0] / w, sonuçta x[1:w+1].mean().

Bu x[-w:].mean(), ulaşılana kadar devam eder .

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

Bu çözelti vektörleştirilir, O(m)okunabilir ve sayısal olarak kararlıdır.


1

Hareketli bir ortalama filtreye ne dersiniz ? Aynı zamanda tek astarlıdır ve dikdörtgenden başka bir şeye ihtiyacınız varsa pencere türünü kolayca değiştirebilmeniz gibi bir avantaja sahiptir. a dizisinin N uzunluğunda basit hareketli ortalaması:

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

Üçgen pencere uygulandığında:

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

Not: Genellikle ilk N örneklerini [N:]sonunda sahte olarak atarım , ancak gerekli değildir ve sadece kişisel bir seçim meselesi.


-7

Varolan bir kitaplığı kullanmak yerine kendi kitaplığınızı döndürmeyi seçerseniz, kayan nokta hatasının farkında olun ve etkilerini en aza indirmeye çalışın:

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

Tüm değerleriniz kabaca aynı büyüklük sırasına sahipse, bu her zaman kabaca benzer büyüklüklerde değerler ekleyerek hassasiyeti korumaya yardımcı olacaktır.


15
Bu son derece belirsiz bir cevaptır, en azından kodda bazı yorumlar veya bunun kayan nokta hatasının neden iyi olduğu konusunda açıklama.
Gabe

Son cümlemde kayan nokta hatasına neden yardımcı olduğunu göstermeye çalışıyordum. İki değer yaklaşık olarak aynı büyüklük sırasına sahipse, bu değerlerin eklenmesi, çok küçük bir sayıya çok büyük bir sayı eklediğinizden daha az kesinlik kazanır. Kod, "bitişik" değerleri, kayar nokta hatasını en aza indirmek için ara toplamların bile her zaman makul ölçüde yakın olması gerektiği şekilde birleştirir. Hiçbir şey aptalca bir kanıt değildir, ancak bu yöntem üretimde çok az uygulanan birkaç projeyi kurtarmıştır.
Mayur Patel

1. orijinal sorun uygulanan, bu korkunç yavaş (hesaplama ortalama), bu yüzden bu sadece ilgisiz 2. 64-bit sayıların hassasiyet sorunu muzdarip, biri yaklaşık 2 ^ 30 özetlemek zorunda eşit sayılar.
Alleo

@Alleo: Değer başına bir ek yapmak yerine iki tane yapacaksınız. Kanıt, bit çevirme problemiyle aynıdır. Ancak, bu cevabın amacı mutlaka performans değil, kesinliktir. 64 bit değerlerin ortalaması için bellek kullanımı önbellekteki 64 öğeyi aşmayacaktır, bu nedenle bellek kullanımında da dostudur.
Mayur Patel

Evet, bunun basit toplamdan 2 kat daha fazla işlem gerektirdiğini haklıyorsunuz, ancak asıl sorun sadece toplam değil hesaplama işlem ortalamasıdır . Bu, O (n) 'de yapılabilir, ancak cevabınız m'nin pencere boyutu olduğu O (mn) gerektirir.
Alleo
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.