औसत चलने या चलने का मतलब


192

क्या पायथन के लिए एक SciPy फ़ंक्शन या NumPy फ़ंक्शन या मॉड्यूल है जो किसी विशिष्ट विंडो को दिए गए 1D सरणी के चल रहे माध्य की गणना करता है?

जवाबों:


24

एक छोटे, तेज समाधान के लिए जो पूरी तरह से एक लूप में करता है, निर्भरता के बिना, नीचे दिए गए कोड महान काम करता है।

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)

44
तेज?! यह समाधान Numpy के साथ समाधान की तुलना में परिमाण धीमी के आदेश है।
बार्ट

3
यद्यपि यह मूल समाधान शांत है, ओपी ने एक सुस्वाद / डरावना कार्य के लिए कहा - संभवतः वे काफी तेज होंगे।
डेमिस

254

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

रनिंग मीन कॉन्वोक मोड


4
मुझे यह समाधान पसंद है क्योंकि यह साफ है (एक पंक्ति) और अपेक्षाकृत कुशल (सुन्न के अंदर किया गया कार्य)। लेकिन Alleo के "कुशल समाधान" के उपयोग numpy.cumsumसे बेहतर जटिलता होती है।
उलरिच स्टर्न

2
@denfromufa, मेरा मानना ​​है कि प्रलेखन बहुत अच्छी तरह से कार्यान्वयन को कवर करता है, और यह विकिपीडिया से भी जुड़ता है जो गणित को समझाता है। प्रश्न के फ़ोकस को ध्यान में रखते हुए, क्या आपको लगता है कि इस उत्तर को कॉपी करने की आवश्यकता है?
लैपिस

@ लैपिस मूविंग एवरेज के लिए कॉन्वोल का उपयोग काफी असामान्य और गैर-स्पष्ट है। यहाँ सबसे अच्छी दृश्य व्याख्या मुझे मिली है: matlabtricks.com/post-11/moving-aiture-by-convolution
denfromufa

