Як обчислити ковзну середню за допомогою 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.