2010-10-21 13 views
14

szukam funkcji, które odbywają się iterowalny i i wielkość n a rentowności krotki długości n które są wartości sekwencyjne z i:generatory Python że grupy inny iterowalny na grupy N

x = [1,2,3,4,5,6,7,8,9,0] 
[z for z in TheFunc(x,3)] 

daje

[(1,2,3),(4,5,6),(7,8,9),(0)] 

Czy taka funkcja istnieje w bibliotece standardowej?

Jeśli istnieje jako część standardowej biblioteki, nie mogę go znaleźć i brakuje mi terminów do wyszukania. Mógłbym napisać własną, ale wolałbym nie.

Odpowiedz

17

Zobacz grouper przepis w docs for the itertools package

def grouper(n, iterable, fillvalue=None): 
    "grouper(3, 'ABCDEFG', 'x') --> ABC DEF Gxx" 
    args = [iter(iterable)] * n 
    return izip_longest(fillvalue=fillvalue, *args) 

(to jednak jest duplikatem quite a few questions.)

+2

Gdybym wiedział, że szukam "groupera", nie musiałbym w ogóle pytać. Ale nie znałem tego terminu. – BCS

+0

+1. Zupełnie zapomniałem o poręcznych przepisach w dokumentach. – Skurmedel

+2

Skończyło się na tym, ale musiałem hack w filtrowanie wartości wypłat na późniejszym etapie. – BCS

4

Jak o tym? Nie ma jednak wartości wypełnienia.

>>> def partition(itr, n): 
...  i = iter(itr) 
...  res = None 
...  while True: 
...    res = list(itertools.islice(i, 0, n)) 
...    if res == []: 
...      break 
...    yield res 
... 
>>> list(partition([1, 2, 3, 4, 5, 6, 7, 8, 9], 3)) 
[[1, 2, 3], [4, 5, 6], [7, 8, 9]] 
>>> 

Wykorzystuje kopię oryginału iteracji, którą wyczerpuje dla każdego kolejnego splotu. Jedyny inny sposób, w jaki mój zmęczony mózg mógłby wymyślić, generował spojenie punktów końcowych z zasięgiem.

Może powinienem zmienić list() na tuple(), aby lepiej odpowiadało twojemu wynikowi.

+0

LOL. Chyba sobie żartujesz. W odpowiedzi jest błąd, a moja edycja została odrzucona? Mój szacunek dla społeczności SO po prostu znacznie się zmniejszył. –

+2

btw, itertools.islice (i, 0, 3) -> itertools.islice (i, 0, n) Nadal nie mogę uwierzyć społeczności SO. –

+0

Nie odrzuciłem go, zrobił to ktoś inny. Ale masz rację. Ten 3 jest zakodowany negatywnie dla celu n jako parametru. Jeśli chcesz, mogę go edytować, ale nie dostaniesz żadnego repa, do ciebie:) – Skurmedel

0
def grouper(iterable, n): 
     while True: 
      yield itertools.chain(iterable.next(),itertools.islice(iterable, n-1)) 
+2

Twoja odpowiedź byłaby lepsza, gdybyś zawarł krótkie wyjaśnienie, aby przejść do tego kodu. – trooper

16

Gdy chcesz grupy iterator w kawałki nbez wyściółki ostateczna grupa o wartości wypełnienia, użyj iter(lambda: list(IT.islice(iterable, n)), []):

import itertools as IT 

def grouper(n, iterable): 
    """ 
    >>> list(grouper(3, 'ABCDEFG')) 
    [['A', 'B', 'C'], ['D', 'E', 'F'], ['G']] 
    """ 
    iterable = iter(iterable) 
    return iter(lambda: list(IT.islice(iterable, n)), []) 

seq = [1,2,3,4,5,6,7] 
print(list(grouper(3, seq))) 

plony

[[1, 2, 3], [4, 5, 6], [7]] 

Istnieje wyjaśnienie, jak to działa w drugiej połowie this answer.


Gdy chcesz grupy iterator w kawałki ni pad końcowe grupy o wartości wypełnienia, użyj grouper recipezip_longest(*[iterator]*n):

Na przykład w python2:

>>> list(IT.izip_longest(*[iter(seq)]*3, fillvalue='x')) 
[(1, 2, 3), (4, 5, 6), (7, 'x', 'x')] 

W języku Python3 nazwa izip_longest została zmieniona na zip_longest:

>>> list(IT.zip_longest(*[iter(seq)]*3, fillvalue='x')) 
[(1, 2, 3), (4, 5, 6), (7, 'x', 'x')] 