प्लॉटिंग और संबंधित कार्यों के लिए इसे किसी भी मूल्य के साथ भरने में मददगार होगा। मेरा (इतना सुंदर नहीं, लेकिन छोटा) सुझाव: `` डिफ मूविंग_आवरेज (x, N, fill = True): np.concatenate ([x में x के लिए [कोई नहीं] * (N // 2 + N% 2) * भरें, np.convolve (x, np.ones ((N,)) / N, मोड = 'वैध'), [कोई नहीं] * (N // 2) * भरें,] अगर len (x)] ` `` कोड एसओ टिप्पणियों में इतनी बदसूरत लग रहा है xD मैं एक और जवाब नहीं जोड़ना चाहता था क्योंकि बहुत सारे थे लेकिन आप बस इसे अपने आईडीई में कॉपी और पेस्ट कर सकते हैं।
चॉस्ट

144

कुशल समाधान

सीधा दृष्टिकोण की तुलना में बातचीत बहुत बेहतर है, लेकिन (मुझे लगता है) यह एफएफटी का उपयोग करता है और इस प्रकार काफी धीमा है। हालाँकि, विशेष रूप से चल रहे कंप्यूटिंग के लिए निम्न दृष्टिकोण ठीक काम करता है

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)
  • अधिक अंक आप अधिक से अधिक फ्लोटिंग पॉइंट एरर जमा करते हैं (इसलिए 1e5 पॉइंट्स ध्यान देने योग्य हैं, 1e6 पॉइंट्स अधिक महत्वपूर्ण हैं, 1e6 से अधिक और आप संचायक को रीसेट करना चाह सकते हैं)
  • आप का उपयोग करके धोखा कर सकते हैं np.longdouble लेकिन आपकी फ़्लोटिंग पॉइंट त्रुटि अभी भी अपेक्षाकृत बड़ी संख्या में अंकों के लिए महत्वपूर्ण होगी (लगभग> 1e5 लेकिन आपके डेटा पर निर्भर करती है)
  • आप त्रुटि की साजिश कर सकते हैं और इसे अपेक्षाकृत तेजी से बढ़ाते हुए देख सकते हैं
  • समाधान धीमा है, लेकिन इस अस्थायी बिंदु का सटीक नुकसान नहीं है
  • यूनिफ़ॉर्म_फिल्टर 1 डी समाधान इस कम्सुम सोल्यूशन की तुलना में तेज़ है और इसमें परिशुद्धता का यह फ्लोटिंग पॉइंट लॉस नहीं है

3
अच्छा समाधान! मेरा कूबड़ numpy.convolveO (mn) है; इसके डॉक्स उल्लेख करते हैं कि scipy.signal.fftconvolveएफएफटी का उपयोग करता है।
उलरिच स्टर्न

3
यह विधि सरणी के किनारों से नहीं निपटती है, क्या यह करता है?
JoVe

6
अच्छा समाधान है, लेकिन ध्यान दें कि यह बड़े सरणियों के लिए संख्यात्मक त्रुटियों से ग्रस्त हो सकता है, क्योंकि सरणी के अंत की ओर, आप एक छोटा परिणाम प्राप्त करने के लिए दो बड़ी संख्याओं को घटा सकते हैं।
बास स्विंकल्स

1
यह फ्लोट डिवीजन के बजाय पूर्णांक विभाजन का उपयोग करता है: running_mean([1,2,3], 2)देता है array([1, 2])xद्वारा प्रतिस्थापित करने [float(value) for value in x]की चाल है।
क्रिस डब्ल्यूडब्ल्यू

4
इस समाधान की संख्यात्मक स्थिरता एक समस्या बन सकती है यदि 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
मिलान

79

अद्यतन: नीचे 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

वहाँ भी अच्छे विकल्प हैं कि कैसे बढ़त मूल्यों से निपटने के लिए।


6
पंडों रोलिंग_मैं नौकरी के लिए एक अच्छा साधन है लेकिन ndarrays के लिए पदावनत किया गया है। भविष्य में पंडों द्वारा जारी की गई यह केवल पंडों की श्रृंखला पर काम करेगी। हम गैर-पंडस सरणी डेटा के लिए अब कहां मुड़ेंगे?
माइक

5
@ माइक रोलिंग_मैं () पदावनत है, लेकिन अब आप रोलिंग का उपयोग कर सकते हैं और अलग से मतलब कर सकते हैं: df.rolling(windowsize).mean()अब इसके बजाय काम करता है (बहुत जल्दी मैं जोड़ सकता हूं)। 6,000 पंक्ति के लिए श्रृंखला %timeit test1.rolling(20).mean()लौटे 1000 छोरों, 3 का सबसे अच्छा: पाश प्रति 1.16 एमएस
Vlox

5
@Vlox df.rolling()काफी अच्छी तरह से काम करता है, समस्या यह है कि यह फ़ॉर्म भविष्य में ndarrays का समर्थन नहीं करेगा। इसका उपयोग करने के लिए हमें पहले अपने डेटा को पंडों के डेटाफ्रेम में लोड करना होगा। मैं इस समारोह या तो करने के लिए जोड़ा देखना पसंद करेंगे numpyया scipy.signal
माइक

1
@ मायके पूरी तरह से सहमत हैं। मैं विशेष रूप से पंडों .ewm () से मिलान करने के लिए संघर्ष कर रहा हूं। मेरा मतलब है () मेरी खुद की सरणियों के लिए गति (पहले उन्हें डीएफ में लोड करने के बजाय)। मेरा मतलब है, यह बहुत अच्छा है कि यह तेज़ है, लेकिन अभी भी थोड़ा सा लगता है कि डेटाफ़्रेम में बहुत बार और बाहर बढ़ रहा है।
Vlox

6
%timeit bottleneck.move_mean(x, N)मेरे पीसी पर कमसुम और पांडा के तरीकों की तुलना में 3 से 15 गुना तेज है। रेपो की README में उनके बेंचमार्क को देखें ।
माब

50

आप एक रनिंग माध्य की गणना कर सकते हैं:

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'हल करने वाले में एक मोड शामिल है जो ऐसा लगता है जैसे इसे शुरुआती क्षणिक मुद्दे को संबोधित करना चाहिए, लेकिन यह इसे शुरुआत और अंत के बीच विभाजित करता है।


ध्यान दें कि पहले N-1 बिंदुओं को हटाने से अभी भी अंतिम बिंदुओं में एक सीमा प्रभाव पड़ता है। इस मुद्दे को हल करने के लिए एक आसान तरीका उपयोग करने के लिए है mode='valid'में convolveहै जो किसी भी पोस्ट-प्रोसेसिंग की आवश्यकता नहीं है।
लैपिस

1
@ साइको - mode='valid'दोनों छोर से क्षणिक को हटाता है, है ना? तो len(x)=10और N=4, एक चलाने के लिए मतलब मैं चाहेगा 10 परिणाम लेकिन valid7. रिटर्न
mtrw

1
यह अंत से क्षणिक को हटाता है, और शुरुआत में एक नहीं है। ठीक है, मुझे लगता है कि यह प्राथमिकताओं की बात है, मुझे शून्य की ओर ढलान की कीमत पर समान परिणाम की आवश्यकता नहीं है जो डेटा में नहीं है। BTW, यहाँ मोड के बीच अंतर दिखाने के लिए एक कमांड है: 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यह भी सीमा प्रभाव मुद्दा है।
मर्गलूम

21

या अजगर के लिए मॉड्यूल जो गणना करता है

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। मुझे यह त्रुटि मिल रही है, सर।
एमडी। रेजवानुल हक

1
ऐसा लगता है कि स्मूथिंग के बाद आपको टाइम सीरीज़ शिफ्ट कर दी जाती है, क्या यह वांछित प्रभाव है?
मर्ग्लूम

विज़ुअलाइज़ेशन प्रयोजनों के लिए @mrloloom हाँ; और वे चार्ट पर एक पंक्ति के रूप में दिखाई देंगे; एमडी। रेजवानुल हक आप पीएआईआर और जानकारी के सभी संदर्भों को हटा सकते हैं; अब आंतरिक परंपरा के लिए उन सैंडबॉक्स वाले तरीके थे
litepresence

21

रेडी-टू-यूज़ समाधान के लिए, 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)

1
यह विधि numpy.convolveकेवल अनुक्रम को बदलने में अंतर पर निर्भर करती है ।
अलेओ

10
जब मैं इनपुट और आउटपुट दोनों इनपुट और आउटपुट दोनों एक ही प्रकृति के होते हैं (उदाहरण के लिए, दोनों लौकिक सिग्नल) से अलग सिग्नल के रिटर्न को सिग्नल प्रोसेसिंग फ़ंक्शन द्वारा हमेशा परेशान किया जाता है। यह संबंधित स्वतंत्र चर (जैसे, समय, आवृत्ति) के साथ पत्राचार को तोड़ता है, जिससे साजिश रचने या तुलना करने में कोई सीधी बात नहीं है ... वैसे भी, यदि आप भावना साझा करते हैं, तो आप प्रस्तावित फ़ंक्शन की अंतिम पंक्तियों को y = np के रूप में बदलना चाह सकते हैं। .convolve (w / w.sum (), एस, मोड = 'एक ही'); वापसी y [window_len-1 :-( window_len-1)]
ईसाई O'Reilly

@ ChristianO'Reilly, आपको एक अलग उत्तर के रूप में पोस्ट करना चाहिए- ठीक यही मैं देख रहा था, जैसा कि मेरे पास वास्तव में दो अन्य सरणियाँ हैं जिन्हें स्मूद डेटा की लंबाई, प्लॉटिंग आदि के लिए मिलान करना है। वास्तव में आपने यह कैसे किया - wखिड़की का आकार, और sडेटा है?
डेमिस

@ डेमिस ग्लैड कमेंट ने मदद की। यहाँ पर numpy convolve फंक्शन की अधिक जानकारी docs.scipy.org/doc/numpy-1.15.0/reference/generated/… एक कनवल्शन फंक्शन ( en.wikipedia.org/wiki/Convolution ) एक दूसरे के साथ दो सिग्नलों को सजाता है । इस स्थिति में, यह एक सामान्यीकृत (यानी एकात्मक क्षेत्र) विंडो (w / w.sum ()) के साथ आपके सिग्नल (ओं) को दर्शाता है।
क्रिश्चियन ओ'रिली

20

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

1
यह एकमात्र उत्तर है जो सीमा के मुद्दों को ध्यान में रखता है (बल्कि महत्वपूर्ण है, खासकर जब प्लॉटिंग)। धन्यवाद!
गेब्रियल

1
मैं प्रोफाइल uniform_filter1d, np.convolve, एक आयत के साथ और np.cumsumके द्वारा पीछा किया np.subtract। मेरे परिणाम: (1.) सबसे धीमा है। (२.) कमसुम / घटाना लगभग २०-३०x तेज है। (३.) वर्दी_फिल्टर १ डी लगभग २-३ एक्स की तुलना में कमसिन / घटाव से अधिक तेज है। विजेता निश्चित रूप से वर्दी_फिल्टर 1 डी है।
ट्रेवर बॉयड स्मिथ

का उपयोग कर uniform_filter1dरहा है तेजी से cumsumसमाधान (2-5x के बारे में द्वारा)। और uniform_filter1d बड़े पैमाने पर फ्लोटिंग पॉइंट एरर नहीं मिलता है जैसेcumsum समाधान करता है।
ट्रेवर बॉयड स्मिथ

15

मुझे पता है कि यह एक पुराना सवाल है, लेकिन यहां एक समाधान है जो किसी भी अतिरिक्त डेटा संरचनाओं या पुस्तकालयों का उपयोग नहीं करता है। यह इनपुट सूची के तत्वों की संख्या में रैखिक है और मैं इसे और अधिक कुशल बनाने के लिए किसी अन्य तरीके के बारे में नहीं सोच सकता (वास्तव में अगर किसी को परिणाम को आवंटित करने का एक बेहतर तरीका पता है, तो कृपया मुझे बताएं)।

ध्यान दें: यह एक सूची के बजाय एक संख्यात्मक सरणी का उपयोग करके बहुत तेज़ी से होगा, लेकिन मैं सभी निर्भरता को समाप्त करना चाहता था। बहु-थ्रेडेड निष्पादन द्वारा प्रदर्शन में सुधार करना भी संभव होगा

फ़ंक्शन मानता है कि इनपुट सूची एक आयामी है, इसलिए सावधान रहें।

### 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+4sum + 4 - 1 = 9

वर्तमान में ऐसा इसलिए होता है sum = data[0] + data[1] + data[2], लेकिन i >= Nघटाव से पहले, यह प्रत्येक के लिए सही sumहै data[i-N] + ... + data[i-2] + data[i-1]


12

मुझे लगता है कि यह अड़चन का उपयोग करके हल किया जा सकता है

नीचे मूल नमूना देखें:

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" चलती मीनिंग के लिए विचार करने के लिए प्रविष्टियों की न्यूनतम संख्या है (जैसे पहले कुछ तत्वों के लिए या यदि सरणी में नैनो मान हैं)।

अच्छा हिस्सा टोंटी नेक नेन मूल्यों से निपटने में मदद करता है और यह बहुत कुशल भी है।


यह परिवाद वास्तव में तेज है। शुद्ध पायथन चलती औसत क्रिया धीमी है। Bootleneck एक PyData पुस्तकालय है, जो मुझे लगता है कि स्थिर है और पायथन समुदाय से निरंतर समर्थन प्राप्त कर सकता है, इसलिए इसका उपयोग क्यों करें?
गोइंग मायवे

6

मैंने अभी तक जाँच नहीं की है कि यह कितना तेज़ है, लेकिन आप कोशिश कर सकते हैं:

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
यही मैं करने जा रहा था। किसी को भी आलोचना कर सकते हैं क्यों यह जाने के लिए एक बुरा तरीका है?
डगमगाते हुए

1
इस सरल अजगर समाधान ने मेरे लिए सुन्न की आवश्यकता के बिना अच्छी तरह से काम किया। मैंने इसे फिर से उपयोग के लिए एक वर्ग में रोल करना समाप्त कर दिया।
मैथ्यू Tsiegg

6

इस उत्तर में तीन अलग-अलग परिदृश्यों के लिए पायथन मानक पुस्तकालय का उपयोग करने वाले समाधान हैं।


के साथ औसत चल रहा है 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 जिसे आप किसी भी समय मान भेज सकते हैं

यह 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, जो कि एक सूची के समान डेटास्ट्रक्चर है, लेकिन दोनों समापन बिंदुओं पर तेज संशोधनों ( popappend) के लिए अनुकूलित है ।

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

5

पार्टी में थोड़ी देर हो गई है, लेकिन मैंने अपना एक छोटा सा फंक्शन बनाया है, जो ज़ीरो के साथ सिरों या पैड्स के चारों ओर लपेटता नहीं है जो तब औसत खोजने के लिए उपयोग किए जाते हैं। एक और उपचार के रूप में, यह है कि यह रैखिक रूप से स्थान बिंदुओं पर संकेत को भी पुन: नमूना करता है। अन्य सुविधाओं को प्राप्त करने के लिए कोड को अनुकूलित करें।

विधि एक सामान्य मैट्रिक्स गुणन है जो सामान्यीकृत गाऊसी कर्नेल के साथ है।

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 संकेत पर एक सरल उपयोग: यहां छवि विवरण दर्ज करें


यह मेरे लिए काम नहीं करता है (अजगर 3.6)। 1 कोई फ़ंक्शन नाम नहीं है sum, np.sumइसके बजाय 2 का उपयोग करके @ऑपरेटर (कोई विचार नहीं है कि क्या है) एक त्रुटि फेंकता है। मैं इसे बाद में देख सकता हूं, लेकिन अभी मेरे पास समय की कमी है
बस्तियन

@आव्यूह गुणन ऑपरेटर जो लागू करता है np.matmul । जाँच करें कि क्या आपका y_inसरणी एक संख्यात्मक सरणी है, जो समस्या हो सकती है।
xyzzyqed

5

इसके बजाय मैं खस्ता या डरपोक हूं, मैं पंडों को यह और अधिक तेजी से करने की सलाह दूंगा:

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

यह कॉलम "डेटा" के 3 अवधियों की चलती औसत (एमए) लेता है। आप शिफ्ट किए गए संस्करणों की गणना भी कर सकते हैं, उदाहरण के लिए, जो वर्तमान सेल को छोड़ देता है (एक वापस स्थानांतरित) को आसानी से गणना की जा सकती है:

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


2
2016 में प्रस्तावित समाधान pandas.rolling_meanमेरा उपयोग करते समय उपयोग करता है pandas.DataFrame.rolling। आप इस पद्धति के साथ-साथ चलती min(), max(), sum()आदि की गणना भी mean()आसानी से कर सकते हैं।
गुरसेल करकोर

पूर्व में आपको एक अलग विधि का उपयोग करने की आवश्यकता होती है जैसे pandas.rolling_min, pandas.rolling_maxआदि। वे समान हैं फिर भी अलग हैं।
गुरसेल करकोर

4

वहाँ से एक टिप्पणी है 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


3

सुन्न, पांडा का उपयोग किए बिना चलती औसत को खोजने के लिए एक और दृष्टिकोण

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]


