Czy istnieje funkcja SciPy lub funkcja lub moduł NumPy dla Pythona, która oblicza średnią roboczą tablicy 1D w określonym oknie?
python
numpy
scipy
moving-average
Shejo284
źródło
źródło
UPD: Alleo i jasaarim zaproponowały bardziej wydajne rozwiązania .
Możesz użyć
np.convolve
do tego:Wyjaśnienie
Średnia bieżąca to przypadek działania matematycznego splotu . Dla średniej biegnącej przesuwasz okno wzdłuż danych wejściowych i obliczasz średnią zawartość okna. W przypadku dyskretnych sygnałów 1D splot jest tym samym, z tą różnicą, że zamiast średniej oblicza się dowolną kombinację liniową, tj. Pomnożyć każdy element przez odpowiedni współczynnik i zsumować wyniki. Współczynniki te, po jednym dla każdej pozycji w oknie, są czasami nazywane jądrem splotowym . Średnia arytmetyczna wartości N jest
(x_1 + x_2 + ... + x_N) / N
więc odpowiednia jądro(1/N, 1/N, ..., 1/N)
i właśnie to otrzymujemynp.ones((N,))/N
.Krawędzie
mode
Argumentnp.convolve
określa sposób obsługiwać brzegi. Wybrałemvalid
tutaj tryb, ponieważ myślę, że tak większość ludzi oczekuje, że bieganie będzie działać, ale możesz mieć inne priorytety. Oto wykres ilustrujący różnicę między trybami:źródło
numpy.cumsum
ma większą złożoność.Wydajne rozwiązanie
Konwolucja jest znacznie lepsza niż proste podejście, ale (jak sądzę) używa FFT, a zatem dość powolna. Jednak specjalnie do obliczania biegu oznacza, że następujące podejście działa dobrze
Kod do sprawdzenia
Zauważ, że
numpy.allclose(result1, result2)
toTrue
dwie metody są równoważne. Im większy N, tym większa różnica w czasie.ostrzeżenie: chociaż suma jest szybsza, wystąpi zwiększony błąd zmiennoprzecinkowy, który może spowodować, że wyniki będą nieprawidłowe / niepoprawne / niedopuszczalne
komentarze wskazywały na ten problem błędu zmiennoprzecinkowego tutaj, ale robię to bardziej oczywistym tutaj w odpowiedzi. .
np.longdouble
ale błąd zmiennoprzecinkowy nadal będzie znaczący dla stosunkowo dużej liczby punktów (około> 1e5, ale zależy od twoich danych)źródło
numpy.convolve
O (mn); jego dokumenty wspominają, żescipy.signal.fftconvolve
używa FFT.running_mean([1,2,3], 2)
dajearray([1, 2])
. Zastąpieniex
przez[float(value) for value in x]
robi lewę.x
zawiera zmiennoprzecinkowe. Przykład:running_mean(np.arange(int(1e7))[::-1] + 0.2, 1)[-1] - 0.2
zwraca,0.003125
gdy się tego oczekuje0.0
. Więcej informacji: en.wikipedia.org/wiki/Loss_of_signianceAktualizacja: Poniższy przykład pokazuje starą
pandas.rolling_mean
funkcję, która została usunięta w najnowszych wersjach pand. Byłby to współczesny odpowiednik poniższego wywołania funkcjipandy są bardziej odpowiednie do tego niż NumPy lub SciPy. Jego funkcja rolling_mean wykonuje tę pracę wygodnie. Zwraca również tablicę NumPy, gdy dane wejściowe są tablicą.
rolling_mean
Wydajność jest trudna do pokonania dzięki dowolnej niestandardowej implementacji czystego języka Python. Oto przykładowa wydajność w stosunku do dwóch proponowanych rozwiązań:Istnieją również dobre opcje radzenia sobie z wartościami krawędzi.
źródło
df.rolling(windowsize).mean()
teraz działa zamiast tego (bardzo szybko mogę dodać). dla 6000 serii wierszy%timeit test1.rolling(20).mean()
zwróciło 1000 pętli, najlepiej 3: 1,16 ms na pętlędf.rolling()
działa wystarczająco dobrze, problem polega na tym, że nawet ta forma nie będzie obsługiwać ndarrays w przyszłości. Aby go użyć, musimy najpierw załadować nasze dane do ramki danych Pandas. Chciałbym zobaczyć tę funkcję dodaną do jednegonumpy
lubscipy.signal
.%timeit bottleneck.move_mean(x, N)
jest 3 do 15 razy szybszy niż metody sumy i pand na moim komputerze. Zobacz ich punkt odniesienia w README repozytorium .Możesz obliczyć średnią bieżącą za pomocą:
Ale jest wolny.
Na szczęście numpy zawiera funkcję splotu, której możemy użyć do przyspieszenia. Średnia biegowa jest równoważna zwojowi
x
z wektorem, który jestN
długi, a wszystkie elementy są równe1/N
. Implementacja numpy splotu obejmuje początkowy stan przejściowy, więc musisz usunąć pierwsze punkty N-1:Na mojej maszynie szybka wersja jest 20-30 razy szybsza, w zależności od długości wektora wejściowego i wielkości okna uśredniania.
Zauważ, że tryb zwojowy zawiera
'same'
tryb, który wydaje się, że powinien rozwiązać początkowy problem przejściowy, ale dzieli go na początek i koniec.źródło
mode='valid'
wconvolve
których nie wymaga żadnego post-processing.mode='valid'
usuwa stan przejściowy z obu końców, prawda? Jeślilen(x)=10
iN=4
, dla średniej bieżącej, chciałbym 10 wyników, alevalid
zwraca 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')
(z importem pyplot i numpy).runningMean
Mam efekt uboczny uśredniania zerami, gdy wychodzisz z tablicyx[ctr:(ctr+N)]
dla prawej strony tablicy.runningMeanFast
mają również ten problem z efektem granicznym.w moich testach na Tradewave.net TA-lib zawsze wygrywa:
wyniki:
źródło
NameError: name 'info' is not defined
. Dostaję ten błąd, proszę pana.Aby uzyskać gotowe do użycia rozwiązanie, zobacz https://scipy-cookbook.readthedocs.io/items/SignalSmooth.html . Zapewnia średnią roboczą z
flat
typem okna. Zauważ, że jest to nieco bardziej wyrafinowane niż prosta metoda zwojowa „zrób to sam”, ponieważ próbuje poradzić sobie z problemami na początku i na końcu danych, odzwierciedlając je (co może, ale nie musi, działać w twoim przypadku. ..).Na początek możesz spróbować:
źródło
numpy.convolve
, że różnica polega tylko na zmianie sekwencji.w
czy rozmiar okna is
dane?Możesz użyć scipy.ndimage.filters.uniform_filter1d :
uniform_filter1d
:'reflect'
jest domyślna, ale w moim przypadku raczej tego chciałem'nearest'
Jest również dość szybki (prawie 50 razy szybszy niż
np.convolve
i 2-5 razy szybszy niż podane powyżej podejście sumowania ):oto 3 funkcje, które pozwalają porównać błąd / prędkość różnych implementacji:
źródło
uniform_filter1d
,np.convolve
z prostokąta, anp.cumsum
następnienp.subtract
. moje wyniki: (1.) splot jest najwolniejszy. (2.) suma / odejmowanie jest około 20-30x szybsze. (3.) uniform_filter1d jest około 2-3 razy szybszy niż suma / odejmowanie. zwycięzcą jest zdecydowanie uniform_filter1d.uniform_filter1d
jest szybsze niżcumsum
rozwiązanie (o około 2-5x). iuniform_filter1d
nie otrzymuje tak dużego błędu zmiennoprzecinkowego jakcumsum
rozwiązanie.Wiem, że to stare pytanie, ale oto rozwiązanie, które nie korzysta z żadnych dodatkowych struktur danych ani bibliotek. Jest liniowy pod względem liczby elementów listy wejściowej i nie mogę wymyślić żadnego innego sposobu, aby uczynić go bardziej wydajnym (w rzeczywistości, jeśli ktoś zna lepszy sposób przydzielenia wyniku, daj mi znać).
UWAGA: byłoby to znacznie szybsze przy użyciu tablicy numpy zamiast listy, ale chciałem wyeliminować wszystkie zależności. Możliwe byłoby również poprawienie wydajności przez wykonanie wielowątkowe
Funkcja zakłada, że lista wejściowa jest jednowymiarowa, więc bądź ostrożny.
Przykład
Załóżmy, że mamy listę,
data = [ 1, 2, 3, 4, 5, 6 ]
na której chcemy obliczyć średnią kroczącą z okresem 3, i że chcemy również listy wyjściowej, która ma taki sam rozmiar jak wejściowy (tak jest najczęściej).Pierwszy element ma indeks 0, więc średnią kroczącą należy obliczyć na elementach o indeksach -2, -1 i 0. Oczywiście nie mamy danych [-2] i danych [-1] (chyba że chcesz użyć specjalnego warunki brzegowe), więc zakładamy, że te elementy są równe 0. Jest to równoważne wypełnianiu listy przez zero, z wyjątkiem tego, że tak naprawdę nie wypełniamy jej, wystarczy śledzić indeksy, które wymagają wypełnienia (od 0 do N-1).
Tak więc dla pierwszych N elementów po prostu sumujemy elementy w akumulatorze.
Od elementów N + 1 do przodu prosta akumulacja nie działa. oczekujemy,
result[3] = (2 + 3 + 4)/3 = 3
ale różni się to od(sum + 4)/3 = 3.333
.Sposób na obliczenie poprawnej wartości polega na odjęciu
data[0] = 1
od tegosum+4
, co dajesum + 4 - 1 = 9
.Dzieje się tak, ponieważ obecnie
sum = data[0] + data[1] + data[2]
, ale jest to również prawdą dla każdego,i >= N
ponieważ przed odjęciemsum
jestdata[i-N] + ... + data[i-2] + data[i-1]
.źródło
Myślę, że można to elegancko rozwiązać za pomocą wąskiego gardła
Zobacz podstawową próbkę poniżej:
„mm” jest ruchomym środkiem dla „a”.
„okno” to maksymalna liczba pozycji do rozważenia dla średniej ruchomej.
„min_count” to minimalna liczba pozycji do rozważenia dla średniej ruchomej (np. dla kilku pierwszych elementów lub jeśli tablica ma wartości nan).
Zaletą jest to, że wąskie gardło pomaga radzić sobie z wartościami nan, a także jest bardzo wydajne.
źródło
Nie sprawdziłem jeszcze, jak szybko to jest, ale możesz spróbować:
źródło
Ta odpowiedź zawiera rozwiązania wykorzystujące standardową bibliotekę Python dla trzech różnych scenariuszy.
Średnia krocząca z
itertools.accumulate
Jest to wydajne pod względem pamięci rozwiązanie w języku Python 3.2+, które oblicza średnią roboczą z iterowalnych wartości poprzez wykorzystanie dźwigni
itertools.accumulate
.Zauważ, że
values
może to być dowolny iterowalny, w tym generatory lub dowolny inny obiekt, który generuje wartości w locie.Po pierwsze, leniwie konstruuj skumulowaną sumę wartości.
Następnie
enumerate
skumulowana suma (od 1) i zbuduj generator, który da ułamek skumulowanych wartości i aktualny wskaźnik wyliczenia.Możesz wydać,
means = list(rolling_avg)
jeśli potrzebujesz wszystkich wartości w pamięci na raz lub zadzwonićnext
przyrostowo.(Oczywiście możesz także iterować
rolling_avg
wfor
pętli, która wywołanext
niejawnie.)To rozwiązanie można zapisać jako funkcję w następujący sposób.
współprogram do których można wysłać wartości w dowolnym momencie
Ta coroutine zużywa wartości, które jej wysyłasz, i utrzymuje bieżącą średnią z wartości obserwowanych do tej pory.
Przydaje się, gdy nie masz iterowalnych wartości, ale potrzebujesz wartości uśrednianych jeden po drugim w różnych momentach życia programu.
Korpus działa w ten sposób:
Obliczanie średniej z przesuwanego okna wielkości
N
Ta funkcja generatora przyjmuje iterowalny rozmiar okna
N
i daje średnią z bieżących wartości w oknie. Wykorzystuje strukturędeque
, która jest strukturą danych podobną do listy, ale zoptymalizowaną pod kątem szybkich modyfikacji (pop
,append
) w obu punktach końcowych .Oto funkcja w akcji:
źródło
Trochę późno na imprezę, ale stworzyłem swoją własną małą funkcję, która NIE zawija się na końcach lub paskach zerami, które są następnie używane do znalezienia średniej. Kolejną zaletą jest to, że ponownie próbkuje sygnał w liniowo rozmieszczonych punktach. Dostosuj kod do woli, aby uzyskać inne funkcje.
Metoda polega na prostym pomnożeniu macierzy ze znormalizowanym jądrem Gaussa.
Proste użycie sygnału sinusoidalnego z dodanym normalnym szumem rozproszonym:
źródło
sum
, stosującnp.sum
zamiast 2 The@
operator (nie wiem, co to znaczy) wyrzuca błąd. Mogę przyjrzeć się temu później, ale brakuje mi teraz czasu@
to operator mnożenia macierzy, który implementuje np.matmul . Sprawdź, czy twojay_in
tablica jest tablicą numpy, może to być problem.Zamiast numpy lub scipy polecam pandy, aby zrobić to szybciej:
To bierze średnią ruchomą (MA) z 3 okresów kolumny „dane”. Można również obliczyć wersje przesunięte, na przykład tę, która wyklucza bieżącą komórkę (przesuniętą do tyłu), można łatwo obliczyć jako:
źródło
pandas.rolling_mean
podczas gdy mojepandas.DataFrame.rolling
. Możesz również łatwo obliczyć ruchmin(), max(), sum()
itp., A także zamean()
pomocą tej metody.pandas.rolling_min, pandas.rolling_max
itp. Są one podobne, ale różne.Istnieje komentarz mab zakopany w jednej z odpowiedzi powyżej, która ma tę metodę.
bottleneck
mamove_mean
prostą średnią ruchomą:min_count
jest przydatnym parametrem, który zasadniczo podnosi średnią ruchomą do tego momentu w tablicy. Jeśli nie ustawiszmin_count
, będzie równywindow
, a wszystko dowindow
punktów będzienan
.źródło
Inne podejście do znalezienia średniej ruchomej bez użycia numpy, panda
wydrukuje [2.0, 4.0, 6.0, 6.5, 7.4, 7.833333333333333]
źródło
To pytanie jest teraz nawet starsze niż kiedy NeXuS napisał o nim w zeszłym miesiącu, ALE podoba mi się, jak jego kod radzi sobie z przypadkowymi przypadkami. Ponieważ jednak jest to „prosta średnia ruchoma”, jej wyniki są opóźnione w stosunku do danych, których dotyczą. Myślałem, że do czynienia z przypadkami brzegowych w bardziej satysfakcjonujący sposób niż trybach NumPy jest
valid
,same
ifull
może być osiągnięte poprzez zastosowanie podobnego podejścia doconvolution()
metody opartej.Mój wkład wykorzystuje centralną średnią bieżącą do dostosowania wyników do ich danych. Gdy dostępnych jest zbyt mało punktów, aby można było użyć pełnowymiarowego okna, średnie działające są obliczane z kolejno mniejszych okien na krawędziach tablicy. [Właściwie z kolejno większych okien, ale to szczegół implementacji.]
Jest stosunkowo powolny, ponieważ korzysta z niego
convolve()
i prawdopodobnie mógłby zostać wyhodowany przez prawdziwego Pythonistę, jednak uważam, że ten pomysł jest słuszny.źródło
Istnieje wiele odpowiedzi powyżej na temat obliczania średniej biegania. Moja odpowiedź dodaje dwie dodatkowe funkcje:
Ta druga funkcja jest szczególnie przydatna do określania, które wartości odbiegają od ogólnego trendu o określoną wartość.
Używam numpy.cumsum, ponieważ jest to metoda najbardziej wydajna czasowo ( patrz odpowiedź Alleo powyżej ).
Ten kod działa tylko dla Ns. Można go wyregulować dla liczb nieparzystych, zmieniając np. Wstaw padded_x i n_nan.
Przykładowy wynik (surowy w kolorze czarnym, movavg w kolorze niebieskim):
Ten kod można łatwo dostosować do usunięcia wszystkich ruchomych średnich wartości obliczonych z mniejszej niż wartość graniczna = 3 wartości innych niż nan.
źródło
Używaj tylko biblioteki standardowej Python (efektywna pamięć)
Podaj inną wersję korzystania tylko ze standardowej biblioteki
deque
. To dla mnie dość zaskakujące, że większość odpowiedzi używapandas
lubnumpy
.Właściwie znalazłem inną implementację w dokumentach Pythona
Wydaje mi się jednak, że wdrożenie jest nieco bardziej skomplikowane niż powinno. Ale z jakiegoś powodu musi to być standardowa dokumentacja Pythona. Czy ktoś mógłby komentować implementację mojej i standardowej dokumentacji?
źródło
O(n*d)
obliczenia (d
czyli rozmiar okna,n
rozmiar iterowalny) i robiąO(n)
Ze zmiennymi @ Aikude napisałem jedno-liniowy.
źródło
Chociaż są tutaj rozwiązania tego pytania, proszę spojrzeć na moje rozwiązanie. To jest bardzo proste i działa dobrze.
źródło
Po przeczytaniu innych odpowiedzi nie sądzę, by o to pytano, ale dotarłem tutaj z potrzebą utrzymania średniej bieżącej listy wartości, które rosły.
Więc jeśli chcesz zachować listę wartości, które skądś nabywasz (witryna, urządzenie pomiarowe itp.) I średnią z ostatnich
n
zaktualizowanych wartości, możesz użyć poniższego kodu, który minimalizuje wysiłek związany z dodawaniem nowych elementy:Możesz to przetestować na przykład:
Co daje:
źródło
Inne rozwiązanie wykorzystujące standardową bibliotekę i deque:
źródło
W celach edukacyjnych dodam jeszcze dwa rozwiązania Numpy (które są wolniejsze niż rozwiązanie sumsum):
Zastosowane funkcje: as_strided , add.reduceat
źródło
Wszystkie wyżej wymienione rozwiązania są złe, ponieważ ich brakuje
numpy.cumsum
lubO(len(x) * w)
implementacjom jako zwojom.Dany
Zauważ, że
x_[:w].sum()
równa sięx[:w-1].sum()
. Tak więc dla pierwszej średniejnumpy.cumsum(...)
dodajex[w] / w
(przezx_[w+1] / w
) i odejmuje0
(odx_[0] / w
). To skutkujex[0:w].mean()
Za pomocą sumsum zaktualizujesz drugą średnią, dodatkowo dodając
x[w+1] / w
i odejmującx[0] / w
, w wyniku czegox[1:w+1].mean()
.Trwa
x[-w:].mean()
to do momentu osiągnięcia.To rozwiązanie jest wektoryzowane
O(m)
, czytelne i stabilne numerycznie.źródło
Co powiesz na filtr średniej ruchomej ? Jest to również jednowarstwowa i ma tę zaletę, że możesz łatwo manipulować typem okna, jeśli potrzebujesz czegoś innego niż prostokąt, tj. N-prosta prosta ruchoma średnia tablicy a:
A po zastosowaniu trójkątnego okna:
Uwaga: zwykle odrzucam pierwsze N próbek jako fałszywe, dlatego
[N:]
na końcu, ale nie jest to konieczne i jest to tylko kwestia osobistego wyboru.źródło
Jeśli zdecydujesz się rzucić własną, zamiast korzystać z istniejącej biblioteki, pamiętaj o błędzie zmiennoprzecinkowym i spróbuj zminimalizować jego skutki:
Jeśli wszystkie twoje wartości są mniej więcej tego samego rzędu wielkości, pomoże to zachować precyzję, zawsze dodając wartości o mniej więcej podobnych wielkościach.
źródło