Gdy chcesz zgrupować sekwencja w kawałki n można użyć chunks przepis:

def chunks(seq, n): 
    # https://stackoverflow.com/a/312464/190597 (Ned Batchelder) 
    """ Yield successive n-sized chunks from seq.""" 
    for i in xrange(0, len(seq), n): 
     yield seq[i:i + n] 

zauważyć, że w przeciwieństwie do iteratorów w ogóle, sequences by definition mają długość (tj __len__ jest zdefiniowany).

0

Wiem, że odpowiedź została udzielona kilka razy, ale dodaję moje rozwiązanie, które powinno poprawić zarówno ogólną przydatność do sekwencji i iteratorów, czytelność (brak warunku wyjścia niewidocznej pętli przez wyjątek StopIteration) i wydajność w porównaniu z grouperem Przepis. Jest najbardziej podobny do ostatniej odpowiedzi Sveina.

def chunkify(iterable, n): 
    iterable = iter(iterable) 
    n_rest = n - 1 

    for item in iterable: 
     rest = itertools.islice(iterable, n_rest) 
     yield itertools.chain((item,), rest) 
0

Oto inne rozwiązanie, które sprawia, że ​​nie wykorzystuje itertools i, mimo że ma jeszcze kilka linii, to podobno przewyższa podanych odpowiedzi, gdy kawałki są dużo krótsze niż iterowalny długości. Jednak w przypadku dużych porcji inne odpowiedzi są znacznie szybsze.

def batchiter(iterable, batch_size): 
    """ 
    >>> list(batchiter('ABCDEFG', 3)) 
    [['A', 'B', 'C'], ['D', 'E', 'F'], ['G']] 
    """ 
    next_batch = [] 
    for element in iterable: 
     next_batch.append(element) 
     if len(next_batch) == batch_size: 
      batch, next_batch = next_batch, [] 
      yield batch 
    if next_batch: 
     yield next_batch 


In [19]: %timeit [b for b in batchiter(range(1000), 3)] 
1000 loops, best of 3: 644 µs per loop 

In [20]: %timeit [b for b in grouper(3, range(1000))] 
1000 loops, best of 3: 897 µs per loop 

In [21]: %timeit [b for b in partition(range(1000), 3)] 
1000 loops, best of 3: 890 µs per loop 

In [22]: %timeit [b for b in batchiter(range(1000), 333)] 
1000 loops, best of 3: 540 µs per loop 

In [23]: %timeit [b for b in grouper(333, range(1000))] 
10000 loops, best of 3: 81.7 µs per loop 

In [24]: %timeit [b for b in partition(range(1000), 333)] 
10000 loops, best of 3: 80.1 µs per loop 
1

Jest to bardzo częste żądanie w języku Python. Wystarczająco pospolity, że trafił do zunifikowanego pakietu narzędziowego. Po pierwsze, there are extensive docs here. Ponadto, the module został zaprojektowany i przetestowany tak, aby opierał się tylko na standardowej bibliotece (kompatybilny z Python 2 i 3), co oznacza, że ​​możesz just download the file directly into your project.

# if you downloaded/embedded, try: 
# from iterutils import chunked 

# with `pip install boltons` use: 

from boltons.iterutils import chunked 

print(chunked(range(10), 3)) 
# [[0, 1, 2], [3, 4, 5], [6, 7, 8], [9]] 

Jest formą iterator/generator o nieokreślonym/długie sekwencje, a także:

print(list(chunked_iter(range(10), 3, fill=None))) 
# [[0, 1, 2], [3, 4, 5], [6, 7, 8], [9, None, None]] 

Jak widać, można również wypełnić sekwencję o wartości wybranego przez użytkownika, jak również. Wreszcie, jako opiekun, mogę zapewnić, że podczas gdy kod został pobrany/przetestowany przez tysiące programistów, jeśli napotkasz jakiekolwiek problemy, uzyskasz najszybsze wsparcie możliwe przez boltons GitHub Issues page. Mam nadzieję, że to (i/lub którykolwiek z pozostałych ponad 150 receptur boltonów) pomogło!

1

To jest bardzo stary quesiton, ale myślę, że warto wspomnieć następujące podejście do ogólnego przypadku. Jego główną zaletą jest to, że wystarczy tylko powtórzyć dane, więc będzie działać z kursorami bazy danych lub innymi sekwencjami, które mogą być użyte tylko raz. Uważam też, że jest bardziej czytelny.

def chunks(n, iterator): 
    out = [] 
    for elem in iterator: 
     out.append(elem) 
     if len(out) == n: 
      yield out 
      out = [] 
    yield out