अजवायन की पत्ती 2.7 में मौजूद नहीं है, लेकिन अजगर 3.4 में करता है
ग्रे

3

यह सवाल अब उससे भी पुराना है जब 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(), और संभवतः एक सच्चे पाइथोनिस्टा द्वारा काफी उछला जा सकता है, हालांकि, मेरा मानना ​​है कि विचार खड़ा है।


3

ऊपर चल रहे गणना की गणना के बारे में कई उत्तर हैं। मेरा जवाब दो अतिरिक्त विशेषताएं जोड़ता है:

  1. नैन मूल्यों की अनदेखी करता है
  2. एन पड़ोसी मूल्यों के लिए माध्य की गणना करता है जिसमें स्वयं ब्याज का मूल्य शामिल नहीं है

यह दूसरी विशेषता विशेष रूप से यह निर्धारित करने के लिए उपयोगी है कि कौन से मूल्य एक निश्चित राशि से सामान्य प्रवृत्ति से भिन्न हैं।

मैं 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 को बदलकर विषम संख्याओं के लिए समायोजित किया जा सकता है।

उदाहरण आउटपुट (कच्चे में कच्चा, नीला में मोलव): प्रत्येक मान के आसपास 10 अंकों का कच्चा डेटा (काला) और मूविंग एवरेज (नीला), उस मूल्य को शामिल नहीं करता है।  नैन मूल्यों की अनदेखी की जाती है।

