Câu hỏi Làm cách nào để chia danh sách thành các phần có kích thước đồng đều?


Tôi có một danh sách các chiều dài tùy ý, và tôi cần phải chia nó thành các phần kích thước bằng nhau và hoạt động trên nó. Có một số cách rõ ràng để làm điều này, như giữ một quầy và hai danh sách, và khi danh sách thứ hai đầy, hãy thêm nó vào danh sách đầu tiên và làm rỗng danh sách thứ hai cho vòng tiếp theo của dữ liệu, nhưng điều này có khả năng cực kỳ tốn kém.

Tôi đã tự hỏi liệu có ai có giải pháp tốt cho danh sách này với bất kỳ độ dài nào không, ví dụ: sử dụng máy phát điện.

Tôi đang tìm kiếm một cái gì đó hữu ích trong itertools nhưng tôi không thể tìm thấy bất cứ điều gì rõ ràng hữu ích. Có thể đã bỏ lỡ nó, mặc dù.

Câu hỏi liên quan: Cách "pythonic" nhất để lặp qua một danh sách theo khối là gì?


1580
2017-11-23 12:15


gốc


Một giải pháp tối ưu (thân thiện với bộ nhớ hơn) ở đây: stackoverflow.com/questions/7133179/python-yield-and-delete - Radim
FWIW, thư viện more_itertools Cung cấp một chunked chức năng thực hiện điều này một cách hiệu quả. - bgusach


Các câu trả lời:


Đây là một trình tạo ra các khối bạn muốn:

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

Nếu bạn đang sử dụng Python 2, bạn nên sử dụng xrange() thay vì 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]

Ngoài ra, bạn chỉ có thể sử dụng tính năng đọc danh sách thay vì viết một hàm. Python 3:

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

Phiên bản Python 2:

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

2116
2017-11-23 12:33



Điều gì sẽ xảy ra nếu chúng ta không thể nói chiều dài của danh sách? Hãy thử điều này trên itertools.repeat ([1, 2, 3]), ví dụ: - jespern
Đó là một phần mở rộng thú vị cho câu hỏi, nhưng câu hỏi ban đầu được hỏi rõ ràng về hoạt động trên một danh sách. - Ned Batchelder
Chương trình chuyển đổi 2to3 thay đổi tất cả các cuộc gọi xrange thành phạm vi kể từ trong Python 3.0, chức năng của phạm vi sẽ tương đương với hàm xrange (tức là nó sẽ trả về một trình lặp). Vì vậy, tôi sẽ tránh sử dụng phạm vi và sử dụng xrange thay thế. - Tomi Kyöstilä
@attz thực sự range đã bị xóa khỏi Python 3.0 và xrange đã được đổi tên thành range. - Kos
@zedr, rằng "tuple comprehension" thực sự là một "biểu hiện máy phát điện". Một sự hiểu biết tuple sẽ giống như tuple(l[i:i+n] for i in xrange(0, len(l), n)). :-) - Ben Hoyt


Nếu bạn muốn một cái gì đó siêu đơn giản:

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



