Frage Wie teilen Sie eine Liste in gleichgroße Stücke auf?


Ich habe eine Liste mit beliebiger Länge, und ich muss sie in gleich große Stücke aufteilen und bearbeiten. Es gibt einige offensichtliche Wege, dies zu tun, wie einen Zähler und zwei Listen zu behalten, und wenn die zweite Liste voll ist, füge sie zu der ersten Liste hinzu und leere die zweite Liste für die nächste Runde von Daten, aber dies ist möglicherweise extrem teuer.

Ich habe mich gefragt, ob jemand eine gute Lösung für Listen von beliebiger Länge, z.B. mit Generatoren.

Ich suchte nach etwas Nützlichem in itertools aber ich konnte nichts offensichtlich Nützliches finden. Vielleicht habe ich es aber verpasst.

Verwandte Frage: Was ist der "pythonischste" Weg, um eine Liste in Chunks zu durchlaufen?


1578
2017-11-23 12:15


Ursprung


Antworten:


Hier ist ein Generator, der die gewünschten Teile liefert:

def chunks(l, n):
    """Yield successive n-sized chunks from l."""
    for i in range(0, len(l), n):
        yield l[i:i + n]

import pprint
pprint.pprint(list(chunks(range(10, 75), 10)))
[[10, 11, 12, 13, 14, 15, 16, 17, 18, 19],
 [20, 21, 22, 23, 24, 25, 26, 27, 28, 29],
 [30, 31, 32, 33, 34, 35, 36, 37, 38, 39],
 [40, 41, 42, 43, 44, 45, 46, 47, 48, 49],
 [50, 51, 52, 53, 54, 55, 56, 57, 58, 59],
 [60, 61, 62, 63, 64, 65, 66, 67, 68, 69],
 [70, 71, 72, 73, 74]]

Wenn Sie Python 2 verwenden, sollten Sie verwenden xrange() Anstatt von range():

def chunks(l, n):
    """Yield successive n-sized chunks from l."""
    for i in xrange(0, len(l), n):
        yield l[i:i + n]

Sie können auch einfach Listenverständnis verwenden, anstatt eine Funktion zu schreiben. Python 3:

[l[i:i + n] for i in range(0, len(l), n)]

Python 2-Version:

[l[i:i + n] for i in xrange(0, len(l), n)]

2113
2017-11-23 12:33



Wenn Sie etwas sehr einfaches wollen:

def chunks(l, n):
    n = max(1, n)
    return (l[i:i+n] for i in xrange(0, len(l), n))

481
2017-11-17 20:17



Direkt aus der (alten) Python-Dokumentation (Rezepte für itertools):

from itertools import izip, chain, repeat

def grouper(n, iterable, padvalue=None):
    "grouper(3, 'abcdefg', 'x') --> ('a','b','c'), ('d','e','f'), ('g','x','x')"
    return izip(*[chain(iterable, repeat(padvalue, n-1))]*n)

Die aktuelle Version, wie von J.F.Sebastian vorgeschlagen:

#from itertools import izip_longest as zip_longest # for Python 2.x
from itertools import zip_longest # for Python 3.x
#from six.moves import zip_longest # for both (uses the six compat library)

def grouper(n, iterable, padvalue=None):
    "grouper(3, 'abcdefg', 'x') --> ('a','b','c'), ('d','e','f'), ('g','x','x')"
    return zip_longest(*[iter(iterable)]*n, fillvalue=padvalue)

Ich schätze, Guidos Zeitmaschine funktioniert - hat funktioniert - wird funktionieren - hat funktioniert - hat wieder funktioniert.

Diese Lösungen funktionieren, weil [iter(iterable)]*n (oder das Äquivalent in der früheren Version) erstellt ein Iterator, wiederholt n mal in der Liste. izip_longest führt dann effektiv ein Round-Robin von "jedem" Iterator durch; Da dies derselbe Iterator ist, wird er durch jeden solchen Aufruf weitergeschaltet, was dazu führt, dass jedes derartige Zip-Roundrobin ein Tupel von erzeugt n Artikel.


251
2017-11-23 15:48



Ich weiß, das ist irgendwie alt, aber ich weiß nicht warum niemand erwähnt hat numpy.array_split:

lst = range(50)
In [26]: np.array_split(lst,5)
Out[26]: 
[array([0, 1, 2, 3, 4, 5, 6, 7, 8, 9]),
 array([10, 11, 12, 13, 14, 15, 16, 17, 18, 19]),
 array([20, 21, 22, 23, 24, 25, 26, 27, 28, 29]),
 array([30, 31, 32, 33, 34, 35, 36, 37, 38, 39]),
 array([40, 41, 42, 43, 44, 45, 46, 47, 48, 49])]

94
2018-06-05 08:54



Hier ist ein Generator, der an beliebigen Iterablen arbeitet:

def split_seq(iterable, size):
    it = iter(iterable)
    item = list(itertools.islice(it, size))
    while item:
        yield item
        item = list(itertools.islice(it, size))

Beispiel:

>>> import pprint
>>> pprint.pprint(list(split_seq(xrange(75), 10)))
[[0, 1, 2, 3, 4, 5, 6, 7, 8, 9],
 [10, 11, 12, 13, 14, 15, 16, 17, 18, 19],
 [20, 21, 22, 23, 24, 25, 26, 27, 28, 29],
 [30, 31, 32, 33, 34, 35, 36, 37, 38, 39],
 [40, 41, 42, 43, 44, 45, 46, 47, 48, 49],
 [50, 51, 52, 53, 54, 55, 56, 57, 58, 59],
 [60, 61, 62, 63, 64, 65, 66, 67, 68, 69],
 [70, 71, 72, 73, 74]]

79
2017-11-23 12:41



Ich bin überrascht, dass niemand daran gedacht hat iterist es Zwei-Argument-Formular:

from itertools import islice

def chunk(it, size):
    it = iter(it)
    return iter(lambda: tuple(islice(it, size)), ())

Demo:

>>> list(chunk(range(14), 3))
[(0, 1, 2), (3, 4, 5), (6, 7, 8), (9, 10, 11), (12, 13)]

Dies funktioniert mit jedem iterablen und produziert träge. Es gibt eher Tupel als Iteratoren zurück, aber ich denke, es hat trotzdem eine gewisse Eleganz. Es paddet auch nicht; Wenn Sie ein Padding wünschen, genügt eine einfache Variation:

from itertools import islice, chain, repeat

def chunk_pad(it, size, padval=None):
    it = chain(iter(it), repeat(padval))
    return iter(lambda: tuple(islice(it, size)), (padval,) * size)

Demo:

>>> list(chunk_pad(range(14), 3))
[(0, 1, 2), (3, 4, 5), (6, 7, 8), (9, 10, 11), (12, 13, None)]
>>> list(chunk_pad(range(14), 3, 'a'))
[(0, 1, 2), (3, 4, 5), (6, 7, 8), (9, 10, 11), (12, 13, 'a')]

Wie izip_longest-basierte Lösungen, die oben genannten immer Pads. Soweit ich weiß, gibt es kein one- oder zweizeiliges itertools Rezept für eine Funktion, die optional Pads. Durch die Kombination der beiden oben genannten Ansätze kommt dieser Punkt recht nahe:

_no_padding = object()

def chunk(it, size, padval=_no_padding):
    if padval == _no_padding:
        it = iter(it)
        sentinel = ()
    else:
        it = chain(iter(it), repeat(padval))
        sentinel = (padval,) * size
    return iter(lambda: tuple(islice(it, size)), sentinel)

Demo:

>>> list(chunk(range(14), 3))
[(0, 1, 2), (3, 4, 5), (6, 7, 8), (9, 10, 11), (12, 13)]
>>> list(chunk(range(14), 3, None))
[(0, 1, 2), (3, 4, 5), (6, 7, 8), (9, 10, 11), (12, 13, None)]
>>> list(chunk(range(14), 3, 'a'))
[(0, 1, 2), (3, 4, 5), (6, 7, 8), (9, 10, 11), (12, 13, 'a')]

Ich glaube, dass dies der kürzeste vorgeschlagene Chunker ist, der optionale Polsterung anbietet.


65
2018-02-26 15:02



def chunk(input, size):
    return map(None, *([iter(input)] * size))

48
2018-06-26 19:10



Einfach und doch elegant

l = range(1, 1000)
print [l[x:x+10] for x in xrange(0, len(l), 10)]

oder wenn Sie bevorzugen:

chunks = lambda l, n: [l[x: x+n] for x in xrange(0, len(l), n)]
chunks(l, 10)

39
2017-07-12 07:58