इस कोड को आसानी से कटऑफ = 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)

कच्चे डेटा (काला) और मूविंग एवरेज (नीला) जबकि किसी भी विंडो को 3 से कम गैर-नैनो मानों की अनदेखी करते हुए


2

केवल पायथन स्टैंडर्ड लाइब्रेरी (मेमोरी कुशल) का उपयोग करें

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

हालाँकि मुझे लगता है कि कार्यान्वयन थोड़ा अधिक जटिल है जितना कि यह होना चाहिए। लेकिन यह एक कारण के लिए मानक अजगर डॉक्स में होना चाहिए, क्या कोई मेरा और मानक डॉक्टर के कार्यान्वयन पर टिप्पणी कर सकता है?


2
एक बड़ा अंतर जो आप खिड़की के सदस्यों को प्रत्येक पुनरावृत्ति को जोड़ते हैं, और वे कुशलता से राशि को अपडेट करते हैं (एक सदस्य को हटा दें और दूसरे को जोड़ें)। जटिलता के संदर्भ में आप O(n*d) गणना कर रहे हैं ( dखिड़की का nआकार, पुनरावृत्ति का आकार) और वे कर रहे हैंO(n)
इफ्ता

@ इफ्ता, अच्छा, स्पष्टीकरण के लिए धन्यवाद, आप सही हैं।
MaThMaX

