Câu trả lời:
Đối với một giải pháp ngắn, nhanh, thực hiện toàn bộ trong một vòng lặp, không phụ thuộc, mã dưới đây hoạt động rất tốt.
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)
CẬP NHẬT : các giải pháp hiệu quả hơn đã được đề xuất bởi Alleo và jasaarim .
Bạn có thể sử dụng np.convolve
cho điều đó:
np.convolve(x, np.ones((N,))/N, mode='valid')
Giá trị trung bình là một trường hợp của phép toán tích chập . Đối với giá trị trung bình đang chạy, bạn trượt một cửa sổ dọc theo đầu vào và tính giá trị trung bình của nội dung của cửa sổ. Đối với các tín hiệu 1D rời rạc, tích chập là điều tương tự, ngoại trừ thay vì trung bình bạn tính một tổ hợp tuyến tính tùy ý, tức là nhân mỗi phần tử với một hệ số tương ứng và cộng các kết quả. Các hệ số đó, một cho mỗi vị trí trong cửa sổ, đôi khi được gọi là hạt nhân chập . Bây giờ, giá trị trung bình số học của các giá trị N là (x_1 + x_2 + ... + x_N) / N
, vì vậy hạt nhân tương ứng là (1/N, 1/N, ..., 1/N)
và đó chính xác là những gì chúng ta nhận được bằng cách sử dụng np.ones((N,))/N
.
Đối mode
số np.convolve
chỉ định cách xử lý các cạnh. Tôi đã chọn valid
chế độ ở đây vì tôi nghĩ đó là cách mà hầu hết mọi người mong đợi hoạt động có nghĩa là hoạt động, nhưng bạn có thể có những ưu tiên khác. Dưới đây là một cốt truyện minh họa sự khác biệt giữa các chế độ:
import numpy as np
import matplotlib.pyplot as plt
modes = ['full', 'same', 'valid']
for m in modes:
plt.plot(np.convolve(np.ones((200,)), np.ones((50,))/50, mode=m));
plt.axis([-10, 251, -.1, 1.1]);
plt.legend(modes, loc='lower center');
plt.show()
numpy.cumsum
có độ phức tạp tốt hơn.
Convolution tốt hơn nhiều so với cách tiếp cận đơn giản, nhưng (tôi đoán) nó sử dụng FFT và do đó khá chậm. Tuy nhiên, đặc biệt để tính toán hoạt động có nghĩa là cách tiếp cận sau hoạt động tốt
def running_mean(x, N):
cumsum = numpy.cumsum(numpy.insert(x, 0, 0))
return (cumsum[N:] - cumsum[:-N]) / float(N)
Mã để kiểm tra
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
Lưu ý rằng numpy.allclose(result1, result2)
là True
, hai phương pháp là tương đương. N càng lớn, sự khác biệt về thời gian càng lớn.
# demonstrate loss of precision with only 100,000 points
np.random.seed(42)
x = np.random.randn(100000)+1e6
y1 = running_mean_convolve(x, 10)
y2 = running_mean_cumsum(x, 10)
assert np.allclose(y1, y2, rtol=1e-12, atol=0)
np.longdouble
nhưng lỗi dấu phẩy động của bạn vẫn sẽ có ý nghĩa đối với số điểm tương đối lớn (khoảng> 1e5 nhưng phụ thuộc vào dữ liệu của bạn)numpy.convolve
là O (mn); tài liệu của nó đề cập đến việc scipy.signal.fftconvolve
sử dụng FFT.
running_mean([1,2,3], 2)
cho array([1, 2])
. Thay thế x
bằng [float(value) for value in x]
thủ thuật.
x
chứa phao. Ví dụ: running_mean(np.arange(int(1e7))[::-1] + 0.2, 1)[-1] - 0.2
trả về 0.003125
trong khi người ta mong đợi 0.0
. Thêm thông tin: vi.wikipedia.org/wiki/Loss_of_significance
Cập nhật: Ví dụ dưới đây cho thấy pandas.rolling_mean
chức năng cũ đã bị xóa trong các phiên bản gần đây của gấu trúc. Một tương đương hiện đại của chức năng gọi dưới đây sẽ là
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])
gấu trúc thích hợp cho việc này hơn NumPy hoặc SciPy. Chức năng của nó cán_mean thực hiện công việc một cách thuận tiện. Nó cũng trả về một mảng NumPy khi đầu vào là một mảng.
Rất khó để đánh bại rolling_mean
hiệu năng với bất kỳ triển khai Python thuần tùy chỉnh nào. Dưới đây là một ví dụ về hiệu suất đối với hai trong số các giải pháp được đề xuất:
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
Ngoài ra còn có các tùy chọn tốt đẹp như làm thế nào để đối phó với các giá trị cạnh.
df.rolling(windowsize).mean()
bây giờ hoạt động thay thế (rất nhanh tôi có thể thêm). cho chuỗi 6.000 hàng %timeit test1.rolling(20).mean()
trả về 1000 vòng, tốt nhất là 3: 1,16 ms mỗi vòng
df.rolling()
hoạt động đủ tốt, vấn đề là ngay cả hình thức này sẽ không hỗ trợ ndarrays trong tương lai. Để sử dụng nó, trước tiên chúng ta sẽ phải tải dữ liệu của mình vào một Dataframe Pandas. Tôi rất thích nhìn thấy chức năng này được bổ sung vào một trong hai numpy
hoặc scipy.signal
.
%timeit bottleneck.move_mean(x, N)
nhanh hơn 3 đến 15 lần so với phương pháp cumsum và gấu trúc trên máy tính của tôi. Hãy xem điểm chuẩn của họ trong README của repo .
Bạn có thể tính toán trung bình hoạt động với:
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
Nhưng nó chậm.
May mắn thay, numpy bao gồm một hàm chập mà chúng ta có thể sử dụng để tăng tốc mọi thứ. Giá trị trung bình tương đương với tích chập x
với một vectơ N
dài, với tất cả các thành viên bằng 1/N
. Việc triển khai tích hợp nhanh chóng bao gồm tạm thời bắt đầu, do đó bạn phải xóa các điểm N-1 đầu tiên:
def runningMeanFast(x, N):
return np.convolve(x, np.ones((N,))/N)[(N-1):]
Trên máy của tôi, phiên bản nhanh nhanh hơn 20-30 lần, tùy thuộc vào độ dài của vectơ đầu vào và kích thước của cửa sổ trung bình.
Lưu ý rằng tích hợp bao gồm một 'same'
chế độ có vẻ như sẽ giải quyết vấn đề nhất thời bắt đầu, nhưng nó sẽ phân chia nó giữa đầu và cuối.
mode='valid'
trong convolve
đó không yêu cầu xử lý hậu kỳ.
mode='valid'
loại bỏ tạm thời từ cả hai đầu, phải không? Nếu len(x)=10
và N=4
, đối với một hoạt động có nghĩa là tôi muốn có 10 kết quả nhưng valid
trả về 7.
modes = ('full', 'same', 'valid'); [plot(convolve(ones((200,)), ones((50,))/50, mode=m)) for m in modes]; axis([-10, 251, -.1, 1.1]); legend(modes, loc='lower center')
(với pyplot và numpy được nhập).
runningMean
Tôi có tác dụng phụ của việc tính trung bình với các số không, khi bạn đi ra khỏi mảng với x[ctr:(ctr+N)]
phía bên phải của mảng.
runningMeanFast
cũng có vấn đề hiệu ứng biên giới này.
hoặc mô-đun cho python tính toán
trong các thử nghiệm của tôi tại Tradewave.net TA-lib luôn thắng:
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])
các kết quả:
[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
. Tôi nhận được lỗi này, thưa ông.
Để biết giải pháp sẵn sàng sử dụng, hãy xem https://scipy-cookbook.readthedocs.io/items/SignalSmooth.html . Nó cung cấp trung bình chạy với flat
loại cửa sổ. Lưu ý rằng phương pháp này phức tạp hơn một chút so với phương pháp xác thực tự làm đơn giản, vì nó cố gắng xử lý các vấn đề ở đầu và cuối dữ liệu bằng cách phản ánh nó (có thể hoặc không thể hoạt động trong trường hợp của bạn. ..).
Để bắt đầu, bạn có thể thử:
a = np.random.random(100)
plt.plot(a)
b = smooth(a, window='flat')
plt.plot(b)
numpy.convolve
, sự khác biệt chỉ trong việc thay đổi trình tự.
w
kích thước cửa sổ và s
dữ liệu?
Bạn có thể sử dụng 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'
là mặc định, nhưng trong trường hợp của tôi, tôi muốn'nearest'
Nó cũng khá nhanh (nhanh hơn gần 50 lần và nhanh hơn np.convolve
2-5 lần so với phương pháp cumsum được đưa ra ở trên ):
%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
Dưới đây là 3 chức năng cho phép bạn so sánh lỗi / tốc độ thực hiện khác nhau:
from __future__ import division
import numpy as np
import scipy.ndimage.filters as ndif
def running_mean_convolve(x, N):
return np.convolve(x, np.ones(N) / float(N), 'valid')
def running_mean_cumsum(x, N):
cumsum = np.cumsum(np.insert(x, 0, 0))
return (cumsum[N:] - cumsum[:-N]) / float(N)
def running_mean_uniform_filter1d(x, N):
return ndif.uniform_filter1d(x, N, mode='constant', origin=-(N//2))[:-(N-1)]
uniform_filter1d
, np.convolve
với một hình chữ nhật, và np.cumsum
theo sau np.subtract
. kết quả của tôi: (1.) chập là chậm nhất. (2.) cumsum / phép trừ nhanh hơn khoảng 20-30 lần. (3.) thống_filter1d nhanh hơn khoảng 2-3 lần so với cumsum / phép trừ. người chiến thắng chắc chắn là thống nhất_filter1d.
uniform_filter1d
là nhanh hơn so với cumsum
giải pháp (khoảng 2-5x). và uniform_filter1d
không nhận được lỗi dấu phẩy động lớn nhưcumsum
giải pháp.
Tôi biết đây là một câu hỏi cũ, nhưng đây là một giải pháp không sử dụng bất kỳ cấu trúc dữ liệu hoặc thư viện bổ sung nào. Đó là tuyến tính về số lượng các yếu tố của danh sách đầu vào và tôi không thể nghĩ ra cách nào khác để làm cho nó hiệu quả hơn (thực sự nếu có ai biết cách phân bổ kết quả tốt hơn, vui lòng cho tôi biết).
LƯU Ý: điều này sẽ nhanh hơn nhiều khi sử dụng một mảng numpy thay vì một danh sách, nhưng tôi muốn loại bỏ tất cả các phụ thuộc. Cũng có thể cải thiện hiệu suất bằng cách thực hiện đa luồng
Hàm giả định rằng danh sách đầu vào là một chiều, vì vậy hãy cẩn thận.
### 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
Thí dụ
Giả sử rằng chúng tôi có một danh sách data = [ 1, 2, 3, 4, 5, 6 ]
mà chúng tôi muốn tính toán trung bình cán trong khoảng thời gian 3 và bạn cũng muốn một danh sách đầu ra có cùng kích thước của đầu vào (thường là như vậy).
Phần tử đầu tiên có chỉ số 0, do đó, giá trị trung bình phải được tính trên các phần tử của chỉ số -2, -1 và 0. Rõ ràng chúng ta không có dữ liệu [-2] và dữ liệu [-1] (trừ khi bạn muốn sử dụng đặc biệt điều kiện biên), vì vậy chúng tôi giả sử rằng các phần tử đó bằng 0. Điều này tương đương với phần không đệm trong danh sách, ngoại trừ chúng tôi không thực sự đệm nó, chỉ cần theo dõi các chỉ số cần đệm (từ 0 đến N-1).
Vì vậy, đối với các phần tử N đầu tiên, chúng ta chỉ cần tiếp tục cộng các phần tử trong một bộ tích lũy.
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
Từ các yếu tố N + 1 chuyển tiếp tích lũy đơn giản không hoạt động. chúng tôi mong đợi result[3] = (2 + 3 + 4)/3 = 3
nhưng điều này khác với (sum + 4)/3 = 3.333
.
Cách để tính toán giá trị chính xác là để trừ data[0] = 1
từ sum+4
, do đó đem lại sum + 4 - 1 = 9
.
Điều này xảy ra bởi vì hiện tại sum = data[0] + data[1] + data[2]
, nhưng nó cũng đúng với mọi i >= N
bởi vì, trước khi trừ, sum
là data[i-N] + ... + data[i-2] + data[i-1]
.
Tôi cảm thấy điều này có thể được giải quyết một cách thanh lịch bằng cách sử dụng nút cổ chai
Xem mẫu cơ bản dưới đây:
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" là giá trị trung bình của "a".
"window" là số lượng mục nhập tối đa cần xem xét để di chuyển trung bình.
"min_count" là số lượng mục nhập tối thiểu cần xem xét để di chuyển trung bình (ví dụ: đối với một số phần tử đầu tiên hoặc nếu mảng có giá trị nan).
Phần tốt là Bottleneck giúp xử lý các giá trị nan và nó cũng rất hiệu quả.
Tôi chưa kiểm tra xem nó nhanh như thế nào, nhưng bạn có thể thử:
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)
Câu trả lời này chứa các giải pháp sử dụng thư viện chuẩn Python cho ba kịch bản khác nhau.
itertools.accumulate
Đây là một giải pháp Python 3.2+ hiệu quả về bộ nhớ, tính toán trung bình đang chạy trên một giá trị lặp lại bằng cách tận dụng itertools.accumulate
.
>>> from itertools import accumulate
>>> values = range(100)
Lưu ý rằng values
có thể là bất kỳ lần lặp nào, bao gồm các trình tạo hoặc bất kỳ đối tượng nào khác tạo ra các giá trị một cách nhanh chóng.
Đầu tiên, lười biếng xây dựng tổng tích lũy của các giá trị.
>>> cumu_sum = accumulate(value_stream)
Tiếp theo, enumerate
tổng tích lũy (bắt đầu từ 1) và xây dựng một trình tạo mang lại tỷ lệ giá trị tích lũy và chỉ số liệt kê hiện tại.
>>> rolling_avg = (accu/i for i, accu in enumerate(cumu_sum, 1))
Bạn có thể phát hành means = list(rolling_avg)
nếu bạn cần tất cả các giá trị trong bộ nhớ cùng một lúc hoặc gọi next
tăng dần.
(Tất nhiên, bạn cũng có thể lặp đi lặp lại rolling_avg
với một for
vòng lặp, sẽ gọi next
ngầm.)
>>> next(rolling_avg) # 0/1
>>> 0.0
>>> next(rolling_avg) # (0 + 1)/2
>>> 0.5
>>> next(rolling_avg) # (0 + 1 + 2)/3
>>> 1.0
Giải pháp này có thể được viết như một chức năng như sau.
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 này tiêu thụ các giá trị bạn gửi nó và giữ mức trung bình hoạt động của các giá trị được nhìn thấy cho đến nay.
Nó rất hữu ích khi bạn không có các giá trị lặp lại nhưng chứa các giá trị được tính trung bình từng cái một vào các thời điểm khác nhau trong suốt cuộc đời của chương trình.
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
Các coroutine hoạt động như thế này:
>>> 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
Hàm tạo này có một kích thước lặp và kích thước cửa sổ N
và mang lại giá trị trung bình trên các giá trị hiện tại bên trong cửa sổ. Nó sử dụng một deque
, đó là một cơ sở hạ tầng tương tự như một danh sách, nhưng được tối ưu hóa để sửa đổi nhanh ( pop
, append
) ở cả hai điểm cuối .
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)
Đây là chức năng trong hành động:
>>> 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
Đến bữa tiệc muộn một chút, nhưng tôi đã tạo ra chức năng nhỏ của riêng mình mà KHÔNG quấn quanh các đầu hoặc miếng đệm bằng số không được sử dụng để tìm mức trung bình. Một điều trị nữa là, nó cũng lấy mẫu lại tín hiệu tại các điểm cách nhau tuyến tính. Tùy chỉnh mã theo ý muốn để có được các tính năng khác.
Phương thức này là một phép nhân ma trận đơn giản với một nhân Gaussian được chuẩn hóa.
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
Một cách sử dụng đơn giản trên tín hiệu hình sin có thêm nhiễu phân tán bình thường:
sum
, sử dụng np.sum
thay vì 2 Các @
nhà điều hành (không có ý tưởng gì đó là) ném một lỗi. Tôi có thể xem xét nó sau nhưng tôi đang thiếu thời gian ngay bây giờ
Thay vì numpy hoặc scipy, tôi muốn đề nghị gấu trúc làm điều này nhanh hơn:
df['data'].rolling(3).mean()
Điều này lấy trung bình di chuyển (MA) của 3 giai đoạn của "dữ liệu" cột. Bạn cũng có thể tính toán các phiên bản đã thay đổi, ví dụ: phiên bản loại trừ ô hiện tại (đã dịch ngược một lần) có thể được tính toán dễ dàng như sau:
df['data'].shift(periods=1).rolling(3).mean()
pandas.rolling_mean
trong khi sử dụng của tôi pandas.DataFrame.rolling
. Bạn cũng có thể tính toán di chuyển min(), max(), sum()
vv cũng như mean()
với phương pháp này một cách dễ dàng.
pandas.rolling_min, pandas.rolling_max
vv Chúng tương tự nhau nhưng khác nhau.
Có một nhận xét của mab chôn trong một trong những câu trả lời ở trên có phương pháp này. bottleneck
có move_mean
một trung bình di chuyển đơn giản:
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
là một tham số tiện dụng về cơ bản sẽ lấy trung bình di chuyển đến điểm đó trong mảng của bạn. Nếu bạn không đặt min_count
, nó sẽ bằng nhau window
và mọi thứ window
sẽ được tính điểm nan
.
Một cách tiếp cận khác để tìm trung bình di chuyển mà không sử dụng numpy, panda
import itertools
sample = [2, 6, 10, 8, 11, 10]
list(itertools.starmap(lambda a,b: b/a,
enumerate(itertools.accumulate(sample), 1)))
sẽ in [2.0, 4.0, 6.0, 6.5, 7.4, 7.833333333333333]
Câu hỏi này thậm chí còn cũ hơn so với khi NeXuS viết về nó vào tháng trước, NHƯNG tôi thích cách mã của anh ấy xử lý các trường hợp cạnh. Tuy nhiên, vì là "trung bình di chuyển đơn giản", kết quả của nó bị tụt hậu so với dữ liệu họ áp dụng. Tôi nghĩ rằng đối phó với trường hợp cạnh một cách thỏa mãn hơn chế độ NumPy của valid
, same
và full
có thể đạt được bằng cách áp dụng một cách tiếp cận tương tự như một convolution()
phương pháp dựa.
Đóng góp của tôi sử dụng trung bình chạy trung tâm để căn chỉnh kết quả của nó với dữ liệu của họ. Khi có quá ít điểm có sẵn cho cửa sổ kích thước đầy đủ được sử dụng, trung bình chạy được tính từ các cửa sổ nhỏ hơn liên tiếp ở các cạnh của mảng. [Trên thực tế, từ các cửa sổ lớn hơn liên tiếp, nhưng đó là một chi tiết triển khai.]
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]])
Nó tương đối chậm bởi vì nó sử dụng convolve()
, và có khả năng có thể được tạo ra khá nhiều bởi một Pythonista thực sự, tuy nhiên, tôi tin rằng ý tưởng đó tồn tại.
Có nhiều câu trả lời ở trên về việc tính toán trung bình đang chạy. Câu trả lời của tôi thêm hai tính năng bổ sung:
Tính năng thứ hai này đặc biệt hữu ích để xác định giá trị nào khác với xu hướng chung theo một lượng nhất định.
Tôi sử dụng numpy.cumsum vì đây là phương pháp hiệu quả nhất về thời gian ( xem câu trả lời của Alleo ở trên ).
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)
Mã này chỉ hoạt động cho Ns. Nó có thể được điều chỉnh cho các số lẻ bằng cách thay đổi np.insert của padded_x và n_nan.
Ví dụ đầu ra (thô màu đen, Movavg màu xanh lam):
Mã này có thể được điều chỉnh dễ dàng để loại bỏ tất cả các giá trị trung bình di chuyển được tính toán từ ít hơn cutoff = 3 giá trị không nan.
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)
Chỉ sử dụng Thư viện chuẩn Python (Hiệu quả bộ nhớ)
Chỉ cần cung cấp một phiên bản khác của việc sử dụng thư viện tiêu chuẩn deque
mà thôi. Tôi khá ngạc nhiên khi hầu hết các câu trả lời đang sử dụng pandas
hoặc 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]
Trên thực tế tôi đã tìm thấy một triển khai khác trong tài liệu python
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
Tuy nhiên, việc thực hiện có vẻ phức tạp hơn một chút so với thực tế. Nhưng nó phải nằm trong tài liệu python tiêu chuẩn vì một lý do, ai đó có thể nhận xét về việc triển khai tài liệu của tôi và tài liệu chuẩn không?
O(n*d)
phép tính ( d
là kích thước của cửa sổ, n
kích thước của vòng lặp) và chúng đang thực hiệnO(n)
Mặc dù có giải pháp cho câu hỏi này ở đây, xin vui lòng xem giải pháp của tôi. Nó rất đơn giản và làm việc tốt.
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)
Từ việc đọc các câu trả lời khác, tôi không nghĩ đây là câu hỏi yêu cầu, nhưng tôi đã đến đây với nhu cầu giữ trung bình hoạt động của một danh sách các giá trị đang tăng kích thước.
Vì vậy, nếu bạn muốn giữ một danh sách các giá trị mà bạn đang có được từ một nơi nào đó (trang web, thiết bị đo, v.v.) và trung bình của các n
giá trị cuối cùng được cập nhật, bạn có thể sử dụng mã dưới đây, để giảm thiểu nỗ lực thêm mới các yếu tố:
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)
Và bạn có thể kiểm tra nó với, ví dụ:
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()
Cung cấp cho:
Một giải pháp khác chỉ cần sử dụng một thư viện tiêu chuẩn và deque:
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
Đối với mục đích giáo dục, hãy để tôi thêm hai giải pháp Numpy (chậm hơn giải pháp cumsum):
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
Các hàm được sử dụng: as_strided , add.reduceat
Tất cả các giải pháp nói trên đều kém vì chúng thiếu
numpy.cumsum
, hoặcO(len(x) * w)
thực hiện như kết luận.Được
import numpy
m = 10000
x = numpy.random.rand(m)
w = 1000
Lưu ý rằng x_[:w].sum()
bằng x[:w-1].sum()
. Vì vậy, đối với mức trung bình đầu tiên numpy.cumsum(...)
bổ sung x[w] / w
(thông qua x_[w+1] / w
), và trừ 0
(từ x_[0] / w
). Kết quả này trongx[0:w].mean()
Thông qua cumsum, bạn sẽ cập nhật mức trung bình thứ hai bằng cách cộng x[w+1] / w
và trừ x[0] / w
, kết quả là x[1:w+1].mean()
.
Điều này diễn ra cho đến khi x[-w:].mean()
đạt được.
x_ = numpy.insert(x, 0, 0)
sliding_average = x_[:w].sum() / w + numpy.cumsum(x_[w:] - x_[:-w]) / w
Giải pháp này được vector hóa O(m)
, có thể đọc và ổn định về số lượng.
Làm thế nào về một bộ lọc trung bình di chuyển ? Nó cũng là một lớp lót và có một lợi thế, bạn có thể dễ dàng thao tác loại cửa sổ nếu bạn cần một cái gì đó khác ngoài hình chữ nhật, tức là. trung bình di chuyển đơn giản dài N của một mảng a:
lfilter(np.ones(N)/N, [1], a)[N:]
Và với cửa sổ hình tam giác được áp dụng:
lfilter(np.ones(N)*scipy.signal.triang(N)/N, [1], a)[N:]
Lưu ý: Tôi thường loại bỏ N mẫu đầu tiên là không có thật do đó [N:]
ở cuối, nhưng nó không cần thiết và chỉ là vấn đề lựa chọn cá nhân.
Nếu bạn chọn tự cuộn, thay vì sử dụng thư viện hiện có, vui lòng lưu ý đến lỗi dấu phẩy động và cố gắng giảm thiểu ảnh hưởng của nó:
class SumAccumulator:
def __init__(self):
self.values = [0]
self.count = 0
def add( self, val ):
self.values.append( val )
self.count = self.count + 1
i = self.count
while i & 0x01:
i = i >> 1
v0 = self.values.pop()
v1 = self.values.pop()
self.values.append( v0 + v1 )
def get_total(self):
return sum( reversed(self.values) )
def get_size( self ):
return self.count
Nếu tất cả các giá trị của bạn có cùng độ lớn, thì điều này sẽ giúp duy trì độ chính xác bằng cách luôn luôn thêm các giá trị có độ lớn gần tương tự nhau.