Существует ли функция SciPy или функция NumPy или модуль для Python, который вычисляет текущее среднее значение одномерного массива для определенного окна?
Текущее среднее - это случай математической операции свертки. Для текущего среднего вы перемещаете окно вдоль входных данных и вычисляете среднее значение содержимого окна. Для дискретных одномерных сигналов свертка - это то же самое, за исключением того, что вместо среднего вы вычисляете произвольную линейную комбинацию, т. Е. Умножаете каждый элемент на соответствующий коэффициент и суммируете результаты. Эти коэффициенты, по одному для каждой позиции в окне, иногда называют ядром свертки. Среднее арифметическое из N значений равно (x_1 + x_2 + ... + x_N) / N, поэтому соответствующее ядро равно (1/N, 1/N, ..., 1/N), и это именно то, что мы получаем, используя np.ones(N)/N.
Ребра
В mode аргументе np.convolve указывается, как обрабатывать ребра. Я выбрал здесь режим valid, потому что думаю, что большинство людей ожидают, что именно так будет работать running mean, но у вас могут быть другие приоритеты. Вот график, иллюстрирующий разницу между режимами:
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()
Ответ 2
Эффективное решение
Свертка намного лучше, чем простой подход, но (я думаю) она использует БПФ и, следовательно, довольно медленная. Однако специально для вычисления текущего среднего следующий подход работает нормально
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, два метода эквивалентны. Чем больше N, тем больше разница во времени.
предупреждение: хотя cumsum работает быстрее, будет увеличена ошибка с плавающей запятой, которая может привести к тому, что ваши результаты будут недействительными / некорректными / неприемлемыми
# 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, но зависит от ваших данных)
вы можете отобразить ошибку на графике и увидеть, как она относительно быстро увеличивается
решение convolve работает медленнее, но не приводит к потере точности с плавающей запятой
решение uniform_filter1d быстрее, чем это решение cumsum, И не имеет такой потери точности с плавающей запятой
%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 as ndi defrunning_mean_convolve(x, N): return np.convolve(x, np.ones(N) / float(N), 'valid') defrunning_mean_cumsum(x, N): cumsum = np.cumsum(np.insert(x, 0, 0)) return (cumsum[N:] - cumsum[:-N]) / float(N) defrunning_mean_uniform_filter1d(x, N): return ndi.uniform_filter1d(x, N, mode='constant', origin=-(N//2))[:-(N-1)]
Ответ 4
Обновление: Приведенный ниже пример показывает старую pandas.rolling_mean функцию, которая была удалена в последних версиях pandas. Современный эквивалент вызова этой функции использовал бы pandas.Серия.переходящая:
Для этого больше подходитpandas, чем NumPy или SciPy. Его функция rolling_mean выполняет эту работу удобно. Он также возвращает массив NumPy, когда входными данными является массив.
Трудно превзойти rolling_mean по производительности любую пользовательскую реализацию на чистом Python. Вот пример производительности по сравнению с двумя предлагаемыми решениями:
In [1]: import numpy as np
In [2]: import pandas as pd
In [3]: defrunning_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
Есть также хорошие варианты того, как обращаться со значениями границ.