2

@ Aikude के वेरिएबल्स के साथ, मैंने वन-लाइनर लिखा।

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

हालाँकि यहाँ इस प्रश्न के समाधान हैं, कृपया मेरे समाधान पर एक नज़र डालें। यह बहुत सरल है और अच्छी तरह से काम कर रहा है।

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

अन्य उत्तरों को पढ़ने से मुझे नहीं लगता कि यह वही प्रश्न है जो पूछा गया था, लेकिन मुझे उन मूल्यों की सूची की एक औसत औसत रखने की आवश्यकता थी जो आकार में बढ़ रहे थे।

इसलिए यदि आप उन मूल्यों की एक सूची रखना चाहते हैं जिन्हें आप कहीं से प्राप्त कर रहे हैं (एक साइट, एक मापने का उपकरण, आदि) और 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()

जो देता है:

मानों के एक समारोह के रूप में मान और उनका औसत


1

एक मानक पुस्तकालय और छल का उपयोग करके एक और समाधान:

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

शैक्षिक उद्देश्यों के लिए, मैं दो और 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


1

उपरोक्त सभी समाधान खराब हैं क्योंकि उनमें कमी है

  • एक सुपीरियर वेक्टर के बजाय एक देशी अजगर के कारण गति,
  • 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),, पठनीय और संख्यात्मक रूप से स्थिर है।