Hoặc (nếu chúng ta đang thực hiện các biểu diễn khác nhau của hàm cụ thể này), bạn có thể định nghĩa hàm lambda qua: lambda x, y: [x [i: i + y] cho i trong phạm vi (0, len (x), y) ]. Tôi thích phương pháp hiểu danh sách này! - J-P
sau khi trở về phải có [, không phải ( - alwbtc
@alwbtc - không có chính xác nó là một máy phát điện - Mr_and_Mrs_D
"Siêu đơn giản" có nghĩa là không phải gỡ lỗi vòng lặp vô hạn - kudo cho max(). - Bob Stein


Trực tiếp từ tài liệu Python (cũ) (công thức nấu ăn cho 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)

Phiên bản hiện tại, theo đề xuất của 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)

Tôi đoán máy thời gian của Guido hoạt động — đã hoạt động - sẽ hoạt động - sẽ hoạt động - đang hoạt động trở lại.

Những giải pháp này hiệu quả vì [iter(iterable)]*n (hoặc tương đương trong phiên bản cũ hơn) tạo một lặp lại, lặp lại n lần trong danh sách. izip_longest sau đó thực hiện hiệu quả vòng xoay của trình lặp "từng"; bởi vì đây là cùng một trình lặp, nó được nâng cao bởi mỗi cuộc gọi như vậy, dẫn đến mỗi vòng tròn như vậy tạo ra một tuple n mặt hàng.


251
2017-11-23 15:48



Nó là izip_longest(*[iter(iterable)]*n, fillvalue=fillvalue) ngày nay. - jfs
upvoted này bởi vì nó hoạt động trên máy phát điện (không có len) và sử dụng mô-đun itertools thường nhanh hơn. - Michael Dillon
Bạn có thể kết hợp tất cả điều này thành một lớp lót ngắn: zip(*[iter(yourList)]*n) (hoặc là izip_longest với fillvalue) - ninjagecko
Một ví dụ cổ điển về ưa thích itertools phương pháp tiếp cận chức năng bật ra một số bùn không thể đọc được, khi so sánh với một thực hiện đơn giản và ngây thơ tinh khiết ngây thơ - wim
@wim Cho rằng câu trả lời này bắt đầu như một đoạn trích từ tài liệu Python, tôi khuyên bạn nên mở một vấn đề bugs.python.org . - tzot


Tôi biết đây là loại cũ nhưng tôi không lý do tại sao không ai đề cập đến 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



Điều này cho phép bạn thiết lập tổng số khối, không phải số phần tử trên mỗi đoạn. - FizxMike
bạn có thể tự mình làm toán. nếu bạn có 10 yếu tố, bạn có thể nhóm chúng thành 2, 5 phần tử khối hoặc năm 2 phần tử - Moj
+1 Đây là giải pháp yêu thích của tôi, vì nó chia mảng thành như nhau các mảng có kích thước, trong khi các giải pháp khác thì không (trong tất cả các giải pháp khác mà tôi đã xem, mảng cuối cùng có thể tùy ý nhỏ). - MiniQuark
Tôi ước tôi đã tìm thấy điều này như 5 năm trước. Cảm ơn @Moj chức năng rất hữu ích. - O.rka
@MiniQuark nhưng điều này làm gì khi số khối không phải là yếu tố của kích thước mảng ban đầu? - Baldrickk


Đây là một máy phát điện hoạt động trên các vòng lặp tùy ý:

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

Thí dụ:

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





Tôi ngạc nhiên không ai nghĩ đến việc sử dụng iter'S dạng hai tham số:

from itertools import islice

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

Bản giới thiệu:

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

Điều này làm việc với bất kỳ lặp lại và sản lượng đầu ra lazily. Nó trả về các bộ dữ liệu thay vì các trình lặp, nhưng tôi nghĩ nó có một sự thanh lịch nhất định. Nó cũng không pad; nếu bạn muốn đệm, một biến thể đơn giản ở trên sẽ đủ:

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)

Bản giới thiệu:

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

Giống như izip_longestgiải pháp dựa trên, ở trên luôn luôn miếng đệm. Theo như tôi biết, không có công thức itertools một hoặc hai dòng cho một hàm tùy chọn miếng đệm. Bằng cách kết hợp hai phương pháp trên, điều này đến khá gần:

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

Bản giới thiệu:

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

Tôi tin rằng đây là chunker ngắn nhất đề xuất rằng cung cấp đệm tùy chọn.


65
2018-02-26 15:02



Tuyệt vời, phiên bản đơn giản của bạn là yêu thích của tôi. Những người khác cũng nghĩ ra điều cơ bản islice(it, size) biểu thức và nhúng nó (như tôi đã làm) trong một cấu trúc vòng lặp. Chỉ có bạn nghĩ về phiên bản hai đối số của iter() (Tôi hoàn toàn không biết), làm cho nó siêu thanh lịch (và có lẽ hiệu quả nhất). Tôi không biết rằng lập luận đầu tiên iter thay đổi đối với hàm 0 đối số khi được gửi. Bạn trả về một vòng lặp (pot. Infinite) của các khối, có thể sử dụng một iterator (pot. Infinite) làm đầu vào, không có len() và không có mảng mảng nào. Tuyệt vời! - ThomasH
Đây là lý do tại sao tôi đọc qua các câu trả lời thay vì chỉ quét các cặp vợ chồng hàng đầu. Tùy chọn padding là một yêu cầu trong trường hợp của tôi, và tôi cũng đã học về hình thức hai đối số của vòng lặp. - Kerr


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

47
2018-06-26 19:10



map(None, iter) bằng izip_longest(iter). - Thomas Ahle
@ TomaszWysocki Bạn có thể giải thích * ở phía trước của bạn iterator tuple? Có thể trong văn bản trả lời của bạn, nhưng tôi đã thấy rằng * được sử dụng theo cách đó trong Python trước đây. Cảm ơn! - theJollySin
@theJollySin Trong ngữ cảnh này, nó được gọi là toán tử splat. Việc sử dụng nó được giải thích ở đây - stackoverflow.com/questions/5917522/unzipping-and-the-operator. - rlms
Đóng nhưng đoạn cuối cùng không có phần tử nào để điền vào. Điều này có thể hoặc có thể không phải là một khiếm khuyết. Thực sự mô hình mát mẻ mặc dù.