Pertanyaan Bagaimana Anda membagi daftar menjadi potongan yang berukuran rata?


Saya memiliki daftar panjang sewenang-wenang, dan saya perlu membaginya menjadi potongan-potongan yang berukuran sama dan beroperasi di atasnya. Ada beberapa cara yang jelas untuk melakukan ini, seperti menyimpan counter dan dua daftar, dan ketika daftar kedua mengisi, tambahkan ke daftar pertama dan kosongkan daftar kedua untuk putaran data berikutnya, tetapi ini berpotensi sangat mahal.

Saya bertanya-tanya apakah ada yang punya solusi bagus untuk ini untuk daftar panjangnya, mis. menggunakan generator.

Saya mencari sesuatu yang bermanfaat itertools tetapi saya tidak dapat menemukan apa pun yang jelas berguna. Mungkin sudah ketinggalan, sih.

Pertanyaan terkait: Apa cara paling "tiruan" untuk mengulang daftar dalam potongan?


1578
2017-11-23 12:15


asal


Jawaban:


Inilah generator yang menghasilkan potongan yang Anda inginkan:

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

Jika Anda menggunakan Python 2, Anda harus menggunakannya xrange() dari pada 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]

Anda juga dapat menggunakan daftar pemahaman daripada menulis suatu fungsi. Python 3:

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

Versi Python 2:

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

2113
2017-11-23 12:33



Jika Anda menginginkan sesuatu yang super sederhana:

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



Langsung dari dokumentasi (Python lama) (resep untuk 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)

Versi saat ini, seperti yang disarankan oleh 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)

Kurasa mesin waktu Guido bekerja — bekerja — akan berhasil — akan berhasil — bekerja kembali.

Solusi ini berfungsi karena [iter(iterable)]*n (atau yang setara dengan versi sebelumnya) buat satu iterator, diulangi n kali dalam daftar. izip_longest kemudian secara efektif melakukan round-robin "masing-masing" iterator; karena ini adalah iterator yang sama, ia dimajukan oleh setiap panggilan tersebut, menghasilkan masing-masing zip-roundrobin menghasilkan satu tupel dari n item.


251
2017-11-23 15:48



Saya tahu ini agak tua tapi saya tidak mengapa tidak ada yang disebutkan 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



Berikut adalah generator yang bekerja pada iterables sewenang-wenang:

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

Contoh:

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



Saya terkejut tak seorang pun berpikir untuk menggunakannya iter's bentuk dua argumen:

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

Ini bekerja dengan setiap iterable dan menghasilkan output dengan malas. Ini mengembalikan tuple daripada iterator, tapi saya pikir itu memiliki keanggunan tertentu. Itu juga tidak pad; jika Anda ingin padding, variasi sederhana di atas akan cukup:

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

Seperti izip_longestsolusi berbasis, di atas selalu bantalan. Sejauh yang saya tahu, tidak ada satu atau dua baris itertools resep untuk fungsi itu secara opsional bantalan. Dengan menggabungkan dua pendekatan di atas, yang satu ini cukup dekat:

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

Saya percaya ini adalah chunker terpendek yang diusulkan yang menawarkan padding opsional.


65
2018-02-26 15:02



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

48
2018-06-26 19:10