1

एक चलती औसत फिल्टर के बारे में कैसे ? यह वन-लाइनर भी है और इसका फायदा यह है, कि आप आसानी से विंडो टाइप में फेरबदल कर सकते हैं, अगर आपको आयत के अलावा किसी और चीज़ की ज़रूरत हो तो। एक सरणी के एक एन-लंबी सरल चलती औसत:

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

और लागू त्रिकोणीय खिड़की के साथ:

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

नोट: मैं आमतौर पर फर्स्ट एन सैंपल्स को फर्जी मानता हूं [N:], इसलिए अंत में यह जरूरी नहीं है और केवल व्यक्तिगत पसंद की बात है।


-7

यदि आप किसी मौजूदा लाइब्रेरी का उपयोग करने के बजाय अपना स्वयं का रोल करना चुनते हैं, तो कृपया फ़्लोटिंग पॉइंट त्रुटि के प्रति सचेत रहें और इसके प्रभावों को कम करने का प्रयास करें:

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

यदि आपके सभी मूल्य मोटे तौर पर परिमाण के समान क्रम हैं, तो यह हमेशा लगभग समान परिमाण के मूल्यों को जोड़कर परिशुद्धता को बनाए रखने में मदद करेगा।


15
यह एक बहुत ही अस्पष्ट उत्तर है, कम से कम कुछ कोड या स्पष्टीकरण में टिप्पणी करता है कि इससे फ्लोटिंग पॉइंट एरर अच्छा होगा।
गाबे

