Mam listę o dowolnej długości i muszę ją podzielić na kawałki o równej wielkości i operować na niej. Istnieje kilka oczywistych sposobów, jak to zrobić, takich jak prowadzenie licznika i dwóch list, a gdy druga lista się zapełni, dodaj ją do pierwszej listy i opróżnij drugą listę do następnej rundy danych, ale jest to potencjalnie niezwykle kosztowne.
Zastanawiałem się, czy ktoś ma dobre rozwiązanie tego problemu dla list dowolnej długości, np. Używając generatorów.
Szukałem czegoś przydatnego, itertools
ale nie mogłem znaleźć niczego oczywiście przydatnego. Może jednak tego nie zauważyłem.
Powiązane pytanie: Jaki jest najbardziej „pytonowy” sposób na iterację listy w częściach?
Odpowiedzi:
Oto generator, który daje pożądane fragmenty:
Jeśli używasz Python 2, powinieneś użyć
xrange()
zamiastrange()
:Możesz także po prostu użyć funkcji listy zamiast pisać funkcję, chociaż dobrze jest zawrzeć takie operacje w nazwanych funkcjach, aby kod był łatwiejszy do zrozumienia. Python 3:
Wersja Python 2:
źródło
Jeśli chcesz czegoś bardzo prostego:
Użyj
xrange()
zamiastrange()
w przypadku Python 2.xźródło
max()
.Bezpośrednio z (starej) dokumentacji Pythona (przepisy na itertools):
Obecna wersja, zgodnie z sugestią JFSebastian:
Wydaje mi się, że maszyna czasu Guido działa - działała - będzie działać - będzie działać - działała ponownie.
Te rozwiązania działają, ponieważ
[iter(iterable)]*n
(lub odpowiednik we wcześniejszej wersji) tworzy jeden iterator, powtarzanyn
raz na liście.izip_longest
następnie efektywnie wykonuje okrężny „iterator”; ponieważ jest to ten sam iterator, jest on przyspieszany przy każdym takim wywołaniu, w wyniku czego każde takie okrążenie zip generuje jedną krotkęn
przedmiotów.źródło
list(grouper(3, range(10)))
zwraca[(0, 1, 2), (3, 4, 5), (6, 7, 8), (9, None, None)]
, a wszystkie krotki mają długość 3. Proszę rozwinąć swój komentarz, ponieważ nie rozumiem tego; jak nazywasz rzecz i jak definiujesz, że jest to wielokrotność 3 w „spodziewaniu się, że twoja rzecz będzie wielokrotnością 3”? Z góry dziękuję.itertools
podejścia funkcjonalnego, które okazuje się nieczytelnym szlamem, w porównaniu do prostej i naiwnej implementacji czystego pytonal==[1, 2, 3]
wtedyf(*l)
jest równoważnef(1, 2, 3)
. Zobacz to pytanie i oficjalną dokumentację .Wiem, że to trochę stare, ale nikt jeszcze nie wspomniał
numpy.array_split
:źródło
Jestem zaskoczony, nikt nie pomyślał o użyciu
iter
„s forma dwóch argumentów :Próbny:
Działa to z każdym iterowalnym i produkuje leniwie. Zwraca krotki zamiast iteratorów, ale myślę, że ma jednak pewną elegancję. To również nie pad; jeśli chcesz uzupełnienia, wystarczy prosta odmiana powyższego:
Próbny:
Podobnie jak
izip_longest
rozwiązania oparte na powyższym zawsze się nakładają. O ile mi wiadomo, nie ma jedno- lub dwuwierszowego przepisu na itertools dla funkcji, która opcjonalnie działa na pad. Łącząc powyższe dwa podejścia, ten jest bardzo zbliżony:Próbny:
Uważam, że jest to najkrótszy zaproponowany fragment, który oferuje opcjonalne wypełnienie.
Jak zauważył Tomasz Gandor , dwa fragmenty paddingu niespodziewanie zatrzymają się, jeśli napotkają długą sekwencję wartości padu. Oto ostatnia odmiana, która w rozsądny sposób rozwiązuje ten problem:
Próbny:
źródło
islice(it, size)
wyrażenie i osadzili je (tak jak ja to zrobiłem) w konstrukcji pętli. Tylko ty pomyślałeś o dwuargumentowej wersjiiter()
(byłem całkowicie nieświadomy), co czyni ją super-elegancką (i prawdopodobnie najbardziej efektywną pod względem wydajności). Nie miałem pojęcia, że pierwszy argument doiter
zmiany na funkcję 0-argumentową po otrzymaniu wartownika. Zwracamy iterator porcji (pot. Nieskończony), można użyć iteratora (pot. Nieskończony) jako danych wejściowych, nie malen()
żadnych wycinków tablicy i nie ma ich. Niesamowite!it
iteratorem. Po drugie i najważniejsze) - skończysz przedwcześnie, jeśli częśćpadval
faktycznie istnieje w twojej iteracji i powinna zostać przetworzonaizip_longest
podejściem - podejrzewam, że może to być złożony kompromis. Ale ... czypadval
problem nie jest wspólny dla każdej odpowiedzi tutaj, która oferujepadval
parametr?()
jako wartownik, czyni pracę prawidłowo Dzieje się tak dlatego,tuple(islice(it, size))
plony()
, gdyit
jest pusta.)Oto generator, który działa na dowolnych iteracjach:
Przykład:
źródło
źródło
map(None, iter)
równaizip_longest(iter)
.*
przed sobą krotkę iteratora? Być może w tekście odpowiedzi, ale zauważyłem,*
że wcześniej tak było w Pythonie. Dzięki!Prosty, ale elegancki
lub jeśli wolisz:
źródło
1
il
są nie do odróżnienia. Tak jak0
iO
. A czasem nawetI
i1
.print [l[x:x+10] for x in xrange(1, len(l), 10)]
range
.Krytyka innych odpowiedzi tutaj:
Żadna z tych odpowiedzi nie jest kawałkami o równej wielkości, wszystkie pozostawiają fragment runtu na końcu, więc nie są całkowicie zrównoważone. Jeśli użyjesz tych funkcji do rozłożenia pracy, masz wbudowaną perspektywę, że jedna z nich prawdopodobnie zakończy się znacznie wcześniej niż inne, więc siedziałaby bezczynnie, podczas gdy inne nadal ciężko pracowały.
Na przykład bieżąca górna odpowiedź kończy się na:
Po prostu nienawidzę tego wyścigu!
Inni, jak
list(grouper(3, xrange(7)))
, ichunk(xrange(7), 3)
zarówno zwrot:[(0, 1, 2), (3, 4, 5), (6, None, None)]
. SąNone
to tylko wypełnienia i moim zdaniem raczej nieeleganckie. NIE są równomiernie dzielące iteracyjne.Dlaczego nie możemy ich lepiej podzielić?
Moje rozwiązanie
Oto wyważone rozwiązanie, dostosowane z funkcji Użyłem w produkcji (Uwaga: W Pythonie 3 do zastąpienia
xrange
zrange
):I stworzyłem generator, który robi to samo, jeśli umieścisz go na liście:
I wreszcie, ponieważ widzę, że wszystkie powyższe funkcje zwracają elementy w ciągłej kolejności (tak jak je podano):
Wynik
Aby je przetestować:
Który drukuje:
Zauważ, że ciągły generator zapewnia fragmenty o takich samych wzorcach długości jak pozostałe dwa, ale wszystkie elementy są w porządku i są one równomiernie podzielone, tak jak można podzielić listę odrębnych elementów.
źródło
list(grouper(3, xrange(7)))
oraz drugi,chunk(xrange(7), 3)
zwracają:[(0, 1, 2), (3, 4, 5), (6, None, None)]
. SąNone
to tylko wypełnienia i moim zdaniem raczej nieeleganckie. NIE są równomiernie dzielące iteracyjne. Dziękuję za Twój głos!import pandas as pd; [pd.DataFrame(np.arange(7))[i::3] for i in xrange(3)]
Widziałem najbardziej niesamowitą odpowiedź w języku Python w duplikacie tego pytania:
Możesz utworzyć n-krotkę dla dowolnego n. Jeśli
a = range(1, 15)
, to wynik będzie:Jeśli lista jest podzielona równo, to można zastąpić
zip_longest
wzip
przeciwnym razie trójka(13, 14, None)
zostaną utracone. Python 3 jest używany powyżej. W przypadku Python 2 użyjizip_longest
.źródło
zip(i, i, i, ... i)
z argumentem „chunk_size” argumenty zip () można zapisać jako:zip(*[i]*chunk_size)
Oczywiście, czy to dobry pomysł, czy nie.zip_longest
należy użyć, jak to zrobiono w: stackoverflow.com/a/434411/1959808range(1, 15)
już brakuje elementów, ponieważ jest 14 elementówrange(1, 15)
, a nie 15.Jeśli znasz rozmiar listy:
Jeśli nie (iterator):
W tym drugim przypadku można go przepiękniej sformułować, jeśli masz pewność, że sekwencja zawsze zawiera całą liczbę fragmentów o danym rozmiarze (tj. Nie ma niekompletnej ostatniej części).
źródło
Toolz biblioteka posiada
partition
funkcję dla tego:źródło
Jeśli na przykład masz porcję o wielkości 3, możesz:
źródło: http://code.activestate.com/recipes/303060-group-a-list-into-sequential-n-tuples/
Użyłbym tego, gdy mój rozmiar fragmentu ma ustaloną liczbę, którą mogę wpisać, np. „3”, i nigdy się nie zmieniam.
źródło
Bardzo podoba mi się wersja doc Pytona zaproponowana przez tzot i JFSebastian, ale ma dwie wady:
Często używam tego w moim kodzie:
AKTUALIZACJA: Leniwa wersja kawałków:
źródło
while True
pętli?StopIteration
Podniesiony, gdytuple
jest pusty iiterable.next()
zostanie wykonany. Nie działa jednak poprawnie we współczesnym Pythonie, gdzie należy wyjść z generatorareturn
, a nie podnosićStopIteration
.try/except StopIteration: return
Wokół całej pętli (i zmieniaiterable.next()
sięnext(iterable)
na cross-wersja COMPAT) rozwiązuje to przy minimalnym obciążeniu przynajmniej.Gdzie AA jest tablicą, SS jest rozmiarem porcji. Na przykład:
źródło
Byłem ciekawy wydajności różnych podejść i oto:
Testowane na Python 3.5.1
Wyniki:
źródło
time
biblioteki nie jest świetnym pomysłem, gdy mamytimeit
modułkod:
wynik:
źródło
Możesz także użyć
get_chunks
funkcjiutilspie
biblioteki jako:Możesz zainstalować
utilspie
przez pip:Oświadczenie: Jestem twórcą biblioteki utilspie .
źródło
W tym momencie myślę, że potrzebujemy generatora rekurencyjnego , na wypadek gdyby ...
W python 2:
W python 3:
Ponadto, w przypadku masowej inwazji obcych, przydatny generator rekurencyjny może się przydać:
źródło
Z wyrażeniach przypisania w Pythonie 3.8 staje się całkiem ładnie:
Działa to na dowolnej iterowalnej, nie tylko na liście.
źródło
heh, wersja jednoliniowa
źródło
def chunk
zamiastchunk=lambda
ma .__ name__ atrybut „chunk” zamiast „<lambda>”. Określona nazwa jest bardziej przydatna w trackbackach.<lamba>
czy nie, jest przynajmniej zauważalną różnicą.stosowanie:
źródło
Kolejna bardziej wyraźna wersja.
źródło
Bez wywoływania len (), co jest dobre dla dużych list:
Dotyczy to iteracji:
Funkcjonalny smak powyższego:
LUB:
LUB:
źródło
len()
dużych list; jest to operacja o stałym czasie.Oto lista dodatkowych podejść:
Dany
Kod
Biblioteka standardowa
more_itertools
+Bibliografia
zip_longest
( powiązany post , powiązany post )setdefault
(uporządkowane wyniki wymagają Python 3.6+)collections.defaultdict
(uporządkowane wyniki wymagają Python 3.6+)more_itertools.chunked
( powiązane posty )more_itertools.sliced
more_itertools.grouper
( powiązany post )more_itertools.windowed
(patrzstagger
,zip_offset
)+ Biblioteka innej firmy, która implementuje przepisy itertools i nie tylko.
> pip install more_itertools
źródło
Zobacz to odniesienie
Python3
źródło
zip(*[iter(range(7))]*3)
Zwraca tylko[(0, 1, 2), (3, 4, 5)]
i zapomina6
o danych wejściowych.Ponieważ wszyscy tutaj mówią o iteratorach.
boltons
ma do tego idealną metodę o nazwieiterutils.chunked_iter
.Wynik:
Ale jeśli nie chcesz litości dla pamięci, możesz użyć starej metody i przechowywać ją w całości
list
za pomocąiterutils.chunked
.źródło
Jeszcze jedno rozwiązanie
źródło
źródło
Rozważ użycie kawałków matplotlib.cbook
na przykład:
źródło