Як обчислити ковзну середню за допомогою NumPy?


109

Здається, немає функції, яка просто обчислює ковзну середню на numpy / scipy, що веде до згортання рішень .

Моє запитання двозначне:

  • Який найпростіший спосіб (правильно) реалізувати ковзну середню з numpy?
  • Оскільки це здається нетривіальним та схильним до помилок, чи є вагомі причини не включити батареї до цього випадку?

19
Рішення згортки не здається мені таким заплутаним!
— Вім

4
Чи не є ковзною середньою лише фільтр низьких частот (тобто «розмиття»)? Досить впевнений, що саме такий вид речі призначений для ...
— user541686

@mmgp Я гадаю, що я сподівався помилитися, або що була хороша, очевидна причина.
— goncalopp

3
@wim Це було наполовину як каламбур. Але сам факт існування питання означає, що створити рухоме середнє значення з numpy.convolute не просто.
— goncalopp

3
Можливий дублікат
— ковзного

Відповіді:


165

Якщо ви просто хочете простий невиважені ковзної середньої, ви можете легко реалізувати його np.cumsum, що може бути це методи швидше , ніж БПФ на основі:

EDIT Виправлено неправильне індексування, помічене Біном у коді. EDIT

def moving_average(a, n=3) :
    ret = np.cumsum(a, dtype=float)
    ret[n:] = ret[n:] - ret[:-n]
    return ret[n - 1:] / n

>>> a = np.arange(20)
>>> moving_average(a)
array([  1.,   2.,   3.,   4.,   5.,   6.,   7.,   8.,   9.,  10.,  11.,
        12.,  13.,  14.,  15.,  16.,  17.,  18.])
>>> moving_average(a, n=4)
array([  1.5,   2.5,   3.5,   4.5,   5.5,   6.5,   7.5,   8.5,   9.5,
        10.5,  11.5,  12.5,  13.5,  14.5,  15.5,  16.5,  17.5])

Тому я здогадуюсь, що відповідь така: це реально легко здійснити, а може бути, нуме вже трохи роздутий спеціалізованою функціональністю.


10
Цей код неправильний. наприклад, переміщення_середнє значення ([1,2,5,10], n = 2) дає [1., 3.5, 8.5]. Навіть тест відповіді для ковзної середньої величини від 0 до 19 є неправильним, стверджуючи, що середнє значення 0, 1 і 2 становить 0,5. Як було отримано 6 оновлень?
— JeremyKun

2
Дякуємо за перевірку помилок, зараз, здається, це працює нормально. Щодо оновлених результатів, я здогадуюсь, що загальна ідея відповіді зважувалася набагато сильніше, ніж помилка в процесі реалізації, але хто знає.
— Хайме

2
Я знайшов проблему. ret[n:] -= ret[:-n]НЕ ТЕБЕ, як ret[n:] = ret[n:] - ret[:-n]. Я зафіксував код у цій відповіді. Редагувати: Ніхто інший просто не перебив мене.
— Timmmm

7
@Timmmm я це зробив, це справді було проблемою. Загальний принцип, що стоїть за цією відповіддю, широко використовується в обробці зображень (підсумовані таблиці таблиць, які вони називають), тому питання повинно бути в реалізації. Хороший приклад того, що вас кусає передчасна оптимізація, оскільки я нагадую, що робив операцію на місці, "оскільки вона буде більш ефективною". З іншого боку, це, ймовірно, швидше дало неправильну відповідь ...
— Хайме

43
Гммм, здається, що ця функція "легко здійснити" насправді досить легко помилитися і сприяла хорошій дискусії щодо ефективності пам'яті. Я щасливий, що зацвіла, якщо це означає, що щось зроблено правильно.
— Річард

81

Відсутність NumPy певної доменної функції, можливо, пов'язана з дисципліною Core Team та вірністю основній директиві NumPy: забезпечити тип N-розмірного масиву , а також функції для створення та індексації цих масивів. Як і багато основоположних цілей, і ця не є маленькою, і NumPy робить це блискуче.

