Vraag Hoe splitst u een lijst in brokken van dezelfde grootte?


Ik heb een lijst met willekeurige lengte en ik moet het opsplitsen in gelijke delen en ermee werken. Er zijn enkele voor de hand liggende manieren om dit te doen, zoals het bijhouden van een teller en twee lijsten, en wanneer de tweede lijst vol is, voeg je deze toe aan de eerste lijst en leeg je de tweede lijst voor de volgende gegevensronde, maar dit is potentieel extreem duur.

Ik vroeg me af of iemand hier een goede oplossing voor had voor lijsten van elke lengte, bijvoorbeeld met behulp van generatoren.

Ik was op zoek naar iets nuttigs in itertools maar ik kon niets vinden dat duidelijk nuttig was. Misschien heb je het gemist.

Gerelateerde vraag: Wat is de meest "pythonic" manier om een ​​lijst in chunks te herhalen?


1578
2017-11-23 12:15


oorsprong


antwoorden:


Hier is een generator die de brokken levert die u wilt:

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]]

Als u Python 2 gebruikt, moet u dit gebruiken xrange() in plaats van 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]

U kunt ook eenvoudig lijstbegrip gebruiken in plaats van een functie te schrijven. Python 3:

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

Python 2-versie:

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

2113
2017-11-23 12:33



Als je iets supereenvoudigs wilt:

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



Rechtstreeks uit de (oude) documentatie van Python (recepten voor 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)

De huidige versie, zoals gesuggereerd door J.F.Sebastian:

#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)

Ik vermoed dat Guido's tijdmachine werkt - werkte - zal werken - zal gewerkt hebben - weer aan het werken was.

Deze oplossingen werken omdat [iter(iterable)]*n (of het equivalent in de eerdere versie) creëert een iterator, herhaald n tijden in de lijst. izip_longest voert dan effectief een round-robin uit van "elke" iterator; omdat dit dezelfde iterator is, wordt deze geavanceerd door elke dergelijke aanroep, wat resulteert in elk van deze zip-roundrobin genereert een tupel van n items.


251
2017-11-23 15:48



Ik weet dat dit een beetje oud is, maar ik begrijp niet waarom niemand het noemde 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 is een generator die werkt op willekeurige iterables:

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

Voorbeeld:

>>> 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



Ik ben verrast dat niemand erover heeft nagedacht iter's twee-argument vorm:

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)]

Dit werkt met elke iterable en produceert lui output. Het retourneert tuples in plaats van iterators, maar ik denk dat het toch een zekere elegantie heeft. Het heeft ook geen pad; als je opvulling wilt, volstaat een simpele variatie op het bovenstaande:

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')]

Zoals de izip_longestop basis van oplossingen, het bovenstaande altijd pads. Voor zover ik weet, is er geen één- of tweeregelig itertools-recept voor een functie die optioneel pads. Door de bovenstaande twee benaderingen te combineren, komt deze vrij goed in de buurt:

_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')]

Ik geloof dat dit de kortste chunker is die wordt voorgesteld en die optionele vulling biedt.


65
2018-02-26 15:02



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

48
2018-06-26 19:10



Eenvoudig maar elegant

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

of als je liever:

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



Ik zag het meest geweldige antwoord op Python in a duplicaat van deze vraag:

from itertools import zip_longest

a = range(1, 16)
i = iter(a)
r = list(zip_longest(i, i, i))
>>> print(r)
[(1, 2, 3), (4, 5, 6), (7, 8, 9), (10, 11, 12), (13, 14, 15)]

U kunt n-tuple maken voor elke n. Als a = range(1, 15), dan is het resultaat:

[(1, 2, 3), (4, 5, 6), (7, 8, 9), (10, 11, 12), (13, 14, None)]

Als de lijst gelijk verdeeld is, kunt u deze vervangen zip_longest met zip, anders de triplet (13, 14, None) zou verloren zijn. Python 3 wordt hierboven gebruikt. Gebruik voor Python 2 izip_longest.


28
2018-03-12 12:36