Potrzebuję ruchomego okna (znanego również jako przesuwne okno), które można iterować po sekwencji / iteratorze / generatorze. Domyślną iterację Pythona można uznać za przypadek specjalny, w którym długość okna wynosi 1. Obecnie używam następującego kodu. Czy ktoś ma bardziej Pythonic, mniej rozwlekłą lub bardziej wydajną metodę robienia tego?
def rolling_window(seq, window_size):
it = iter(seq)
win = [it.next() for cnt in xrange(window_size)] # First window
yield win
for e in it: # Subsequent windows
win[:-1] = win[1:]
win[-1] = e
yield win
if __name__=="__main__":
for w in rolling_window(xrange(6), 3):
print w
"""Example output:
[0, 1, 2]
[1, 2, 3]
[2, 3, 4]
[3, 4, 5]
"""
sum()
Lubmax()
), warto mieć na uwadze, że istnieją wydajne algorytmy do obliczania nowej wartości dla każdego okna w stałym czasie (niezależnie od rozmiaru okna). Zebrałem niektóre z tych algorytmów razem w bibliotece Pythona: Rolling .Odpowiedzi:
Jest jeden w starej wersji dokumentacji Pythona z
itertools
przykładami :Ten z dokumentacji jest trochę bardziej zwięzły i wykorzystuje go
itertools
do większego efektu, jaki sobie wyobrażam.źródło
for elem in it
pętlę?Wydaje się, że jest to dostosowane do potrzeb,
collections.deque
ponieważ zasadniczo masz FIFO (dodaj na jednym końcu, usuń z drugiego). Jednak nawet jeśli używasz alist
, nie powinieneś kroić dwa razy; zamiast tego prawdopodobnie powinieneś po prostupop(0)
z listy iappend()
nowej pozycji.Oto zoptymalizowana implementacja oparta na deque wzorowana na oryginale:
W moich testach z łatwością pokonuje wszystko inne zamieszczone tutaj przez większość czasu, chociaż
tee
wersja pillmunchera pokonuje go w przypadku dużych iteracji i małych okien. W większych oknachdeque
znowu rusza do przodu z surową prędkością.Dostęp do poszczególnych elementów
deque
może być szybszy lub wolniejszy niż w przypadku list lub krotek. (Elementy blisko początku są szybsze lub elementy zbliżone do końca, jeśli używasz indeksu ujemnego). Umieściłemsum(w)
w treści mojej pętli; to działa na siłę deque (iteracja od jednego przedmiotu do drugiego jest szybka, więc ta pętla działała o pełne 20% szybciej niż następna najszybsza metoda, pillmuncher). Kiedy zmieniłem to, aby indywidualnie wyszukiwać i dodawać elementy w oknie dziesięciu, tabele się odwróciły itee
metoda była o 20% szybsza. Byłem w stanie odzyskać trochę szybkości, używając ujemnych indeksów dla ostatnich pięciu terminów w dodatku, aletee
nadal byłem trochę szybszy. Ogólnie oceniam, że każdy z nich jest wystarczająco szybki dla większości zastosowań, a jeśli potrzebujesz trochę większej wydajności, profil i wybierz ten, który działa najlepiej.źródło
yield win
powinien byćyield tuple(win)
lubyield list(win)
aby zapobiec zwracaniu iteratora odwołań do tego samegodeque
obiektu.pip install sliding_window
i uruchom zfrom sliding_window import window
.list(window(range(10)))
powinieneś wyprodukować coś takiego jak [[0,1], [1,2], [2,3], ...]list(list(x) for x in window(range(10)))
albo dodać to do iteratora. Dla niektórych aplikacji będzie to miało znaczenie, dla innych nie, a ponieważ chciałem zwiększyć szybkość, zdecydowałem się tego nie robić i nałożyłem na rozmówcę obowiązek skopiowania okna w razie potrzeby.tuple()
przed uzyskaniem plonów, ta metoda nie ma żadnej przewagi nad innymi.Lubię
tee()
:daje:
źródło
timeit
testów wynika, że jest to znacznie wolniejsze niż Daniel DePaolo (w stosunku około 2: 1) i nie jest o wiele „przyjemniejsze”.size
. Jeśli ją zwiększysz (np. Jeśli iteracja ma 100000 elementów, zmień rozmiar okna na 1000), możesz zauważyć wzrost.iters
wynosi O (rozmiar!), A wywoływanienext()
wielu razy (inizip()
) jest prawdopodobnie dużo bardziej czasochłonne niż dwukrotne kopiowanie krotki. Używałem Pythona 2.6.5, BTW.iters
to O (rozmiar ^ 2), prawda?Oto uogólnienie, które dodaje obsługę
step
,fillvalue
parametrami:Daje w kawałkach
size
elementy na raz, przesuwającstep
pozycje na iterację, wypełniając każdy fragment,fillvalue
jeśli to konieczne. Przykład dlasize=4, step=3, fillvalue='*'
:Na przykład przypadku użycia dla
step
zapoznać parametru, zobacz wydajne przetwarzanie dużego pliku .txt w języku Python .źródło
Istnieje biblioteka, która robi dokładnie to, czego potrzebujesz:
źródło
step=3
powinny zostać usunięte, abylist(more_itertools.windowed(range(6), 3))
Tylko szybki wkład.
Ponieważ obecne dokumenty Pythona nie mają „okna” w przykładach narzędzi itertool (tj. Na dole strony http://docs.python.org/library/itertools.html ), oto fragment oparty na kodzie grupy, który to jeden z podanych przykładów:
Zasadniczo tworzymy serię podzielonych na plasterki iteratorów, z których każdy ma punkt początkowy o jedno miejsce dalej. Następnie łączymy je razem. Uwaga, ta funkcja zwraca generator (sam w sobie nie jest bezpośrednio generatorem).
Podobnie jak w powyższych wersjach elementu dołączającego i iteratora zaawansowanego, wydajność (tj. Która jest najlepsza) różni się w zależności od rozmiaru listy i rozmiaru okna. Podoba mi się ten, ponieważ jest dwuwierszowy (mógłby to być jeden wiersz, ale wolę koncepcje nazewnictwa).
Okazuje się, że powyższy kod jest błędny . Działa, jeśli parametr przekazany do iterowalnego jest sekwencją, ale nie, jeśli jest iteratorem. Jeśli jest to iterator, ten sam iterator jest współdzielony (ale nie trójnikowy) między wywołaniami islice, co źle wszystko psuje.
Oto trochę poprawionego kodu:
Jeszcze jedna wersja książek. Zamiast kopiować iterator, a następnie wielokrotnie przesuwać kopie do przodu, ta wersja tworzy kopie parami każdego iteratora, gdy przesuwamy pozycję początkową do przodu. Zatem iterator t zapewnia zarówno „kompletny” iterator z punktem początkowym w t, jak i podstawę do utworzenia iteratora t + 1:
źródło
Aby pokazać, jak można łączyć
itertools
przepisy , rozszerzampairwise
przepis tak bezpośrednio, jak to możliwe, z powrotem dowindow
przepisu, korzystając zconsume
przepisu:window
Recepta jest taka sama, jak w przypadkupairwise
, to po prostu zastępuje pojedynczy element „konsumować” na drugimtee
-ED iterator ze stopniowo zwiększając zużywa nan - 1
iteratory. Używanieconsume
zamiast zawijania każdego iteratoraislice
jest marginalnie szybsze (dla wystarczająco dużych iterable), ponieważ płacisz zaislice
zawijanie tylko podczasconsume
fazy, a nie podczas procesu wyodrębniania każdej wartości w oknie (więc jest ograniczonan
, a nie liczbą elementów witerable
).Pod względem wydajności, w porównaniu z niektórymi innymi rozwiązaniami, jest to całkiem niezłe (i lepsze niż jakiekolwiek inne rozwiązanie, które testowałem w miarę skalowania). Testowane w Pythonie 3.5.0, Linux x86-64, przy użyciu
ipython
%timeit
magii.kindall znajduje się
deque
rozwiązanie , grunt pod wydajności / brytyjski stosującislice
zamiast generatora ekspresji domowego walcowane i testowanie powstałych długość tak, że nie daje wyniki, gdy iterowalny jest krótszy niż okno, jak przepuszczaniemaxlen
zdeque
pozycyjnie zamiast według słów kluczowych (robi zaskakującą różnicę w przypadku mniejszych danych wejściowych):Tak samo jak w poprzednim dostosowanym rozwiązaniu kindall, ale po każdej
yield win
zmianie nayield tuple(win)
tak zapisywanie wyników z generatora działa bez wszystkich zapisanych wyników, które są w rzeczywistości widokiem najnowszego wyniku (wszystkie inne rozsądne rozwiązania są bezpieczne w tym scenariuszu) i dodajątuple=tuple
do definicji funkcji przenieść wykorzystaniatuple
zB
sięLEGB
doL
:consume
rozwiązanie bazowe pokazane powyżej:To samo co
consume
, ale z wbudowanymelse
przypadkiem,consume
aby uniknąć wywoływania funkcji in is None
testowania w celu skrócenia czasu działania, szczególnie w przypadku małych danych wejściowych, w których narzut konfiguracji jest znaczącą częścią pracy:(Na marginesie: wariant
pairwise
tego używatee
z domyślnym argumentem 2 wielokrotnie do tworzeniatee
obiektów zagnieżdżonych , więc każdy podany iterator jest przesuwany tylko raz, a nie niezależnie konsumowany rosnącą liczbę razy, podobnie jak odpowiedź MrDrFennera jest podobna do odpowiedzi niewymienionejconsume
i wolniej niż inlineconsume
we wszystkich testach, więc pominąłem te wyniki dla zwięzłości).Jak widać, jeśli nie przejmujesz się możliwością przechowywania przez dzwoniącego wyników, moja zoptymalizowana wersja rozwiązania kindall wygrywa przez większość czasu, z wyjątkiem „dużego iterowalnego, małego rozmiaru okna” (gdzie inline
consume
wygrywa ); degraduje się szybko wraz ze wzrostem iterowalnego rozmiaru, ale w ogóle nie ulega degradacji wraz ze wzrostem rozmiaru okna (każde inne rozwiązanie degraduje się wolniej przy zwiększaniu iterowalnego rozmiaru, ale także degraduje się przy wzroście rozmiaru okna). Można go nawet dostosować do przypadku „potrzeby krotek” przez zawijaniemap(tuple, ...)
, które działa nieco wolniej niż umieszczenie krotki w funkcji, ale jest trywialne (trwa 1-5% dłużej) i pozwala zachować elastyczność działania szybciej kiedy możesz tolerować wielokrotne zwracanie tej samej wartości.Jeśli potrzebujesz bezpieczeństwa przed przechowywaniem zwrotów, inline
consume
wygrywa dla wszystkich oprócz najmniejszych rozmiarów wejściowych (gdzie nie-inlineconsume
jest nieco wolniejszy, ale skaluje się podobnie). Plikdeque
& Tupling wygrywa rozwiązanie oparte tylko dla najmniejszych nakładów, z powodu mniejszych kosztów instalacyjnych, a zysk jest niewielki; degraduje się źle, gdy iteracja staje się dłuższa.Dla przypomnienia, dostosowanej wersji rozwiązania kindall że
yield
Stuple
S I użyto:Porzuć buforowanie
tuple
w linii definicji funkcji i użyjtuple
w każdymyield
z nich, aby uzyskać szybszą, ale mniej bezpieczną wersję.źródło
consume
ma zastosowanie ogólne (w tym możliwość wykonania pełnegoconsume
) i dlatego wymaga dodatkowego importu i testu na użycie dlan is None
. W prawdziwym kodzie, jeśli i tylko gdybym określił, że wydajność jest problemem lub naprawdę potrzebowałbym bardziej zwięzłego kodu, rozważałbym wstawienieelse
przypadkuconsume
wwindow
, zakładając, że nie używałem goconsume
do niczego innego. Ale jeśli nie wykazano, że wydajność stanowi problem, zachowałbym osobne definicje; wymienionaconsume
funkcja sprawia, że operacja jest mniej magiczna / samodokumentująca.Używam poniższego kodu jako prostego przesuwanego okna, które wykorzystuje generatory, aby drastycznie zwiększyć czytelność. Z mojego doświadczenia wynika, że do tej pory jego szybkość była wystarczająca do wykorzystania w bioinformatycznej analizie sekwencji.
Umieszczam to tutaj, ponieważ nie widziałem jeszcze tej metody. Ponownie, nie twierdzę, że jest porównywalny z wydajnością.
źródło
len(sequence)
wezwanie. To nie zadziała, jeślisequence
jest iteratorem lub generatorem. Gdy dane wejściowe mieszczą się w pamięci, zapewnia to bardziej czytelne rozwiązanie niż w przypadku iteratorów.źródło
range(len
jest to zły wzorzec w Pythonie?nieco zmodyfikowana wersja okna deque, aby było to prawdziwe okno toczące się. Tak więc zaczyna być wypełniany tylko jednym elementem, a następnie rośnie do maksymalnego rozmiaru okna, a następnie kurczy się, gdy jego lewa krawędź zbliża się do końca:
to daje
źródło
Wykonano to dla funkcji średniej kroczącej
źródło
Dlaczego nie
Jest to udokumentowane w Pythonie doc . Możesz go łatwo rozszerzyć na szersze okno.
źródło
Wiele iteratorów!
next(it)
podnosi się,StopIteration
gdy sekwencja jest zakończona iz jakiegoś fajnego powodu, który jest poza mną, instrukcja yield tutaj wyłącza ją i funkcja zwraca, ignorując pozostałe wartości, które nie tworzą pełnego okna.W każdym razie, jest to jednak rozwiązanie najmniejszych linie którego jedynym wymaganiem jest to, że
seq
wdrożenie albo__iter__
albo__getitem__
i nie opiera się naitertools
lubcollections
poza użytkownika @ dansalmo rozwiązanie :)źródło
Zróbmy to leniwie!
źródło
„”
źródło
Przetestowałem kilka rozwiązań i jeden wymyśliłem i stwierdziłem, że ten, który wymyśliłem, jest najszybszy, więc pomyślałem, że się nim podzielę.
źródło
źródło
Co powiesz na użycie następujących:
Wynik:
źródło
To stare pytanie, ale dla tych, którzy nadal są zainteresowani, jest świetna implementacja suwaka okna przy użyciu generatorów na tej stronie (autor: Adrian Rosebrock).
Jest to implementacja dla OpenCV, jednak można ją łatwo wykorzystać do innych celów. Dla chętnych wkleję kod tutaj, ale aby lepiej go zrozumieć, polecam odwiedzenie oryginalnej strony.
Wskazówka: możesz sprawdzić
.shape
okno podczas iteracji generatora, aby odrzucić te, które nie spełniają Twoich wymagańTwoje zdrowie
źródło
Zmodyfikowano odpowiedź DiPaolo, aby umożliwić dowolne wypełnienie i zmienną wielkość kroku
źródło
tutaj jest jedna wkładka. Zmierzyłem czas i jest to porównywalne z wydajnością górnej odpowiedzi i staje się stopniowo lepsze przy większej sekwencji od 20% wolniej z len (seq) = 20 i 7% wolniej z len (seq) = 10000
źródło
Próbuję mojej części, prosty, jeden linijkowy, pythonowy sposób przy użyciu islice. Ale może nie być optymalnie wydajne.
Objaśnienie: Utwórz okno za pomocą islice o rozmiarze window_size i wykonaj iterację tej operacji, używając map over all array.
źródło
Zoptymalizowana funkcja dla danych przesuwanego okna w uczeniu głębokim
źródło