(Набагато) більший SciPy містить набагато більшу колекцію доменних специфічних бібліотек (званих субпакетами від SciPy devs) - наприклад, чисельну оптимізацію ( оптимізацію ), обробку сигналів ( сигнал ) та інтегральне обчислення ( інтегрувати ).

Я здогадуюсь, що функція, яку ви виконуєте, знаходиться принаймні в одному з підпакетів SciPy ( можливо, scipy.signal ); однак я спершу зазирнув би у колекцію SciPy scikits , визначив відповідні науковці та шукав би там цікаву функцію.

Scikits - це незалежно розроблені пакети, засновані на NumPy / SciPy і спрямовані на певну технічну дисципліну (наприклад, scikits-образ , scikits-learn тощо). Деякі з них (зокрема, дивовижний OpenOpt для чисельної оптимізації) були високо оцінені, зрілі проекти задовго до того, як вибрати місце проживання за новою рубрикою наукових наук . У Scikits не любив домашню сторінку вище списків близько 30 таких scikits , хоча по крайней мере , деякі з них вже не в стадії активного розвитку.

Дотримуючись цієї поради, ви привели б вас до науковців-таймерів ; однак цей пакет вже не знаходиться в активному розвитку; Фактично, Pandas став, AFAIK, бібліотекою часових рядів, заснованою на фактичному NumPy .

Панда має кілька функцій, які можна використовувати для обчислення ковзної середньої величини ; найпростіший з них - це, мабуть, rolling_mean , який ви використовуєте так:

>>> # the recommended syntax to import pandas
>>> import pandas as PD
>>> import numpy as NP

>>> # prepare some fake data:
>>> # the date-time indices:
>>> t = PD.date_range('1/1/2010', '12/31/2012', freq='D')

>>> # the data:
>>> x = NP.arange(0, t.shape[0])

>>> # combine the data & index into a Pandas 'Series' object
>>> D = PD.Series(x, t)

Тепер просто зателефонуйте функції rolling_mean, що передає об'єкт Series, і розмір вікна , який у моєму прикладі нижче становить 10 днів .

>>> d_mva = PD.rolling_mean(D, 10)

>>> # d_mva is the same size as the original Series
>>> d_mva.shape
    (1096,)

>>> # though obviously the first w values are NaN where w is the window size
>>> d_mva[:3]
    2010-01-01         NaN
    2010-01-02         NaN
    2010-01-03         NaN

переконайтеся, що воно спрацювало - наприклад, порівняно значення 10 - 15 у вихідній серії проти нової серії, згладженої ковзаючим середнім

>>> D[10:15]
     2010-01-11    2.041076
     2010-01-12    2.041076
     2010-01-13    2.720585
     2010-01-14    2.720585
     2010-01-15    3.656987
     Freq: D

>>> d_mva[10:20]
      2010-01-11    3.131125
      2010-01-12    3.035232
      2010-01-13    2.923144
      2010-01-14    2.811055
      2010-01-15    2.785824
      Freq: D

Функція rolling_mean, поряд з десятком або близько іншої функції, неофіційно згруповані в документації Pandas під рубрикою вікна, що рухається ; друга, споріднена група функцій в Пандах називається експоненціально зваженими функціями (наприклад, ewma , яка обчислює експоненціально рухоме середньозважене середнє значення). Те, що ця друга група не входить до першої ( функції вікон, що рухаються ), можливо, тому, що перетворення в експоненціально зваженому стані не покладаються на вікно фіксованої довжини


6
У Pandas є сильна лінія рухомих віконних функцій. Але мені здається, що трохи занадто великі накладні витрати для простого ковзного середнього.
— Хайме

6
добре, я сумніваюся, що розрахунок ковзної середньої величини є окремою вимогою до ОП або майже для когось іншого. Якщо вам потрібно обчислити ковзну середню величину, то ви майже напевно маєте часовий ряд, а це означає, що вам потрібна структура даних, яка дозволяє вам відповідати індексу дати та часу вашим даним, і це "накладні витрати", на які ви посилаєтесь.
— дог