अपने अंतिम वाक्य में मैं यह संकेत करने की कोशिश कर रहा था कि यह फ्लोटिंग पॉइंट एरर में मदद क्यों करता है। यदि दो मान लगभग परिमाण के समान क्रम हैं, तो उन्हें जोड़ने से कम सटीकता आती है यदि आपने बहुत बड़ी संख्या को बहुत छोटे से जोड़ा। कोड "आसन्न" मूल्यों को इस तरह से जोड़ता है कि फ्लोटिंग पॉइंट त्रुटि को कम करने के लिए मध्यवर्ती समता को हमेशा यथोचित रूप से करीब होना चाहिए। कुछ भी मूर्खतापूर्ण सबूत नहीं है, लेकिन इस पद्धति ने उत्पादन में बहुत खराब कार्यान्वित परियोजनाओं को बचाया है।
मयूर पटेल

1. मूल समस्या के लिए लागू किया जा रहा है, यह बहुत धीमी गति से (कंप्यूटिंग औसत) होगा, इसलिए यह सिर्फ अप्रासंगिक है 2. 64-बिट संख्या की सटीकता की समस्या से पीड़ित होने के लिए, किसी को लगभग >> 2 ^ 30 का योग करना होगा समान संख्या।
अलेलो

@Alleo: प्रति मूल्य एक जोड़ करने के बजाय, आप दो कर रहे होंगे। प्रमाण बिट-फ़्लिपिंग समस्या के समान है। हालांकि, इस जवाब का बिंदु जरूरी नहीं कि प्रदर्शन है, लेकिन सटीक है। 64-बिट मानों के औसत मेमोरी का उपयोग कैश में 64 तत्वों से अधिक नहीं होगा, इसलिए यह मेमोरी उपयोग में भी अनुकूल है।
मयूर पटेल

हां, आप सही कह रहे हैं कि साधारण योग की तुलना में यह 2x अधिक संचालन करता है, लेकिन मूल समस्या गणना योग है , केवल योग नहीं। जिसे O (n) में किया जा सकता है, लेकिन आपके उत्तर के लिए O (mn) की आवश्यकता होती है, जहाँ m विंडो का आकार है।
एलेलो
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.