Yanıtlar:
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)
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')
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
.
' mode
Argümanı np.convolve
kenarların nasıl ele alınacağını belirtir. valid
Burada 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()
numpy.cumsum
kullanımı daha iyi karmaşıklığa sahiptir.
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.
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)
np.longdouble
ama kayan nokta hatası hala nispeten çok sayıda nokta için önemli olacaktır (yaklaşık> 1e5 ama verilerinize bağlıdır)numpy.convolve
O (mn); onun dokümanlar söz scipy.signal.fftconvolve
kullandığı FFT.
running_mean([1,2,3], 2)
verir array([1, 2])
. Yerine x
göre [float(value) for value in x]
hile yok.
x
şamandıralar içeriyorsa sorun olabilir . Örnek: biri beklerken running_mean(np.arange(int(1e7))[::-1] + 0.2, 1)[-1] - 0.2
döner . Daha fazla bilgi: en.wikipedia.org/wiki/Loss_of_significance0.003125
0.0
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_mean
Herhangi 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.
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
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 numpy
ya scipy.signal
.
%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 .
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 x
olduğu N
uzun 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.
mode='valid'
içinde convolve
herhangi sonrası işlem gerektirmez hangi.
mode='valid'
geçici olanı her iki uçtan kaldırır, değil mi? Eğer len(x)=10
ve N=4
, çalışan bir ortalama için 10 sonuç isterdim ama valid
7 döndürürse.
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ış).
runningMean
Dizinin x[ctr:(ctr+N)]
sağ tarafı için diziden çıktığınızda sıfırlarla ortalamanın yan etkisi var mı ?
runningMeanFast
Ayrıca bu sınır efekti sorunu var.
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
NameError: name 'info' is not defined
. Bu hatayı alıyorum, efendim.
Kullanıma hazır bir çözüm için bkz. Https://scipy-cookbook.readthedocs.io/items/SignalSmooth.html . flat
Pencere 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)
numpy.convolve
, sadece diziyi değiştirme farkına dayanır .
w
pencere boyutu ves
veri mi?
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
:
'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.convolve
ve 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)]
uniform_filter1d
, np.convolve
bir dikdörtgen ile ve np.cumsum
bunu 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.
uniform_filter1d
bir daha hızlı cumsum
çözeltisi (2-5x yaklaşık olarak). ve çözüm uniform_filter1d
gibi büyük kayan nokta hatasıcumsum
almaz.
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 = 3
ama bu farklı (sum + 4)/3 = 3.333
.
Doğru değeri hesaplamak için bir yol çıkarma için data[0] = 1
den sum+4
bu ş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ü sum
olduğunu data[i-N] + ... + data[i-2] + data[i-1]
.
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.
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)
Bu yanıt, üç farklı senaryo için Python standart kitaplığını kullanan çözümler içerir .
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 values
jeneratö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 enumerate
kü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 next
aşamalı.
(Tabii ki, örtük olarak çağıracak rolling_avg
bir for
dö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))
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
N
Bu jeneratör fonksiyonu tekrarlanabilir ve bir pencere boyutu alır ve pencerenin N
içindeki geçerli değerlerin ortalamasını verir. deque
Listeye 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
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:
sum
kullanarak np.sum
yerine 2@
operatör (nedir hiçbir fikri) bir hata atar. Daha sonra bakabilirim ama şu anda zamanım yok
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()
pandas.rolling_mean
benimki kullanırken kullanıyor pandas.DataFrame.rolling
. Ayrıca hareket etmeyi min(), max(), sum()
vb mean()
. Bu yöntemle kolayca hesaplayabilirsiniz .
pandas.rolling_min, pandas.rolling_max
vb. Gibi farklı bir yöntem kullanmanız gerekiyor .
Bu yönteme sahip yukarıdaki cevaplardan birine gömülü mab tarafından bir yorum var. sahip basit bir hareketli ortalama olduğu:bottleneck
move_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_count
temel olarak hareketli ortalamayı dizinizdeki o noktaya kadar götürecek kullanışlı bir parametredir. Ayarlamazsanız min_count
, eşit olacaktır window
ve window
puanlara kadar her şey olacaktır nan
.
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]
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
, same
ve full
bir 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.
Yukarıda, çalışan bir ortalamanın hesaplanmasıyla ilgili birçok cevap vardır. Cevabım iki ekstra özellik ekliyor:
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):
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)
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 pandas
ya 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?
O(n*d)
hesaplamalar yapıyorsunuz ( d
pencerenin n
boyutu, ölçülebilir boyut) ve yapıyorlarO(n)
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)
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 n
gü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:
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
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
Yukarıda belirtilen tüm çözümler zayıftır çünkü
numpy.cumsum
veyaO(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] / w
ve çı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.
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.
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.