2
По-перше, дякую, що знайшли час, щоб написати цю надзвичайно інформативну відповідь. Дійсно, я не можу побачити використання ковзної середньої величини, яка не включає часовий ряд. Але це не означає, що потрібно узгоджувати його з датою або навіть з певним інтервалом вибірки (це може бути невідомо)
— goncalopp

3
Просто хотілося додати, що ковзаюча середня функція була вилучена в бібліотеку Bottleneck, якщо панди здаються занадто важкими як залежність.
— robochat

4
'rolling_mean' вже не є частиною панд pf, будь ласка, дивіться відповідь, використовуючи замість 'rolling'
— Vladtn

61

Простий спосіб досягти цього - за допомогою використання np.convolve. Ідея цього полягає в тому, щоб використовувати спосіб обчислення дискретної згортки і використовувати її для повернення колінного середнього . Це можна зробити, склавши послідовність np.onesдовжини, що дорівнює потрібній нам довжині розсувного вікна.

Для цього ми могли б визначити наступну функцію:

def moving_average(x, w):
    return np.convolve(x, np.ones(w), 'valid') / w

Ця функція буде приймати згортку послідовності xта послідовності довжини w. Зверніть увагу , що обраний modeє validтаким , що згортка твір задається тільки для точок , де послідовності перекриваються повністю.


Деякі приклади:

x = np.array([5,3,8,10,2,1,5,1,0,2])

Для ковзної середньої з вікном довжини 2ми мали б:

moving_average(x, 2)
# array([4. , 5.5, 9. , 6. , 1.5, 3. , 3. , 0.5, 1. ])

А для вікна довжини 4:

moving_average(x, 4)
# array([6.5 , 5.75, 5.25, 4.5 , 2.25, 1.75, 2.  ])

Як convolveпрацює?

Давайте детальніше розглянемо спосіб обчислення дискретної згортки. Наступна функція має на меті повторити спосіб np.convolveобчислення вихідних значень:

def mov_avg(x, w):
    for m in range(len(x)-(w-1)):
        yield sum(np.ones(w) * x[m:m+w]) / w 

Що, у тому ж прикладі вище, також дасть:

list(mov_avg(x, 2))
# [4.0, 5.5, 9.0, 6.0, 1.5, 3.0, 3.0, 0.5, 1.0]

Тож, що робиться на кожному кроці, - це взяти внутрішній продукт між масивом з них та поточним вікном . У цьому випадку множення на np.ones(w)відмінне, враховуючи, що ми безпосередньо беремо sumпослідовність.

Нижче наведено приклад того, як обчислюються перші виходи, щоб було трохи зрозуміліше. Припустимо, ми хочемо вікно w=4:

[1,1,1,1]
[5,3,8,10,2,1,5,1,0,2]
= (1*5 + 1*3 + 1*8 + 1*10) / w = 6.5

Такий вихід буде обчислюватися як:

  [1,1,1,1]
[5,3,8,10,2,1,5,1,0,2]
= (1*3 + 1*8 + 1*10 + 1*2) / w = 5.75

І так далі, повертаючи ковзну середню послідовність, коли всі перекриття виконані.


Це приємна ідея! Це швидше, ніж відповідь @ Хайме для малого n, але стає більш повільним для більшого n.
— Феліпе Джерард

Дякую @FelipeGerard! Так, як зазначалося в коментарях, хоча цей підхід може бути не таким ефективним, як деякі інші нудні рішення, тому що приємно мати альтернативу для майбутніх відвідувачів, враховуючи його простоту та стислість
— yatu

Іноді корисно мати вихідний масив такого ж розміру, що і вхідний. Для цього mode='valid'баночку можна замінити 'same'. Просто в цьому випадку крайові точки будуть тяжіти до нуля.
— Ілля Бараховський

15

