जवाबों:
एक छोटे, तेज समाधान के लिए जो पूरी तरह से एक लूप में करता है, निर्भरता के बिना, नीचे दिए गए कोड महान काम करता है।
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 और jasaarim द्वारा प्रस्तावित किए गए हैं ।
आप इसके लिए उपयोग कर सकते हैं np.convolve
:
np.convolve(x, np.ones((N,))/N, mode='valid')
चल रहा है मतलब की गणितीय प्रक्रिया का एक मामला है घुमाव । रनिंग माध्य के लिए, आप इनपुट के साथ एक विंडो को स्लाइड करते हैं और विंडो की सामग्री के माध्य की गणना करते हैं। असतत 1D संकेतों के लिए, कन्वेंशन एक ही चीज है, मतलब के बजाय आप एक मनमाना रैखिक संयोजन की गणना करते हैं, अर्थात प्रत्येक तत्व को एक गुणांक से गुणा करते हैं और परिणाम जोड़ते हैं। वे गुणांक, जो खिड़की में प्रत्येक स्थिति के लिए होते हैं, कभी-कभी कन्वेक्शन कर्नेल कहलाते हैं । अब, N मानों का अंकगणित माध्य है (x_1 + x_2 + ... + x_N) / N
, इसलिए संबंधित कर्नेल है (1/N, 1/N, ..., 1/N)
, और ठीक वैसा ही है जैसा कि हम उपयोग करके प्राप्त करते हैंnp.ones((N,))/N
।
किनारों को संभालने के तरीके को निर्दिष्ट करने का mode
तर्क np.convolve
। मैंने valid
यहां विधा को चुना क्योंकि मुझे लगता है कि ज्यादातर लोग उम्मीद करते हैं कि काम करने के लिए दौड़ने का मतलब होगा, लेकिन आपके पास अन्य प्राथमिकताएं हो सकती हैं। यहाँ एक प्लॉट है जो मोड के बीच अंतर दिखाता है:
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
से बेहतर जटिलता होती है।
सीधा दृष्टिकोण की तुलना में बातचीत बहुत बेहतर है, लेकिन (मुझे लगता है) यह एफएफटी का उपयोग करता है और इस प्रकार काफी धीमा है। हालाँकि, विशेष रूप से चल रहे कंप्यूटिंग के लिए निम्न दृष्टिकोण ठीक काम करता है
def running_mean(x, N):
cumsum = numpy.cumsum(numpy.insert(x, 0, 0))
return (cumsum[N:] - cumsum[:-N]) / float(N)
जाँचने का कोड
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
ध्यान दें कि numpy.allclose(result1, result2)
हैTrue
, दो तरीकों के बराबर हैं। अधिक से अधिक एन, समय में अधिक अंतर।
# 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
लेकिन आपकी फ़्लोटिंग पॉइंट त्रुटि अभी भी अपेक्षाकृत बड़ी संख्या में अंकों के लिए महत्वपूर्ण होगी (लगभग> 1e5 लेकिन आपके डेटा पर निर्भर करती है)numpy.convolve
O (mn) है; इसके डॉक्स उल्लेख करते हैं कि scipy.signal.fftconvolve
एफएफटी का उपयोग करता है।
running_mean([1,2,3], 2)
देता है array([1, 2])
। x
द्वारा प्रतिस्थापित करने [float(value) for value in x]
की चाल है।
x
इसमें फ़्लोट्स शामिल हैं। उदाहरण: जब कोई उम्मीद करता है तो running_mean(np.arange(int(1e7))[::-1] + 0.2, 1)[-1] - 0.2
वह लौटता 0.003125
है 0.0
। अधिक जानकारी: en.wikipedia.org/wiki/Loss_of_significance
अद्यतन: नीचे pandas.rolling_mean
दिया गया उदाहरण पुराने फ़ंक्शन को दिखाता है जिसे पंडों के हाल के संस्करणों में हटा दिया गया है। नीचे फ़ंक्शन कॉल का एक आधुनिक समकक्ष होगा
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])
नुमा या SciPy की तुलना में पांडा इसके लिए अधिक उपयुक्त है। इसका कार्य रोलिंग_मैंन आसानी से करता है। जब इनपुट एक सरणी होता है तो यह एक NumPy सरणी भी देता है।
rolling_mean
किसी भी कस्टम शुद्ध पायथन कार्यान्वयन के साथ प्रदर्शन में हरा करना मुश्किल है । यहाँ प्रस्तावित समाधानों में से दो के खिलाफ एक उदाहरण प्रदर्शन है:
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
वहाँ भी अच्छे विकल्प हैं कि कैसे बढ़त मूल्यों से निपटने के लिए।
df.rolling(windowsize).mean()
अब इसके बजाय काम करता है (बहुत जल्दी मैं जोड़ सकता हूं)। 6,000 पंक्ति के लिए श्रृंखला %timeit test1.rolling(20).mean()
लौटे 1000 छोरों, 3 का सबसे अच्छा: पाश प्रति 1.16 एमएस
df.rolling()
काफी अच्छी तरह से काम करता है, समस्या यह है कि यह फ़ॉर्म भविष्य में ndarrays का समर्थन नहीं करेगा। इसका उपयोग करने के लिए हमें पहले अपने डेटा को पंडों के डेटाफ्रेम में लोड करना होगा। मैं इस समारोह या तो करने के लिए जोड़ा देखना पसंद करेंगे numpy
या scipy.signal
।
%timeit bottleneck.move_mean(x, N)
मेरे पीसी पर कमसुम और पांडा के तरीकों की तुलना में 3 से 15 गुना तेज है। रेपो की README में उनके बेंचमार्क को देखें ।
आप एक रनिंग माध्य की गणना कर सकते हैं:
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
लेकिन यह धीमा है।
सौभाग्य से, numpy एक भी शामिल है convolve समारोह जो हम ऊपर गति बातें करने के लिए उपयोग कर सकते हैं। रनिंग माध्य x
एक वेक्टर के साथ संकलित करने के बराबर है जो N
लंबा है, सभी सदस्यों के बराबर है 1/N
। दृढ़ संकल्प के कार्यान्वयन में शुरुआती क्षणिक शामिल हैं, इसलिए आपको पहले N-1 अंक निकालना होगा:
def runningMeanFast(x, N):
return np.convolve(x, np.ones((N,))/N)[(N-1):]
मेरी मशीन पर, इनपुट वेक्टर की लंबाई और औसत विंडो के आकार के आधार पर, तेज संस्करण 20-30 गुना तेज है।
ध्यान दें कि 'same'
हल करने वाले में एक मोड शामिल है जो ऐसा लगता है जैसे इसे शुरुआती क्षणिक मुद्दे को संबोधित करना चाहिए, लेकिन यह इसे शुरुआत और अंत के बीच विभाजित करता है।
mode='valid'
में convolve
है जो किसी भी पोस्ट-प्रोसेसिंग की आवश्यकता नहीं है।
mode='valid'
दोनों छोर से क्षणिक को हटाता है, है ना? तो len(x)=10
और N=4
, एक चलाने के लिए मतलब मैं चाहेगा 10 परिणाम लेकिन valid
7. रिटर्न
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 और numpy आयातित के साथ)।
runningMean
क्या मेरे पास शून्य के साथ औसत का दुष्प्रभाव है, जब आप सरणी x[ctr:(ctr+N)]
के दाईं ओर के लिए सरणी से बाहर जाते हैं ।
runningMeanFast
यह भी सीमा प्रभाव मुद्दा है।
या अजगर के लिए मॉड्यूल जो गणना करता है
Tradewave.net पर मेरे परीक्षणों में TA-lib हमेशा जीतता है:
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])
परिणाम:
[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
। मुझे यह त्रुटि मिल रही है, सर।
रेडी-टू-यूज़ समाधान के लिए, https://scipy-cookbook.readthedocs.io/items/SignalSmooth.html देखें । यह रनिंग औसत प्रदान करता हैflat
विंडो प्रकार के । ध्यान दें कि यह सरल-से-अपने आप को हल करने की विधि की तुलना में थोड़ा अधिक परिष्कृत है, क्योंकि यह डेटा की शुरुआत और अंत में समस्याओं को संभालने की कोशिश करता है इसे प्रतिबिंबित करके (जो आपके मामले में काम कर भी सकता है और नहीं भी। ..)।
शुरू करने के लिए, आप कोशिश कर सकते हैं:
a = np.random.random(100)
plt.plot(a)
b = smooth(a, window='flat')
plt.plot(b)
numpy.convolve
केवल अनुक्रम को बदलने में अंतर पर निर्भर करती है ।
w
खिड़की का आकार, और s
डेटा है?
आप 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
:
'reflect'
है, जहां डिफ़ॉल्ट है, लेकिन मेरे मामले में, मैं बल्कि चाहता था'nearest'
यह बल्कि त्वरित भी है ( ऊपर दिए गए कम्सम दृष्टिकोण की तुलना में लगभग 50 गुना अधिक तेज np.convolve
और 2-5 गुना तेज ):
%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
यहां 3 कार्य हैं जो आपको विभिन्न कार्यान्वयनों की त्रुटि / गति की तुलना करने देते हैं:
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
, एक आयत के साथ और np.cumsum
के द्वारा पीछा किया np.subtract
। मेरे परिणाम: (1.) सबसे धीमा है। (२.) कमसुम / घटाना लगभग २०-३०x तेज है। (३.) वर्दी_फिल्टर १ डी लगभग २-३ एक्स की तुलना में कमसिन / घटाव से अधिक तेज है। विजेता निश्चित रूप से वर्दी_फिल्टर 1 डी है।
uniform_filter1d
रहा है तेजी से cumsum
समाधान (2-5x के बारे में द्वारा)। और uniform_filter1d
बड़े पैमाने पर फ्लोटिंग पॉइंट एरर नहीं मिलता है जैसेcumsum
समाधान करता है।
मुझे पता है कि यह एक पुराना सवाल है, लेकिन यहां एक समाधान है जो किसी भी अतिरिक्त डेटा संरचनाओं या पुस्तकालयों का उपयोग नहीं करता है। यह इनपुट सूची के तत्वों की संख्या में रैखिक है और मैं इसे और अधिक कुशल बनाने के लिए किसी अन्य तरीके के बारे में नहीं सोच सकता (वास्तव में अगर किसी को परिणाम को आवंटित करने का एक बेहतर तरीका पता है, तो कृपया मुझे बताएं)।
ध्यान दें: यह एक सूची के बजाय एक संख्यात्मक सरणी का उपयोग करके बहुत तेज़ी से होगा, लेकिन मैं सभी निर्भरता को समाप्त करना चाहता था। बहु-थ्रेडेड निष्पादन द्वारा प्रदर्शन में सुधार करना भी संभव होगा
फ़ंक्शन मानता है कि इनपुट सूची एक आयामी है, इसलिए सावधान रहें।
### 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
उदाहरण
मान लें कि हमारे पास एक सूची data = [ 1, 2, 3, 4, 5, 6 ]
है जिस पर हम 3 की अवधि के साथ एक रोलिंग माध्य की गणना करना चाहते हैं, और यह भी कि आप एक आउटपुट सूची चाहते हैं जो इनपुट एक के समान आकार है (जो कि अक्सर होता है)।
पहले तत्व में इंडेक्स 0 है, इसलिए रोलिंग माध्य को इंडेक्स -2, -1 और 0. के तत्वों पर गणना की जानी चाहिए। जाहिर है हमारे पास डेटा नहीं है [-2] और डेटा [-1] (जब तक आप विशेष का उपयोग नहीं करना चाहते हैं सीमा की स्थिति), इसलिए हम मानते हैं कि वे तत्व 0. हैं। यह सूची को शून्य-पेडिंग करने के बराबर है, सिवाय इसके कि हम वास्तव में इसे पैड नहीं करते हैं, बस उन सूचकांकों का ध्यान रखें जिन्हें पैडिंग की आवश्यकता होती है (0 से एन -1 तक)।
तो, पहले N तत्वों के लिए हम एक संचयक में तत्वों को जोड़ते रहते हैं।
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
तत्वों से एन + 1 फॉरवर्ड सरल संचय काम नहीं करता है। हम उम्मीद करते हैं result[3] = (2 + 3 + 4)/3 = 3
लेकिन यह अलग है (sum + 4)/3 = 3.333
।
सही मूल्य की गणना करने का तरीका इस प्रकार data[0] = 1
से घटाना है ।sum+4
sum + 4 - 1 = 9
वर्तमान में ऐसा इसलिए होता है sum = data[0] + data[1] + data[2]
, लेकिन i >= N
घटाव से पहले, यह प्रत्येक के लिए सही sum
है data[i-N] + ... + data[i-2] + data[i-1]
।
मुझे लगता है कि यह अड़चन का उपयोग करके हल किया जा सकता है
नीचे मूल नमूना देखें:
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" का मूविंग माध्य है।
"विंडो" चलती औसत के लिए विचार करने के लिए प्रविष्टियों की अधिकतम संख्या है।
"min_count" चलती मीनिंग के लिए विचार करने के लिए प्रविष्टियों की न्यूनतम संख्या है (जैसे पहले कुछ तत्वों के लिए या यदि सरणी में नैनो मान हैं)।
अच्छा हिस्सा टोंटी नेक नेन मूल्यों से निपटने में मदद करता है और यह बहुत कुशल भी है।
मैंने अभी तक जाँच नहीं की है कि यह कितना तेज़ है, लेकिन आप कोशिश कर सकते हैं:
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)
इस उत्तर में तीन अलग-अलग परिदृश्यों के लिए पायथन मानक पुस्तकालय का उपयोग करने वाले समाधान हैं।
itertools.accumulate
यह एक स्मृति कुशल पायथन 3.2+ समाधान है जो लीवरेजिंग द्वारा चलने वाले मूल्यों के चलने योग्य औसत पर गणना करता है itertools.accumulate
।
>>> from itertools import accumulate
>>> values = range(100)
ध्यान दें कि values
कोई भी चलने योग्य हो सकता है, जिसमें जनरेटर या कोई अन्य वस्तु शामिल है जो मक्खी पर मूल्यों का उत्पादन करती है।
सबसे पहले, आलसी मूल्यों के संचयी योग का निर्माण करते हैं।
>>> cumu_sum = accumulate(value_stream)
अगला, enumerate
संचयी योग (1 से शुरू) और एक जनरेटर का निर्माण करता है जो संचित मूल्यों और वर्तमान गणना सूचकांक के अंश का उत्पादन करता है।
>>> rolling_avg = (accu/i for i, accu in enumerate(cumu_sum, 1))
means = list(rolling_avg)
यदि आप एक बार में सभी मानों को स्मृति में रख सकते हैं या next
वृद्धिशील रूप से कॉल कर सकते हैं।
(बेशक, आप rolling_avg
एक for
लूप के साथ भी पुनरावृति कर सकते हैं , जो 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
इस समाधान को निम्नानुसार फ़ंक्शन के रूप में लिखा जा सकता है।
from itertools import accumulate
def rolling_avg(iterable):
cumu_sum = accumulate(iterable)
yield from (accu/i for i, accu in enumerate(cumu_sum, 1))
यह coroutine आपके द्वारा भेजे गए मूल्यों का उपभोग करता है और अब तक देखे गए मूल्यों का एक औसत चल रहा है।
यह तब उपयोगी होता है जब आपके पास मानों का चलने योग्य नहीं होता है, लेकिन आपके कार्यक्रम के जीवन भर में अलग-अलग समय पर एक-एक करके औसत मूल्यों को प्राप्त करना होता है।
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
कोरटाइन इस तरह काम करता है:
>>> 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
यह जनरेटर-फ़ंक्शन एक पुनरावृत्त और एक खिड़की का आकार लेता है और खिड़की के N
अंदर मौजूदा मूल्यों पर औसत पैदावार देता है। यह एक का उपयोग करता है deque
, जो कि एक सूची के समान डेटास्ट्रक्चर है, लेकिन दोनों समापन बिंदुओं पर तेज संशोधनों ( pop
। append
) के लिए अनुकूलित है ।
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)
यहाँ क्रिया में कार्य है:
>>> 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
पार्टी में थोड़ी देर हो गई है, लेकिन मैंने अपना एक छोटा सा फंक्शन बनाया है, जो ज़ीरो के साथ सिरों या पैड्स के चारों ओर लपेटता नहीं है जो तब औसत खोजने के लिए उपयोग किए जाते हैं। एक और उपचार के रूप में, यह है कि यह रैखिक रूप से स्थान बिंदुओं पर संकेत को भी पुन: नमूना करता है। अन्य सुविधाओं को प्राप्त करने के लिए कोड को अनुकूलित करें।
विधि एक सामान्य मैट्रिक्स गुणन है जो सामान्यीकृत गाऊसी कर्नेल के साथ है।
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
जोड़ा सामान्य वितरित शोर के साथ एक sinusoidal संकेत पर एक सरल उपयोग:
sum
, np.sum
इसके बजाय 2 का उपयोग करके @
ऑपरेटर (कोई विचार नहीं है कि क्या है) एक त्रुटि फेंकता है। मैं इसे बाद में देख सकता हूं, लेकिन अभी मेरे पास समय की कमी है
इसके बजाय मैं खस्ता या डरपोक हूं, मैं पंडों को यह और अधिक तेजी से करने की सलाह दूंगा:
df['data'].rolling(3).mean()
यह कॉलम "डेटा" के 3 अवधियों की चलती औसत (एमए) लेता है। आप शिफ्ट किए गए संस्करणों की गणना भी कर सकते हैं, उदाहरण के लिए, जो वर्तमान सेल को छोड़ देता है (एक वापस स्थानांतरित) को आसानी से गणना की जा सकती है:
df['data'].shift(periods=1).rolling(3).mean()
pandas.rolling_mean
मेरा उपयोग करते समय उपयोग करता है pandas.DataFrame.rolling
। आप इस पद्धति के साथ-साथ चलती min(), max(), sum()
आदि की गणना भी mean()
आसानी से कर सकते हैं।
pandas.rolling_min, pandas.rolling_max
आदि। वे समान हैं फिर भी अलग हैं।
वहाँ से एक टिप्पणी है mab में से एक में दफन जवाब जो ऊपर इस पद्धति है। 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
एक आसान पैरामीटर है जो मूल रूप से आपके सरणी में उस बिंदु तक चलती औसत ले जाएगा। यदि आप सेट नहीं करते हैं min_count
, तो यह बराबर होगा window
, और window
अंक तक सब कुछ होगा nan
।
सुन्न, पांडा का उपयोग किए बिना चलती औसत को खोजने के लिए एक और दृष्टिकोण
import itertools
sample = [2, 6, 10, 8, 11, 10]
list(itertools.starmap(lambda a,b: b/a,
enumerate(itertools.accumulate(sample), 1)))
प्रिंट करेगा [2.0, 4.0, 6.0, 6.5, 7.4, 7.833333333333333]
यह सवाल अब उससे भी पुराना है जब NeXuS ने पिछले महीने इसके बारे में लिखा था, लेकिन क्या मुझे यह पसंद है कि उसका कोड एज केसों से कैसे निपटता है। हालाँकि, क्योंकि यह एक "सरल चलती औसत" है, जिसके परिणाम उनके द्वारा लागू किए गए डेटा से पिछड़ जाते हैं। मैंने सोचा था कि NumPy की विधियों की तुलना में एक अधिक संतोषजनक ढंग से बढ़त मामलों से निपटने है कि valid
, same
, और full
एक करने के लिए एक समान दृष्टिकोण को लागू करने के द्वारा प्राप्त किया जा सकता है convolution()
आधारित पद्धति।
मेरा योगदान अपने डेटा के साथ परिणामों को संरेखित करने के लिए एक केंद्रीय रनिंग औसत का उपयोग करता है। जब फुल-साइज़ विंडो का उपयोग करने के लिए बहुत कम अंक उपलब्ध होते हैं, तो रनिंग औसत को सरणी के किनारों पर क्रमिक रूप से छोटी खिड़कियों से गणना की जाती है। [वास्तव में, क्रमिक रूप से बड़ी खिड़कियों से, लेकिन यह एक कार्यान्वयन विवरण है।]
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]])
यह अपेक्षाकृत धीमी गति से है क्योंकि यह उपयोग करता है convolve()
, और संभवतः एक सच्चे पाइथोनिस्टा द्वारा काफी उछला जा सकता है, हालांकि, मेरा मानना है कि विचार खड़ा है।
ऊपर चल रहे गणना की गणना के बारे में कई उत्तर हैं। मेरा जवाब दो अतिरिक्त विशेषताएं जोड़ता है:
यह दूसरी विशेषता विशेष रूप से यह निर्धारित करने के लिए उपयोगी है कि कौन से मूल्य एक निश्चित राशि से सामान्य प्रवृत्ति से भिन्न हैं।
मैं numpy.cumsum का उपयोग करता हूं क्योंकि यह सबसे अधिक समय तक चलने वाला तरीका है ( ऊपर देखें Alleo का उत्तर )।
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)
यह कोड केवल एनएस के लिए भी काम करता है। इसे पैडेड_x और n_nan के np.insert को बदलकर विषम संख्याओं के लिए समायोजित किया जा सकता है।
उदाहरण आउटपुट (कच्चे में कच्चा, नीला में मोलव):
इस कोड को आसानी से कटऑफ = 3 गैर-नैन मूल्यों से कम से गणना की जाने वाली सभी चलती औसत मूल्यों को हटाने के लिए अनुकूलित किया जा सकता है।
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)
केवल पायथन स्टैंडर्ड लाइब्रेरी (मेमोरी कुशल) का उपयोग करें
deque
केवल मानक पुस्तकालय का उपयोग करने का दूसरा संस्करण दें । यह मेरे लिए काफी आश्चर्य की बात है कि अधिकांश उत्तर का उपयोग pandas
कर रहे हैं या 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]
वास्तव में मुझे अजगर डॉक्स में एक और कार्यान्वयन मिला
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
हालाँकि मुझे लगता है कि कार्यान्वयन थोड़ा अधिक जटिल है जितना कि यह होना चाहिए। लेकिन यह एक कारण के लिए मानक अजगर डॉक्स में होना चाहिए, क्या कोई मेरा और मानक डॉक्टर के कार्यान्वयन पर टिप्पणी कर सकता है?
O(n*d)
गणना कर रहे हैं ( d
खिड़की का n
आकार, पुनरावृत्ति का आकार) और वे कर रहे हैंO(n)
हालाँकि यहाँ इस प्रश्न के समाधान हैं, कृपया मेरे समाधान पर एक नज़र डालें। यह बहुत सरल है और अच्छी तरह से काम कर रहा है।
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)
अन्य उत्तरों को पढ़ने से मुझे नहीं लगता कि यह वही प्रश्न है जो पूछा गया था, लेकिन मुझे उन मूल्यों की सूची की एक औसत औसत रखने की आवश्यकता थी जो आकार में बढ़ रहे थे।
इसलिए यदि आप उन मूल्यों की एक सूची रखना चाहते हैं जिन्हें आप कहीं से प्राप्त कर रहे हैं (एक साइट, एक मापने का उपकरण, आदि) और n
अद्यतन किए गए अंतिम मानों के औसत से , आप कोड बेलो का उपयोग कर सकते हैं, जो नए को जोड़ने के प्रयास को कम करता है तत्वों:
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)
और उदाहरण के लिए, आप इसका परीक्षण कर सकते हैं:
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()
जो देता है:
एक मानक पुस्तकालय और छल का उपयोग करके एक और समाधान:
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
शैक्षिक उद्देश्यों के लिए, मैं दो और Numpy समाधान जोड़ता हूं (जो कि कम्सम समाधान की तुलना में धीमे हैं):
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
उपयोग किए गए कार्य: as_strered , add.reduceat
उपरोक्त सभी समाधान खराब हैं क्योंकि उनमें कमी है
numpy.cumsum
या के खराब उपयोग के कारण संख्यात्मक स्थिरता , याO(len(x) * w)
प्रस्तावों के रूप में कार्यान्वयन के कारण गति ।दिया हुआ
import numpy
m = 10000
x = numpy.random.rand(m)
w = 1000
ध्यान दें कि x_[:w].sum()
बराबरी x[:w-1].sum()
। तो पहले औसत के लिए numpy.cumsum(...)
जोड़ता है x[w] / w
(के माध्यम से x_[w+1] / w
), और घटाना 0
(से x_[0] / w
)। इसका परिणाम यह होगाx[0:w].mean()
व्हाट्स कम्सम, आप दूसरे औसत को अतिरिक्त जोड़ x[w+1] / w
और घटाकर अपडेट करेंगे x[0] / w
, जिसके परिणामस्वरूप x[1:w+1].mean()
।
यह तब तक चलता है जब तक x[-w:].mean()
पहुंच नहीं जाता है।
x_ = numpy.insert(x, 0, 0)
sliding_average = x_[:w].sum() / w + numpy.cumsum(x_[w:] - x_[:-w]) / w
यह समाधान वेक्टर O(m)
,, पठनीय और संख्यात्मक रूप से स्थिर है।
एक चलती औसत फिल्टर के बारे में कैसे ? यह वन-लाइनर भी है और इसका फायदा यह है, कि आप आसानी से विंडो टाइप में फेरबदल कर सकते हैं, अगर आपको आयत के अलावा किसी और चीज़ की ज़रूरत हो तो। एक सरणी के एक एन-लंबी सरल चलती औसत:
lfilter(np.ones(N)/N, [1], a)[N:]
और लागू त्रिकोणीय खिड़की के साथ:
lfilter(np.ones(N)*scipy.signal.triang(N)/N, [1], a)[N:]
नोट: मैं आमतौर पर फर्स्ट एन सैंपल्स को फर्जी मानता हूं [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
यदि आपके सभी मूल्य मोटे तौर पर परिमाण के समान क्रम हैं, तो यह हमेशा लगभग समान परिमाण के मूल्यों को जोड़कर परिशुद्धता को बनाए रखने में मदद करेगा।