Ось найрізноманітніші способи зробити це разом із деякими орієнтирами. Найкращі методи - це версії з використанням оптимізованого коду з інших бібліотек. bottleneck.move_meanМетод, ймовірно , краще за все навколо. scipy.convolveПідхід також дуже швидко, розширюваний і синтаксичний і концептуально простий, але не дуже добре масштабується для дуже великих значень вікна. numpy.cumsumМетод хороший , якщо вам потрібен чистий numpyпідхід.

Примітка. Деякі з них (наприклад bottleneck.move_mean) не в центрі, і перенесуть ваші дані.

import numpy as np
import scipy as sci
import scipy.signal as sig
import pandas as pd
import bottleneck as bn
import time as time

def rollavg_direct(a,n): 
    'Direct "for" loop'
    assert n%2==1
    b = a*0.0
    for i in range(len(a)) :
        b[i]=a[max(i-n//2,0):min(i+n//2+1,len(a))].mean()
    return b

def rollavg_comprehension(a,n):
    'List comprehension'
    assert n%2==1
    r,N = int(n/2),len(a)
    return np.array([a[max(i-r,0):min(i+r+1,N)].mean() for i in range(N)]) 

def rollavg_convolve(a,n):
    'scipy.convolve'
    assert n%2==1
    return sci.convolve(a,np.ones(n,dtype='float')/n, 'same')[n//2:-n//2+1]  

def rollavg_convolve_edges(a,n):
    'scipy.convolve, edge handling'
    assert n%2==1
    return sci.convolve(a,np.ones(n,dtype='float'), 'same')/sci.convolve(np.ones(len(a)),np.ones(n), 'same')  

def rollavg_cumsum(a,n):
    'numpy.cumsum'
    assert n%2==1
    cumsum_vec = np.cumsum(np.insert(a, 0, 0)) 
    return (cumsum_vec[n:] - cumsum_vec[:-n]) / n

def rollavg_cumsum_edges(a,n):
    'numpy.cumsum, edge handling'
    assert n%2==1
    N = len(a)
    cumsum_vec = np.cumsum(np.insert(np.pad(a,(n-1,n-1),'constant'), 0, 0)) 
    d = np.hstack((np.arange(n//2+1,n),np.ones(N-n)*n,np.arange(n,n//2,-1)))  
    return (cumsum_vec[n+n//2:-n//2+1] - cumsum_vec[n//2:-n-n//2]) / d

def rollavg_roll(a,n):
    'Numpy array rolling'
    assert n%2==1
    N = len(a)
    rolling_idx = np.mod((N-1)*np.arange(n)[:,None] + np.arange(N), N)
    return a[rolling_idx].mean(axis=0)[n-1:] 

def rollavg_roll_edges(a,n):
    # see /programming/42101082/fast-numpy-roll
    'Numpy array rolling, edge handling'
    assert n%2==1
    a = np.pad(a,(0,n-1-n//2), 'constant')*np.ones(n)[:,None]
    m = a.shape[1]
    idx = np.mod((m-1)*np.arange(n)[:,None] + np.arange(m), m) # Rolling index
    out = a[np.arange(-n//2,n//2)[:,None], idx]
    d = np.hstack((np.arange(1,n),np.ones(m-2*n+1+n//2)*n,np.arange(n,n//2,-1)))
    return (out.sum(axis=0)/d)[n//2:]

def rollavg_pandas(a,n):
    'Pandas rolling average'
    return pd.DataFrame(a).rolling(n, center=True, min_periods=1).mean().to_numpy()

def rollavg_bottlneck(a,n):
    'bottleneck.move_mean'
    return bn.move_mean(a, window=n, min_count=1)

N = 10**6
a = np.random.rand(N)
functions = [rollavg_direct, rollavg_comprehension, rollavg_convolve, 
        rollavg_convolve_edges, rollavg_cumsum, rollavg_cumsum_edges, 
        rollavg_pandas, rollavg_bottlneck, rollavg_roll, rollavg_roll_edges]

print('Small window (n=3)')
%load_ext memory_profiler
for f in functions : 
    print('\n'+f.__doc__+ ' : ')
    %timeit b=f(a,3)

print('\nLarge window (n=1001)')
for f in functions[0:-2] : 
    print('\n'+f.__doc__+ ' : ')
    %timeit b=f(a,1001)

print('\nMemory\n')
print('Small window (n=3)')
N = 10**7
a = np.random.rand(N)
%load_ext memory_profiler
for f in functions[2:] : 
    print('\n'+f.__doc__+ ' : ')
    %memit b=f(a,3)

print('\nLarge window (n=1001)')
for f in functions[2:-2] : 
    print('\n'+f.__doc__+ ' : ')
    %memit b=f(a,1001)

Час, невелике вікно (n = 3)

Direct "for" loop : 

4.14 s ± 23.7 ms per loop (mean ± std. dev. of 7 runs, 1 loop each)

List comprehension : 
3.96 s ± 27.9 ms per loop (mean ± std. dev. of 7 runs, 1 loop each)

scipy.convolve : 
1.07 ms ± 26.7 µs per loop (mean ± std. dev. of 7 runs, 1000 loops each)

scipy.convolve, edge handling : 
4.68 ms ± 9.69 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)

numpy.cumsum : 
5.31 ms ± 5.11 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)

numpy.cumsum, edge handling : 
8.52 ms ± 11.1 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)

Pandas rolling average : 
9.85 ms ± 9.63 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)

bottleneck.move_mean : 
1.3 ms ± 12.2 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)

Numpy array rolling : 
31.3 ms ± 91.9 µs per loop (mean ± std. dev. of 7 runs, 10 loops each)

Numpy array rolling, edge handling : 
61.1 ms ± 55.9 µs per loop (mean ± std. dev. of 7 runs, 10 loops each)

Час, велике вікно (n = 1001)

Direct "for" loop : 
4.67 s ± 34 ms per loop (mean ± std. dev. of 7 runs, 1 loop each)

List comprehension : 
4.46 s ± 14.6 ms per loop (mean ± std. dev. of 7 runs, 1 loop each)

scipy.convolve : 
103 ms ± 165 µs per loop (mean ± std. dev. of 7 runs, 10 loops each)

scipy.convolve, edge handling : 
272 ms ± 1.23 ms per loop (mean ± std. dev. of 7 runs, 1 loop each)

numpy.cumsum : 
5.19 ms ± 12.4 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)

numpy.cumsum, edge handling : 
8.7 ms ± 11.5 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)

Pandas rolling average : 
9.67 ms ± 199 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)

bottleneck.move_mean : 
1.31 ms ± 15.7 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)

Пам'ять, невелике вікно (n = 3)

The memory_profiler extension is already loaded. To reload it, use:
  %reload_ext memory_profiler

scipy.convolve : 
peak memory: 362.66 MiB, increment: 73.61 MiB

scipy.convolve, edge handling : 
peak memory: 510.24 MiB, increment: 221.19 MiB

numpy.cumsum : 
peak memory: 441.81 MiB, increment: 152.76 MiB

numpy.cumsum, edge handling : 
peak memory: 518.14 MiB, increment: 228.84 MiB

Pandas rolling average : 
peak memory: 449.34 MiB, increment: 160.02 MiB

bottleneck.move_mean : 
peak memory: 374.17 MiB, increment: 75.54 MiB

Numpy array rolling : 
peak memory: 661.29 MiB, increment: 362.65 MiB

Numpy array rolling, edge handling : 
peak memory: 1111.25 MiB, increment: 812.61 MiB

Пам'ять, велике вікно (n = 1001)

scipy.convolve : 
peak memory: 370.62 MiB, increment: 71.83 MiB

scipy.convolve, edge handling : 
peak memory: 521.98 MiB, increment: 223.18 MiB

numpy.cumsum : 
peak memory: 451.32 MiB, increment: 152.52 MiB

numpy.cumsum, edge handling : 
peak memory: 527.51 MiB, increment: 228.71 MiB

Pandas rolling average : 
peak memory: 451.25 MiB, increment: 152.50 MiB

bottleneck.move_mean : 
peak memory: 374.64 MiB, increment: 75.85 MiB

11

Ця відповідь за допомогою Pandas адаптована згори, оскільки rolling_meanвона вже не є частиною Pandas

# the recommended syntax to import pandas
import pandas as pd
import numpy as np

# prepare some fake data:
# the date-time indices:
t = pd.date_range('1/1/2010', '12/31/2012', freq='D')

# the data:
x = np.arange(0, t.shape[0])

# combine the data & index into a Pandas 'Series' object
D = pd.Series(x, t)

Тепер просто зателефонуйте функції rollingна кадрі даних з розміром вікна, який у моєму прикладі нижче становить 10 днів.

d_mva10 = D.rolling(10).mean()

# d_mva is the same size as the original Series
# though obviously the first w values are NaN where w is the window size
d_mva10[:11]

2010-01-01    NaN
2010-01-02    NaN
2010-01-03    NaN
2010-01-04    NaN
2010-01-05    NaN
2010-01-06    NaN
2010-01-07    NaN
2010-01-08    NaN
2010-01-09    NaN
2010-01-10    4.5
2010-01-11    5.5
Freq: D, dtype: float64

5

Я думаю, що це можна легко вирішити за допомогою вузького місця

Дивіться базовий зразок нижче:

import numpy as np
import bottleneck as bn

a = np.random.randint(4, 1000, size=(5, 7))
mm = bn.move_mean(a, window=2, min_count=1)

Це дає середнє переміщення вздовж кожної осі.

  • "mm" - це рухоме значення для "a".

  • "window" - це максимальна кількість записів, які слід врахувати для переміщення середнього значення.

  • "min_count" - це мінімальна кількість записів, яку слід врахувати за рухомим середнім (наприклад, для першого елемента або якщо масив має нан-значення).

Гарна частина полягає в тому, що Bottleneck допомагає боротися з значеннями нана, і це також дуже ефективно.


2

Якщо ви хочете ретельно піклуватися про крайові умови ( обчислюйте середнє значення лише з доступних елементів на краях ), наступна функція виконає трюк.

import numpy as np

def running_mean(x, N):
    out = np.zeros_like(x, dtype=np.float64)
    dim_len = x.shape[0]
    for i in range(dim_len):
        if N%2 == 0:
            a, b = i - (N-1)//2, i + (N-1)//2 + 2
        else:
            a, b = i - (N-1)//2, i + (N-1)//2 + 1

        #cap indices to min and max indices
        a = max(0, a)
        b = min(dim_len, b)
        out[i] = np.mean(x[a:b])
    return out

>>> running_mean(np.array([1,2,3,4]), 2)
array([1.5, 2.5, 3.5, 4. ])

>>> running_mean(np.array([1,2,3,4]), 3)
array([1.5, 2. , 3. , 3.5])

1
for i in range(len(Data)):
    Data[i, 1] = Data[i-lookback:i, 0].sum() / lookback

Спробуйте цей фрагмент коду. Я думаю, що це простіше і робить роботу. lookback - це вікно ковзної середньої.

У розділі Data[i-lookback:i, 0].sum()Я 0посилаюся на перший стовпець набору даних, але ви можете помістити будь-який стовпець, який вам подобається, якщо у вас є більше одного стовпця.


0

Я насправді хотів трохи іншої поведінки, ніж прийнята відповідь. Я будував ковзаючу середню функцію витяжки для sklearnтрубопроводу, тому мені потрібно було, щоб вихід ковзаючої середньої величини був такий же розмір, що і вхідний. Мені хочеться, щоб середня середня припускала, що серія залишається постійною, тобто ковзаюче середнє значення [1,2,3,4,5]з вікном 2 дало б [1.5,2.5,3.5,4.5,5.0].

Для векторів стовпців (мій варіант використання) ми отримуємо

def moving_average_col(X, n):
  z2 = np.cumsum(np.pad(X, ((n,0),(0,0)), 'constant', constant_values=0), axis=0)
  z1 = np.cumsum(np.pad(X, ((0,n),(0,0)), 'constant', constant_values=X[-1]), axis=0)
  return (z1-z2)[(n-1):-1]/n

І для масивів

def moving_average_array(X, n):
  z2 = np.cumsum(np.pad(X, (n,0), 'constant', constant_values=0))
  z1 = np.cumsum(np.pad(X, (0,n), 'constant', constant_values=X[-1]))
  return (z1-z2)[(n-1):-1]/n

Звичайно, не потрібно приймати постійні значення для прокладки, але це має бути адекватним у більшості випадків.


0

таліб містить простий інструмент ковзної середньої, а також інші подібні інструменти усереднення (тобто експоненціальну ковзну середню). Нижче порівнюється метод з деякими іншими рішеннями.


%timeit pd.Series(np.arange(100000)).rolling(3).mean()
2.53 ms ± 40.5 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)

%timeit talib.SMA(real = np.arange(100000.), timeperiod = 3)
348 µs ± 3.5 µs per loop (mean ± std. dev. of 7 runs, 1000 loops each)

%timeit moving_average(np.arange(100000))
638 µs ± 45.1 µs per loop (mean ± std. dev. of 7 runs, 1000 loops each)

Одне застереження - це те, що справжні повинні мати елементи dtype = float. Інакше виникає наступна помилка

Виняток: реальний не є подвійним


0

Ось швидка реалізація за допомогою numba (пам'ятайте про типи). Зверніть увагу, він містить нан, де зміщений.

import numpy as np
import numba as nb

@nb.jit(nb.float64[:](nb.float64[:],nb.int64),
        fastmath=True,nopython=True)
def moving_average( array, window ):    
    ret = np.cumsum(array)
    ret[window:] = ret[window:] - ret[:-window]
    ma = ret[window - 1:] / window
    n = np.empty(window-1); n.fill(np.nan)
    return np.concatenate((n.ravel(), ma.ravel())) 

Це повертає нан на початку.
— Адам Еріксон

0

ковзний середній

  • повернути масив на i і просто взяти середнє значення від i до n.

  • використовувати розуміння списку для створення міні-масивів на льоту.

x = np.random.randint(10, size=20)

def moving_average(arr, n):
    return [ (arr[:i+1][::-1][:n]).mean() for i, ele in enumerate(arr) ]
n = 5

moving_average(x, n)

0

Я використовую або прийняте рішення відповіді, злегка модифіковане, щоб мати таку ж довжину для виведення як вхід, або pandas"версію, як згадується в коментарі іншої відповіді. Я підсумовую обидва тут з відтворюваним прикладом для подальшої довідки:

import numpy as np
import pandas as pd

def moving_average(a, n):
    ret = np.cumsum(a, dtype=float)
    ret[n:] = ret[n:] - ret[:-n]
    return ret / n

def moving_average_centered(a, n):
    return pd.Series(a).rolling(window=n, center=True).mean().to_numpy()

A = [0, 0, 1, 2, 4, 5, 4]
print(moving_average(A, 3))    
# [0.         0.         0.33333333 1.         2.33333333 3.66666667 4.33333333]
print(moving_average_centered(A, 3))
# [nan        0.33333333 1.         2.33333333 3.66666667 4.33333333 nan       ]

0

Порівнюючи наведене нижче рішення з тим, яке використовує кончину нумету, ця займає майже половину часу . Це тому, що не потрібно пройти весь масив, щоб виконати закінчення, а потім зробити все віднімання. Більше того, закінчення може бути " небезпечним ", якщо масив величезний, а кількість величезна ( можливий переповнення ). Звичайно, і тут небезпека існує, але принаймні підсумовуються разом лише основні числа.

def moving_average(array_numbers, n):
    if n > len(array_numbers):
      return []
    temp_sum = sum(array_numbers[:n])
    averages = [temp_sum / float(n)]
    for first_index, item in enumerate(array_numbers[n:]):
        temp_sum += item - array_numbers[first_index]
        averages.append(temp_sum / float(n))
    return averages
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.