คุณจะแบ่งรายการออกเป็นกลุ่มขนาดเท่า ๆ กันได้อย่างไร


2264

ฉันมีรายการความยาวโดยพลการและฉันจำเป็นต้องแยกมันออกเป็นชิ้นขนาดเท่ากันและทำงานกับมัน มีวิธีที่ชัดเจนในการทำเช่นรักษาตัวนับและสองรายการและเมื่อรายการที่สองเติมให้เพิ่มลงในรายการแรกและล้างรายการที่สองสำหรับรอบถัดไปของข้อมูล แต่อาจมีราคาแพงมาก

ฉันสงสัยว่าใครมีวิธีแก้ปัญหาที่ดีสำหรับรายการที่มีความยาวเช่นใช้เครื่องกำเนิดไฟฟ้า

ฉันกำลังมองหาบางสิ่งที่มีประโยชน์itertoolsแต่ฉันไม่พบสิ่งใดที่มีประโยชน์ชัดเจน แต่อาจจะพลาดได้

คำถามที่เกี่ยวข้อง: วิธี "pythonic" ที่มากที่สุดในการวนซ้ำรายการในกลุ่มคืออะไร


1
ก่อนที่คุณจะโพสต์คำตอบใหม่พิจารณาว่ามีคำตอบสำหรับคำถามนี้ 60+ ข้อ กรุณาตรวจสอบให้แน่ใจว่าคำตอบของคุณก่อให้เกิดข้อมูลที่ไม่ใช่คำตอบที่มีอยู่
janniks

สำหรับผู้ใช้ที่ต้องการหลีกเลี่ยงก้อนสุดท้ายขนาดเล็กโดยพลการมองไปที่การแยกรายการออกเป็นส่วน N ความยาวเท่ากันประมาณ
Wim

คำตอบ:


3150

นี่คือเครื่องกำเนิดไฟฟ้าที่ให้ผลตอบแทนชิ้นที่คุณต้องการ:

def chunks(lst, n):
    """Yield successive n-sized chunks from lst."""
    for i in range(0, len(lst), n):
        yield lst[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]]

หากคุณใช้ Python 2 คุณควรใช้xrange()แทนrange():

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

นอกจากนี้คุณสามารถใช้รายการความเข้าใจแทนการเขียนฟังก์ชั่นได้แม้ว่าจะเป็นความคิดที่ดีในการสรุปการดำเนินการเช่นนี้ในฟังก์ชั่นที่ตั้งชื่อเพื่อให้โค้ดของคุณเข้าใจง่ายขึ้น Python 3:

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

Python 2 เวอร์ชั่น:

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

71
จะเกิดอะไรขึ้นถ้าเราไม่สามารถบอกความยาวของรายการได้? ลองทำดูที่ itertools.repeat ([1, 2, 3]) เช่น
jespern

47
นั่นเป็นส่วนขยายที่น่าสนใจสำหรับคำถาม แต่คำถามเดิมถามชัดเจนเกี่ยวกับการใช้งานในรายการ
Ned Batchelder

33
ฟังก์ชั่นนี้จะต้องอยู่ในห้องสมุดมาตรฐานที่น่ากลัว
dgan

6
@ Calimo: คุณแนะนำอะไร? ฉันส่งรายการคุณด้วยองค์ประกอบ 47 รายการ คุณต้องการแยกออกเป็น "ชิ้นขนาดเท่ากัน" อย่างไร OP ยอมรับคำตอบดังนั้นพวกเขาจึงตกลงอย่างชัดเจนกับก้อนขนาดแตกต่างกันล่าสุด บางทีวลีภาษาอังกฤษไม่แน่ชัด
Ned Batchelder

8
โปรดอย่าตั้งชื่อตัวแปรของคุณ l ดูเหมือนว่า 1 และสับสน ผู้คนกำลังคัดลอกรหัสของคุณและคิดว่าไม่เป็นไร
Yasen

555

หากคุณต้องการสิ่งที่ง่ายสุด ๆ :

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

ใช้xrange()แทนrange()ในกรณีของ Python 2.x


6
หรือ (ถ้าเราทำหน้าที่เป็นตัวแทนที่แตกต่างกันของฟังก์ชั่นนี้โดยเฉพาะ) คุณสามารถกำหนดฟังก์ชั่นแลมบ์ดาผ่าน: lambda x, y: [x [i: i + y] สำหรับ i ในช่วง (0, len (x), y) ]. ฉันชอบวิธี list-comprehension!
JP

4
หลังจากส่งคืนจะต้องมี [ไม่ใช่ (
alwbtc

2
"ซูเปอร์ง่าย" หมายถึงไม่ต้องแก้ปัญหาลูปอนันต์ - max()รุ่งโรจน์สำหรับ
Bob Stein

มีอะไรที่ง่ายๆเกี่ยวกับการแก้ปัญหานี้
mit

1
@Nhoj_Gonk อ๊ะไม่ใช่ห่วงที่ไม่สิ้นสุด แต่ชิ้น (L, 0) จะเพิ่ม ValueError โดยไม่มี max () แต่ค่าสูงสุด () จะเปลี่ยนอะไรที่น้อยกว่า 1 ให้เป็น 1
Bob Stein

295

โดยตรงจากเอกสาร Python (เก่า) (สูตรสำหรับ 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)

เวอร์ชันปัจจุบันตามที่แนะนำโดย JFSebastian:

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

ฉันเดาว่าไทม์แมชชีนของไททำงาน - ทำงาน - จะทำงาน - จะทำงาน - ทำงานได้อีกครั้ง

โซลูชันเหล่านี้ใช้งานได้เพราะ[iter(iterable)]*n(หรือเทียบเท่าในรุ่นก่อนหน้า) สร้างตัววนซ้ำหนึ่งnครั้งเวลาซ้ำในรายการ izip_longestจากนั้นจึงทำการวนรอบของแต่ละตัววนซ้ำอย่างมีประสิทธิภาพ เพราะนี่คือตัววนซ้ำเดียวกันมันเป็นขั้นสูงโดยการเรียกแต่ละครั้งทำให้เกิด zip-roundrobin แต่ละตัวที่สร้างรายการ tuple หนึ่งnรายการ


@ninjagecko: list(grouper(3, range(10)))ผลตอบแทน[(0, 1, 2), (3, 4, 5), (6, 7, 8), (9, None, None)]และสิ่งอันดับทั้งหมดมีความยาว 3. โปรดอธิบายความคิดเห็นของคุณให้ละเอียดเพราะฉันไม่เข้าใจ คุณเรียกว่าอะไรและคุณกำหนดให้มันเป็นผลคูณของ 3ใน“ คาดหวังให้สิ่งของของคุณเป็นทวีคูณของ 3”? ขอบคุณล่วงหน้า.
tzot

14
อัปเดตสิ่งนี้เพราะมันทำงานกับเครื่องกำเนิดไฟฟ้า (ไม่ len) และใช้โมดูล itertools ที่เร็วขึ้น
Michael Dillon

88
ตัวอย่างคลาสสิกของแฟนซีitertoolsวิธีการทำงานเปิดออกบางตะกอนอ่านไม่ได้เมื่อเทียบกับการที่ง่ายและไร้เดียงสาการดำเนินหลามบริสุทธิ์
Wim

15
@wim ระบุว่าคำตอบนี้เริ่มเป็นตัวอย่างข้อมูลจากเอกสารหลามผมขอแนะนำให้คุณเปิดปัญหาในbugs.python.org
tzot

1
@pedrosaurio ถ้าl==[1, 2, 3]แล้วเทียบเท่ากับf(*l) f(1, 2, 3)ดูว่าคำถามและเอกสารอย่างเป็นทางการ
tzot

225

ฉันรู้ว่ามันเก่า แต่ก็ยังไม่มีใครพูดถึงnumpy.array_split:

import numpy as np

lst = range(50)
np.array_split(lst, 5)
# [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])]

12
สิ่งนี้ช่วยให้คุณกำหนดจำนวนชิ้นทั้งหมดไม่ใช่จำนวนองค์ประกอบต่อชิ้น
FizxMike

6
คุณสามารถทำคณิตศาสตร์ได้ด้วยตัวเอง หากคุณมี 10 องค์ประกอบคุณสามารถจัดกลุ่มองค์ประกอบเหล่านั้นเป็นชิ้น 2, 5 ชิ้นหรือห้าชิ้น 2 ชิ้น
Moj

24
+1 นี่คือโซลูชันที่ฉันโปรดปรานเพราะมันแยกอาร์เรย์ออกเป็นอาร์เรย์ขนาดเท่า ๆ กันในขณะที่โซลูชันอื่นไม่ได้ (ในโซลูชันอื่น ๆ ทั้งหมดที่ฉันดูอาร์เรย์สุดท้ายอาจมีขนาดเล็กโดยพลการ)
MiniQuark

@MiniQuark แต่จะทำอย่างไรเมื่อจำนวนบล็อกไม่ได้เป็นปัจจัยของขนาดอาร์เรย์เดิม
Baldrickk

1
@Baldrickk หากคุณแยกองค์ประกอบ N ออกเป็น K ชิ้นส่วน N% K ชิ้นแรกจะมีองค์ประกอบ N / K + 1 และส่วนที่เหลือจะมีองค์ประกอบ N / K ตัวอย่างเช่นถ้าคุณแยกอาร์เรย์ที่มีองค์ประกอบ 108 รายการเป็น 5 ชิ้นดังนั้น 108% 5 = 3 ชิ้นแรกจะมีองค์ประกอบ 108 // 5 + 1 = 22 และชิ้นส่วนที่เหลือจะมี 108 // 5 = 21 องค์ประกอบ
MiniQuark

147

ฉันไม่มีใครแปลกใจที่มีความคิดของการใช้iterเป็นรูปแบบที่สองอาร์กิวเมนต์ :

from itertools import islice

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

การสาธิต:

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

ทำงานได้กับ iterable ใด ๆ และสร้างผลลัพธ์อย่างขี้เกียจ มันส่งคืนสิ่งอันดับมากกว่าที่จะเป็นตัววนซ้ำ แต่ฉันคิดว่ามันมีความสง่างามบางอย่าง มันไม่ได้เป็นแผ่น หากคุณต้องการแพ็ดดิ้งรูปแบบง่าย ๆ ด้านบนจะพอเพียง:

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)

การสาธิต:

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

เช่นเดียวกับizip_longestโซลูชันที่ใช้แผ่นรองข้างต้นจะเสมอ เท่าที่ผมรู้ว่าไม่มีสูตร itertools หนึ่งหรือสองบรรทัดสำหรับฟังก์ชั่นที่เลือกแผ่น ด้วยการรวมสองวิธีข้างต้นเข้าด้วยกัน:

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

การสาธิต:

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

ฉันเชื่อว่านี่เป็นชอว์เกอร์ที่สั้นที่สุดที่เสนอให้มีช่องว่างเสริม

ดังที่ Tomasz Gandor ได้สังเกตพบว่าช่องว่างระหว่างสองชั้นจะหยุดลงอย่างกะทันหันหากพวกเขาพบกับค่าแผ่นยาว นี่คือรูปแบบสุดท้ายที่สามารถแก้ไขปัญหานั้นได้อย่างสมเหตุสมผล:

_no_padding = object()
def chunk(it, size, padval=_no_padding):
    it = iter(it)
    chunker = iter(lambda: tuple(islice(it, size)), ())
    if padval == _no_padding:
        yield from chunker
    else:
        for ch in chunker:
            yield ch if len(ch) == size else ch + (padval,) * (size - len(ch))

การสาธิต:

>>> list(chunk([1, 2, (), (), 5], 2))
[(1, 2), ((), ()), (5,)]
>>> list(chunk([1, 2, None, None, 5], 2, None))
[(1, 2), (None, None), (5, None)]

7
มหัศจรรย์รุ่นที่เรียบง่ายของคุณเป็นที่ชื่นชอบ คนอื่น ๆ ก็เกิดขึ้นพร้อมกับการislice(it, size)แสดงออกขั้นพื้นฐานและฝังมัน (เหมือนที่ฉันเคยทำ) ในโครงสร้างลูป มีเพียงคุณเท่านั้นที่คิดว่ารุ่นสองข้อโต้แย้งของiter()(ฉันไม่รู้ทั้งหมด) ซึ่งทำให้ดูสง่างามมาก (และอาจมีประสิทธิภาพดีที่สุด) ฉันไม่ทราบว่าอาร์กิวเมนต์แรกของการiterเปลี่ยนแปลงเป็นฟังก์ชัน 0 อาร์กิวเมนต์เมื่อได้รับ Sentinel คุณคืนค่าตัววนซ้ำ (pot. infinite) ของ chunks สามารถใช้ iterator (pot. infinite) เป็นอินพุตได้ไม่มีส่วนย่อยlen()และไม่มี ! น่ากลัว
ThomasH

1
นี่คือเหตุผลที่ฉันอ่านคำตอบมากกว่าที่จะสแกนแค่คู่บน ตัวเลือกเสริมคือข้อกำหนดในกรณีของฉันและฉันก็เรียนรู้เกี่ยวกับรูปแบบสองอาร์กิวเมนต์ของ iter
เคอร์

ฉันลงคะแนนเสียงให้กับสิ่งนี้ แต่ยัง - อย่าให้มากเกินไป! ก่อนอื่นแลมบ์ดาอาจไม่ดี (ปิดช้ากว่าตัวitวนซ้ำอย่างที่สองและสำคัญที่สุด - คุณจะสิ้นสุดก่อนกำหนดหากชิ้นpadvalอันที่มีอยู่จริงในการทำซ้ำของคุณและควรได้รับการประมวลผล
Tomasz Gandor

@TomaszGandor ฉันใช้จุดแรกของคุณ! แม้ว่าความเข้าใจของฉันคือแลมบ์ดาไม่ได้ช้ากว่าฟังก์ชั่นทั่วไปแน่นอนคุณพูดถูกว่าการเรียกฟังก์ชั่นและการปิดฟังก์ชั่นจะช้าลง ฉันไม่ทราบว่าประสิทธิภาพที่สัมพันธ์กันของสิ่งนี้จะเป็นอย่างไรเมื่อเทียบกับizip_longestวิธีการ - ฉันสงสัยว่ามันอาจเป็นการค้าที่ซับซ้อน แต่ ... padvalปัญหาไม่ได้ถูกแชร์โดยคำตอบทุกข้อที่เสนอpadvalพารามิเตอร์
senderle

1
@TomaszGandor ยุติธรรมพอ! แต่มันก็ไม่ยากเกินไปที่จะสร้างเวอร์ชันที่แก้ไขสิ่งนี้ (นอกจากนี้ยังทราบว่ารุ่นแรกซึ่งใช้()เป็นแมวมองที่ไม่ทำงานอย่างถูกต้องเพราะนี่คือ. tuple(islice(it, size))อัตราผลตอบแทน()เมื่อitเป็นที่ว่างเปล่า.)
senderle

93

นี่คือตัวกำเนิดที่ทำงานกับค่าที่สุ่มได้:

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

ตัวอย่าง:

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

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

map(None, iter)izip_longest(iter)เท่ากับ
โทมัส Ahle

1
@TomaszWysocki คุณสามารถอธิบาย*ต่อหน้า iterator tuple ของคุณได้ไหม อาจเป็นไปได้ในข้อความคำตอบของคุณ แต่ฉันเห็นว่า*เคยใช้วิธีนี้ใน Python มาก่อน ขอบคุณ!
theJollySin

1
@theJollySin ในบริบทนี้จะเรียกว่าตัวดำเนินการ splat การใช้งานจะมีการอธิบายที่นี่ - stackoverflow.com/questions/5917522/unzipping-and-the-operator
rlms

2
ปิด แต่อันสุดท้ายไม่มีองค์ประกอบที่จะเติมให้เต็ม นี่อาจเป็นข้อบกพร่อง รูปแบบเด็ดจริงๆ

49

เรียบง่าย แต่สง่างาม

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

หรือถ้าคุณต้องการ:

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

18
ท่านจะไม่ตั้งชื่อตัวแปรในลักษณะของตัวเลขอารบิก ในบางฟอนต์1และlแยกไม่ออก ในฐานะที่เป็นและ0 Oและบางครั้งก็และI 1
Alfe

14
@Alfe แบบอักษรที่มีข้อบกพร่อง ผู้คนไม่ควรใช้แบบอักษรดังกล่าว ไม่ได้สำหรับการเขียนโปรแกรมไม่ได้สำหรับอะไร
Jerry B

17
แลมบ์ดามีวัตถุประสงค์เพื่อใช้เป็นฟังก์ชันที่ไม่มีชื่อ ไม่มีประโยชน์ในการใช้มันอย่างนั้น นอกจากนี้ยังทำให้การดีบักยากขึ้นเนื่องจากการติดตามกลับจะรายงาน "ใน <lambda>" แทนที่จะเป็น "ในกลุ่ม" ในกรณีที่เกิดข้อผิดพลาด ฉันขอให้คุณโชคหาปัญหาหากคุณมีทั้งกลุ่มของ :) เหล่านี้
คริส Koston

1
มันควรเป็น 0 และไม่ใช่ 1 ภายใน xrange ในprint [l[x:x+10] for x in xrange(1, len(l), 10)]
scottydelta

หมายเหตุ:rangeสำหรับการใช้งานผู้ใช้ 3 คนหลาม
Christian Dean

40

คำติชมของคำตอบอื่น ๆ ที่นี่:

ไม่มีคำตอบเหล่านี้เป็นชิ้นขนาดเท่ากันพวกเขาทั้งหมดออกจากชิ้น runt ตอนท้ายดังนั้นพวกเขาไม่สมดุลอย่างสมบูรณ์ หากคุณใช้ฟังก์ชั่นเหล่านี้เพื่อแจกจ่ายงานคุณได้สร้างโอกาสที่จะทำให้งานเสร็จก่อนที่คนอื่นจะสร้างมันขึ้นมาดังนั้นมันจะไม่ทำอะไรเลยในขณะที่คนอื่นทำงานหนักต่อไป

ตัวอย่างเช่นคำตอบยอดนิยมปัจจุบันลงท้ายด้วย:

[60, 61, 62, 63, 64, 65, 66, 67, 68, 69],
[70, 71, 72, 73, 74]]

ฉันแค่เกลียดแค้นที่ท้าย!

คนอื่น ๆ ชอบlist(grouper(3, xrange(7)))และchunk(xrange(7), 3)ทั้งสองกลับมา: [(0, 1, 2), (3, 4, 5), (6, None, None)]. None's เป็นเพียงรองและค่อนข้างไม่งดงามในความคิดของฉัน พวกเขาจะไม่ chunking iterables อย่างสม่ำเสมอ

ทำไมเราไม่หารเหล่านี้ให้ดีกว่านี้?

โซลูชันของฉัน

นี่คือวิธีการแก้ปัญหาที่สมดุลซึ่งดัดแปลงมาจากฟังก์ชั่นที่ฉันใช้ในการผลิต (หมายเหตุใน Python 3 เพื่อแทนที่xrangeด้วยrange):

def baskets_from(items, maxbaskets=25):
    baskets = [[] for _ in xrange(maxbaskets)] # in Python 3 use range
    for i, item in enumerate(items):
        baskets[i % maxbaskets].append(item)
    return filter(None, baskets) 

และฉันสร้างเครื่องกำเนิดไฟฟ้าที่ทำเช่นเดียวกันหากคุณใส่ไว้ในรายการ:

def iter_baskets_from(items, maxbaskets=3):
    '''generates evenly balanced baskets from indexable iterable'''
    item_count = len(items)
    baskets = min(item_count, maxbaskets)
    for x_i in xrange(baskets):
        yield [items[y_i] for y_i in xrange(x_i, item_count, baskets)]

และในที่สุดเนื่องจากฉันเห็นว่าฟังก์ชั่นทั้งหมดข้างต้นคืนองค์ประกอบตามลำดับที่ต่อเนื่องกัน (ตามที่ได้รับ):

def iter_baskets_contiguous(items, maxbaskets=3, item_count=None):
    '''
    generates balanced baskets from iterable, contiguous contents
    provide item_count if providing a iterator that doesn't support len()
    '''
    item_count = item_count or len(items)
    baskets = min(item_count, maxbaskets)
    items = iter(items)
    floor = item_count // baskets 
    ceiling = floor + 1
    stepdown = item_count % baskets
    for x_i in xrange(baskets):
        length = ceiling if x_i < stepdown else floor
        yield [items.next() for _ in xrange(length)]

เอาท์พุต

เพื่อทดสอบพวกเขา

print(baskets_from(xrange(6), 8))
print(list(iter_baskets_from(xrange(6), 8)))
print(list(iter_baskets_contiguous(xrange(6), 8)))
print(baskets_from(xrange(22), 8))
print(list(iter_baskets_from(xrange(22), 8)))
print(list(iter_baskets_contiguous(xrange(22), 8)))
print(baskets_from('ABCDEFG', 3))
print(list(iter_baskets_from('ABCDEFG', 3)))
print(list(iter_baskets_contiguous('ABCDEFG', 3)))
print(baskets_from(xrange(26), 5))
print(list(iter_baskets_from(xrange(26), 5)))
print(list(iter_baskets_contiguous(xrange(26), 5)))

ซึ่งพิมพ์ออกมา:

[[0], [1], [2], [3], [4], [5]]
[[0], [1], [2], [3], [4], [5]]
[[0], [1], [2], [3], [4], [5]]
[[0, 8, 16], [1, 9, 17], [2, 10, 18], [3, 11, 19], [4, 12, 20], [5, 13, 21], [6, 14], [7, 15]]
[[0, 8, 16], [1, 9, 17], [2, 10, 18], [3, 11, 19], [4, 12, 20], [5, 13, 21], [6, 14], [7, 15]]
[[0, 1, 2], [3, 4, 5], [6, 7, 8], [9, 10, 11], [12, 13, 14], [15, 16, 17], [18, 19], [20, 21]]
[['A', 'D', 'G'], ['B', 'E'], ['C', 'F']]
[['A', 'D', 'G'], ['B', 'E'], ['C', 'F']]
[['A', 'B', 'C'], ['D', 'E'], ['F', 'G']]
[[0, 5, 10, 15, 20, 25], [1, 6, 11, 16, 21], [2, 7, 12, 17, 22], [3, 8, 13, 18, 23], [4, 9, 14, 19, 24]]
[[0, 5, 10, 15, 20, 25], [1, 6, 11, 16, 21], [2, 7, 12, 17, 22], [3, 8, 13, 18, 23], [4, 9, 14, 19, 24]]
[[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]]

โปรดสังเกตว่าตัวกำเนิดที่ต่อเนื่องกันจัดให้ชิ้นส่วนในรูปแบบความยาวเดียวกันกับอีกสองชิ้น แต่รายการทั้งหมดนั้นอยู่ในลำดับและพวกมันจะถูกแบ่งเท่า ๆ กันอย่างที่ใครคนหนึ่งอาจแบ่งรายการองค์ประกอบที่ไม่ต่อเนื่องกัน


คุณบอกว่าไม่มีสิ่งใดที่กล่าวมาข้างต้น แต่คนนี้ไม่เช่นเดียวกับคนนี้
ส่ง

1
@senderle คนแรกlist(grouper(3, xrange(7)))และคนที่chunk(xrange(7), 3)สองกลับมาทั้งสอง: [(0, 1, 2), (3, 4, 5), (6, None, None)]. None's เป็นเพียงรองและค่อนข้างไม่งดงามในความคิดของฉัน พวกเขาจะไม่ chunking iterables อย่างสม่ำเสมอ ขอบคุณสำหรับการโหวตของคุณ!
Aaron Hall

4
คุณตั้งคำถาม (โดยไม่ทำอย่างชัดเจนดังนั้นตอนนี้ฉันทำที่นี่) ไม่ว่าจะเป็นชิ้นขนาดเท่ากัน (ยกเว้นที่ผ่านมาหากไม่สามารถทำได้) หรือผลลัพธ์ที่สมดุล (ดีที่สุดเท่าที่เป็นไปได้) นั้นบ่อยกว่าสิ่งที่ต้องการ คุณคิดว่าโซลูชันที่สมดุลคือสิ่งที่ต้องการ สิ่งนี้อาจเป็นจริงหากสิ่งที่โปรแกรมของคุณใกล้เคียงกับโลกแห่งความเป็นจริง (เช่นอัลกอริธึมการแจกการ์ดสำหรับเกมไพ่จำลอง) ในกรณีอื่น ๆ (เช่นการกรอกบรรทัดด้วยคำ) หนึ่งจะชอบที่จะให้เต็มบรรทัดที่เป็นไปได้ ดังนั้นฉันจึงไม่ชอบอีกอันหนึ่งมากกว่า มันเป็นเพียงสำหรับกรณีการใช้งานที่แตกต่างกัน
Alfe

@ ChristopherBarrington-Leigh จุดที่ดีสำหรับ DataFrames คุณควรใช้ส่วนข้อมูลเนื่องจากฉันเชื่อว่าวัตถุ DataFrame มักจะไม่คัดลอกเมื่อแบ่งตัวอย่างเช่นimport pandas as pd; [pd.DataFrame(np.arange(7))[i::3] for i in xrange(3)]
Aaron Hall

1
@AaronHall โอ๊ะโอ ฉันลบความคิดเห็นของฉันเพราะฉันเดาคำติชมของฉันที่สอง แต่คุณได้อย่างรวดเร็วในการวาด ขอบคุณ! ในความเป็นจริงการอ้างสิทธิ์ของฉันว่ามันใช้งานไม่ได้กับ dataframes จริง หาก items เป็น dataframe ให้ใช้รายการผลผลิต [range (x_i, item_count, baskets)] เป็นบรรทัดสุดท้าย ฉันเสนอคำตอบแยกต่างหาก (อีกคำตอบ) ซึ่งคุณระบุขนาดกลุ่ม (ขั้นต่ำ) ที่ต้องการ
CPBL

38

ฉันเห็นคำตอบของ Python-ish ที่ยอดเยี่ยมที่สุดในคำถามที่ซ้ำกัน :

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

คุณสามารถสร้าง n-tuple สำหรับ n ใดก็ได้ ถ้าa = range(1, 15)ผลลัพธ์จะเป็น:

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

หากรายการถูกแบ่งเท่า ๆ กันคุณสามารถแทนที่zip_longestด้วยzipมิฉะนั้น triplet (13, 14, None)จะหายไป Python 3 ถูกใช้งานด้านบน สำหรับงูหลาม 2 izip_longestใช้


นั่นเป็นสิ่งที่ดีถ้ารายชื่อและชิ้นส่วนของคุณสั้นคุณจะปรับตัวเลือกนี้เพื่อแยกรายการของคุณเป็นชิ้น ๆ 1000 ได้อย่างไร คุณ "ไม่ไปรหัสไปรษณีย์ (i, i, i, i, i, i, i, i, i, i, i, i ..... i = 1,000)
Tom Smith

9
zip(i, i, i, ... i)ด้วยอาร์กิวเมนต์ "chunk_size" เพื่อ zip () สามารถเขียนเป็นzip(*[i]*chunk_size)ไม่ว่าจะเป็นความคิดที่ดีหรือไม่เป็นที่ถกเถียงกันแน่นอน
Wilson F

1
ข้อเสียของสิ่งนี้คือถ้าคุณไม่ได้แบ่งเท่า ๆ กันคุณจะดร็อปอิลิเมนต์เนื่องจาก zip จะหยุดที่ iterable ที่สั้นที่สุด - & izip_longest จะเพิ่มอิลิเมนต์เริ่มต้น
Aaron Hall

zip_longestควรใช้ตามที่ทำไว้ใน: stackoverflow.com/a/434411/1959808
Ioannis Filippidis

คำตอบที่มีrange(1, 15)องค์ประกอบขาดหายไปแล้วเนื่องจากมี 14 องค์ประกอบในrange(1, 15)ไม่ใช่ 15
Ioannis Filippidis

35

ถ้าคุณรู้ขนาดของรายการ:

def SplitList(mylist, chunk_size):
    return [mylist[offs:offs+chunk_size] for offs in range(0, len(mylist), chunk_size)]

หากคุณทำไม่ได้ (เป็นตัววนซ้ำ):

def IterChunks(sequence, chunk_size):
    res = []
    for item in sequence:
        res.append(item)
        if len(res) >= chunk_size:
            yield res
            res = []
    if res:
        yield res  # yield the last, incomplete, portion

ในกรณีหลังมันสามารถใช้ถ้อยคำใหม่ได้อย่างสวยงามยิ่งขึ้นถ้าคุณมั่นใจได้ว่าลำดับนั้นมีจำนวนชิ้นขนาดที่กำหนดเสมอ (เช่นไม่มีชิ้นสุดท้ายที่ไม่สมบูรณ์)


ฉันเสียใจที่ฝังตัวลงไปมาก IterChunks ทำงานได้ทุกอย่างและเป็นวิธีการแก้ปัญหาทั่วไปและไม่มีคำเตือนที่ฉันรู้
Jason Dunkelberger

18

Toolzห้องสมุดมีpartitionฟังก์ชั่นสำหรับการนี้:

from toolz.itertoolz.core import partition

list(partition(2, [1, 2, 3, 4]))
[(1, 2), (3, 4)]

ดูเหมือนว่าคำแนะนำทั้งหมดจะง่ายที่สุด ฉันแค่สงสัยว่ามันจะเป็นจริงได้หรือไม่ว่าต้องใช้ห้องสมุดบุคคลที่สามเพื่อรับฟังก์ชั่นพาร์ติชั่น ฉันคาดว่าจะมีสิ่งที่เทียบเท่ากับฟังก์ชั่นพาร์ติชั่นนั้นมีอยู่เป็นภาษาในตัว
kasperd

1
คุณสามารถทำพาร์ติชันด้วย itertools แต่ฉันชอบไลบรารี่ toolz มันเป็นห้องสมุดที่ได้รับแรงบันดาลใจจาก clojure สำหรับการทำงานกับคอลเล็กชั่นในสไตล์การใช้งาน คุณไม่ได้รับการเปลี่ยนไม่ได้ แต่คุณจะได้คำศัพท์เล็ก ๆ น้อย ๆ สำหรับการทำงานกับคอลเล็กชั่นเรียบง่าย ข้อดีก็คือ cytoolz เขียนด้วย cython และเพิ่มประสิทธิภาพที่ดี github.com/pytoolz/cytoolz matthewrocklin.com/blog/work/2014/05/01/Introducing-CyToolz
zach

ลิงค์จากความคิดเห็นของ zach ใช้งานได้ถ้าคุณใช้เครื่องหมายทับต่อท้าย: matthewrocklin.com/blog/work/2014/05/01/Introducing-CyToolz
mit

17

หากคุณมีขนาดชิ้นละ 3 คุณสามารถทำได้:

zip(*[iterable[i::3] for i in range(3)]) 

แหล่งที่มา: http://code.activestate.com/recipes/303060-group-a-list-into-sequential-n-tuples/

ฉันจะใช้สิ่งนี้เมื่อขนาดก้อนของฉันเป็นจำนวนคงที่ฉันสามารถพิมพ์ได้เช่น '3' และจะไม่มีวันเปลี่ยนแปลง


11
สิ่งนี้จะไม่ทำงานหาก len (iterable)% 3! = 0 กลุ่มตัวเลข (สั้น) สุดท้ายจะไม่ถูกส่งกลับ
sherbang

16

ฉันชอบเวอร์ชันของ Python doc ที่เสนอโดย tzot และ JFSebastian มาก แต่มันมีข้อบกพร่องสองประการ:

  • มันไม่ชัดเจนมาก
  • ฉันมักจะไม่ต้องการเติมค่าในก้อนสุดท้าย

ฉันใช้อันนี้มากในรหัสของฉัน:

from itertools import islice

def chunks(n, iterable):
    iterable = iter(iterable)
    while True:
        yield tuple(islice(iterable, n)) or iterable.next()

อัปเดต: เวอร์ชันของกลุ่มขี้เกียจ:

from itertools import chain, islice

def chunks(n, iterable):
   iterable = iter(iterable)
   while True:
       yield chain([next(iterable)], islice(iterable, n-1))

เงื่อนไขการแบ่งสำหรับwhile Trueลูปคืออะไร
wjandrea

@wjandrea: การStopIterationยกเมื่อtupleว่างเปล่าและiterable.next()ได้รับการดำเนินการ ทำงานไม่ถูกต้องในปัจจุบันแม้ว่างูใหญ่ที่ออกจากเครื่องกำเนิดไฟฟ้าควรจะทำกับไม่ยกreturn รอบวงทั้ง (และการเปลี่ยนแปลงที่จะข้ามรุ่น compat) แก้ไขนี้ด้วยค่าใช้จ่ายน้อยที่สุดอย่างน้อย StopIterationtry/except StopIteration: returniterable.next()next(iterable)
ShadowRanger

15
[AA[i:i+SS] for i in range(len(AA))[::SS]]

โดยที่ AA คืออาร์เรย์ SS คือขนาดก้อน ตัวอย่างเช่น:

>>> AA=range(10,21);SS=3
>>> [AA[i:i+SS] for i in range(len(AA))[::SS]]
[[10, 11, 12], [13, 14, 15], [16, 17, 18], [19, 20]]
# or [range(10, 13), range(13, 16), range(16, 19), range(19, 21)] in py3

2
มันเป็นสิ่งที่ดีที่สุดและเรียบง่าย
F.Tamy

2
สั้นและเรียบง่าย ความเรียบง่ายมากกว่าความซับซ้อน
dkrynicki

15

ฉันอยากรู้เกี่ยวกับประสิทธิภาพของวิธีการต่าง ๆ และนี่คือ:

ทดสอบกับ Python 3.5.1

import time
batch_size = 7
arr_len = 298937

#---------slice-------------

print("\r\nslice")
start = time.time()
arr = [i for i in range(0, arr_len)]
while True:
    if not arr:
        break

    tmp = arr[0:batch_size]
    arr = arr[batch_size:-1]
print(time.time() - start)

#-----------index-----------

print("\r\nindex")
arr = [i for i in range(0, arr_len)]
start = time.time()
for i in range(0, round(len(arr) / batch_size + 1)):
    tmp = arr[batch_size * i : batch_size * (i + 1)]
print(time.time() - start)

#----------batches 1------------

def batch(iterable, n=1):
    l = len(iterable)
    for ndx in range(0, l, n):
        yield iterable[ndx:min(ndx + n, l)]

print("\r\nbatches 1")
arr = [i for i in range(0, arr_len)]
start = time.time()
for x in batch(arr, batch_size):
    tmp = x
print(time.time() - start)

#----------batches 2------------

from itertools import islice, chain

def batch(iterable, size):
    sourceiter = iter(iterable)
    while True:
        batchiter = islice(sourceiter, size)
        yield chain([next(batchiter)], batchiter)


print("\r\nbatches 2")
arr = [i for i in range(0, arr_len)]
start = time.time()
for x in batch(arr, batch_size):
    tmp = x
print(time.time() - start)

#---------chunks-------------
def chunks(l, n):
    """Yield successive n-sized chunks from l."""
    for i in range(0, len(l), n):
        yield l[i:i + n]
print("\r\nchunks")
arr = [i for i in range(0, arr_len)]
start = time.time()
for x in chunks(arr, batch_size):
    tmp = x
print(time.time() - start)

#-----------grouper-----------

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

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

arr = [i for i in range(0, arr_len)]
print("\r\ngrouper")
start = time.time()
for x in grouper(arr, batch_size):
    tmp = x
print(time.time() - start)

ผล:

slice
31.18285083770752

index
0.02184295654296875

batches 1
0.03503894805908203

batches 2
0.22681021690368652

chunks
0.019841909408569336

grouper
0.006506919860839844

3
การเปรียบเทียบการใช้timetimeit
ไลบรารี่

13

รหัส:

def split_list(the_list, chunk_size):
    result_list = []
    while the_list:
        result_list.append(the_list[:chunk_size])
        the_list = the_list[chunk_size:]
    return result_list

a_list = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]

print split_list(a_list, 3)

ผลลัพธ์:

[[1, 2, 3], [4, 5, 6], [7, 8, 9], [10]]

12

คุณอาจใช้get_chunksฟังก์ชั่นของutilspieห้องสมุดเป็น:

>>> from utilspie import iterutils
>>> a = [1, 2, 3, 4, 5, 6, 7, 8, 9]

>>> list(iterutils.get_chunks(a, 5))
[[1, 2, 3, 4, 5], [6, 7, 8, 9]]

คุณสามารถติดตั้งutilspieผ่าน pip:

sudo pip install utilspie

Disclaimer: ผมผู้สร้างของutilspieห้องสมุด


11

ณ จุดนี้ฉันคิดว่าเราต้องการเครื่องกำเนิดไฟฟ้าแบบเรียกซ้ำในกรณี ...

ในหลาม 2:

def chunks(li, n):
    if li == []:
        return
    yield li[:n]
    for e in chunks(li[n:], n):
        yield e

ในหลาม 3:

def chunks(li, n):
    if li == []:
        return
    yield li[:n]
    yield from chunks(li[n:], n)

นอกจากนี้ในกรณีที่มีการบุกรุกของมนุษย์ต่างดาวขนาดใหญ่เครื่องกำเนิดไฟฟ้าแบบวนซ้ำที่ตกแต่งไว้อาจมีประโยชน์

def dec(gen):
    def new_gen(li, n):
        for e in gen(li, n):
            if e == []:
                return
            yield e
    return new_gen

@dec
def chunks(li, n):
    yield li[:n]
    for e in chunks(li[n:], n):
        yield e

9

ด้วยการมอบหมายการแสดงออกใน Python 3.8 มันค่อนข้างดี:

import itertools

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

ใช้งานได้กับ iterable โดยพลการไม่ใช่แค่รายการ

>>> import pprint
>>> pprint.pprint(list(batch(range(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]]

1
ตอนนี้เป็นคำตอบใหม่ที่คู่ควรสำหรับคำถามนี้ จริง ๆ แล้วฉันชอบสิ่งนี้ ฉันไม่มั่นใจในการแสดงออกที่ได้รับมอบหมาย แต่เมื่อพวกเขาทำงานพวกเขาทำงาน
juanpa.arrivillaga

7

เฮ้รุ่นหนึ่งบรรทัด

In [48]: chunk = lambda ulist, step:  map(lambda i: ulist[i:i+step],  xrange(0, len(ulist), step))

In [49]: chunk(range(1,100), 10)
Out[49]: 
[[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, 75, 76, 77, 78, 79, 80],
 [81, 82, 83, 84, 85, 86, 87, 88, 89, 90],
 [91, 92, 93, 94, 95, 96, 97, 98, 99]]

36
กรุณาใช้ "def chunk" แทน "chunk = lambda" มันใช้งานได้เหมือนกัน หนึ่งบรรทัด คุณสมบัติเดียวกัน ง่ายกว่ามากที่ n00bz เพื่ออ่านและทำความเข้าใจ
S.Lott

4
@ S.Lott: ไม่ใช่ถ้า n00bz มาจากแบบแผน: P นี่ไม่ใช่ปัญหาจริง มีแม้กระทั่งคำสำคัญสำหรับ google! คุณสมบัติอื่น ๆ ที่แสดงให้เห็นว่าเราหลีกเลี่ยงเพื่อประโยชน์ของ n00bz หรือไม่ ฉันคิดว่าผลผลิตไม่จำเป็น / c- เหมือนพอที่จะเป็นมิตรกับ n00b แล้ว
Janus Troelsen

16
วัตถุฟังก์ชั่นที่เกิดจากdef chunkแทนที่จะchunk=lambdaมี. _ name__ คุณลักษณะ 'chunk' แทน '<lambda>' ชื่อเฉพาะนั้นมีประโยชน์มากกว่าในการสืบค้นกลับ
เทอร์รี่ Jan Reedy

1
@Alfe: ฉันไม่แน่ใจว่าสามารถเรียกความแตกต่างทางความหมายหลักได้หรือไม่ แต่มีชื่อที่มีประโยชน์ในการสืบค้นกลับแทนที่จะเป็น<lamba>หรือไม่อย่างน้อยก็มีความแตกต่างที่น่าสังเกต
martineau

1
หลังจากทดสอบกลุ่มของพวกเขาเพื่อการแสดงแล้วนี่ยอดเยี่ยมมาก!
Sunny Patel

7
def split_seq(seq, num_pieces):
    start = 0
    for i in xrange(num_pieces):
        stop = start + len(seq[i::num_pieces])
        yield seq[start:stop]
        start = stop

การใช้งาน:

seq = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]

for seq in split_seq(seq, 3):
    print seq

7

อีกรุ่นที่ชัดเจนมากขึ้น

def chunkList(initialList, chunkSize):
    """
    This function chunks a list into sub lists 
    that have a length equals to chunkSize.

    Example:
    lst = [3, 4, 9, 7, 1, 1, 2, 3]
    print(chunkList(lst, 3)) 
    returns
    [[3, 4, 9], [7, 1, 1], [2, 3]]
    """
    finalList = []
    for i in range(0, len(initialList), chunkSize):
        finalList.append(initialList[i:i+chunkSize])
    return finalList

(2016 Sep 12) คำตอบนี้เป็นภาษาที่เป็นอิสระมากที่สุดและง่ายที่สุดในการอ่าน
อดัมส์ D

7

โดยไม่ต้องเรียก len () ซึ่งดีสำหรับรายการขนาดใหญ่:

def splitter(l, n):
    i = 0
    chunk = l[:n]
    while chunk:
        yield chunk
        i += n
        chunk = l[i:i+n]

และนี่คือสำหรับ iterables:

def isplitter(l, n):
    l = iter(l)
    chunk = list(islice(l, n))
    while chunk:
        yield chunk
        chunk = list(islice(l, n))

ฟังก์ชั่นรสชาติของข้างต้น:

def isplitter2(l, n):
    return takewhile(bool,
                     (tuple(islice(start, n))
                            for start in repeat(iter(l))))

หรือ:

def chunks_gen_sentinel(n, seq):
    continuous_slices = imap(islice, repeat(iter(seq)), repeat(0), repeat(n))
    return iter(imap(tuple, continuous_slices).next,())

หรือ:

def chunks_gen_filter(n, seq):
    continuous_slices = imap(islice, repeat(iter(seq)), repeat(0), repeat(n))
    return takewhile(bool,imap(tuple, continuous_slices))

16
ไม่มีเหตุผลที่จะหลีกเลี่ยงlen()รายการใหญ่ ๆ เป็นการดำเนินการเวลาคงที่
Thomas Wouters

7

นี่คือรายการของวิธีการเพิ่มเติม:

ป.ร. ให้ไว้

import itertools as it
import collections as ct

import more_itertools as mit


iterable = range(11)
n = 3

รหัส

ห้องสมุดมาตรฐาน

list(it.zip_longest(*[iter(iterable)] * n))
# [(0, 1, 2), (3, 4, 5), (6, 7, 8), (9, 10, None)]

d = {}
for i, x in enumerate(iterable):
    d.setdefault(i//n, []).append(x)

list(d.values())
# [[0, 1, 2], [3, 4, 5], [6, 7, 8], [9, 10]]

dd = ct.defaultdict(list)
for i, x in enumerate(iterable):
    dd[i//n].append(x)

list(dd.values())
# [[0, 1, 2], [3, 4, 5], [6, 7, 8], [9, 10]]

more_itertools+

list(mit.chunked(iterable, n))
# [[0, 1, 2], [3, 4, 5], [6, 7, 8], [9, 10]]

list(mit.sliced(iterable, n))
# [range(0, 3), range(3, 6), range(6, 9), range(9, 11)]

list(mit.grouper(n, iterable))
# [(0, 1, 2), (3, 4, 5), (6, 7, 8), (9, 10, None)]

list(mit.windowed(iterable, len(iterable)//n, step=n))
# [(0, 1, 2), (3, 4, 5), (6, 7, 8), (9, 10, None)]

อ้างอิง

+ห้องสมุดบุคคลที่สามที่ใช้สูตรของ itertoolsและอีกมากมาย> pip install more_itertools


6

ดูเอกสารอ้างอิงนี้

>>> orange = range(1, 1001)
>>> otuples = list( zip(*[iter(orange)]*10))
>>> print(otuples)
[(1, 2, 3, 4, 5, 6, 7, 8, 9, 10), ... (991, 992, 993, 994, 995, 996, 997, 998, 999, 1000)]
>>> olist = [list(i) for i in otuples]
>>> print(olist)
[[1, 2, 3, 4, 5, 6, 7, 8, 9, 10], ..., [991, 992, 993, 994, 995, 996, 997, 998, 999, 1000]]
>>> 

Python3


3
ดี แต่ลดลงองค์ประกอบในตอนท้ายถ้าขนาดไม่ตรงกับจำนวนทั้งหมดของชิ้นเช่นzip(*[iter(range(7))]*3)ผลตอบแทน[(0, 1, 2), (3, 4, 5)]และลืม6จากการป้อนข้อมูล
Alfe

6

เนื่องจากทุกคนที่นี่พูดถึงตัววนซ้ำ มีวิธีการที่สมบูรณ์แบบสำหรับการที่เรียกว่าboltonsiterutils.chunked_iter

from boltons import iterutils

list(iterutils.chunked_iter(list(range(50)), 11))

เอาท์พุท:

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

แต่ถ้าคุณไม่ต้องการที่จะเป็นความเมตตากับหน่วยความจำคุณสามารถใช้วิธีเก่าและเก็บเต็มรูปแบบในสถานที่แรกที่มีlistiterutils.chunked


และอันนี้ใช้งานได้จริงโดยไม่คำนึงถึงลำดับที่หนึ่งจะดูที่ตัวอ้างอิงย่อย !!
Peter Gerdes

6

ทางออกหนึ่งเพิ่มเติม

def make_chunks(data, chunk_size): 
    while data:
        chunk, data = data[:chunk_size], data[chunk_size:]
        yield chunk

>>> for chunk in make_chunks([1, 2, 3, 4, 5, 6, 7], 2):
...     print chunk
... 
[1, 2]
[3, 4]
[5, 6]
[7]
>>> 

5
def chunks(iterable,n):
    """assumes n is an integer>0
    """
    iterable=iter(iterable)
    while True:
        result=[]
        for i in range(n):
            try:
                a=next(iterable)
            except StopIteration:
                break
            else:
                result.append(a)
        if result:
            yield result
        else:
            break

g1=(i*i for i in range(10))
g2=chunks(g1,3)
print g2
'<generator object chunks at 0x0337B9B8>'
print list(g2)
'[[0, 1, 4], [9, 16, 25], [36, 49, 64], [81]]'

1
ในขณะที่สิ่งนี้อาจดูไม่สั้นหรือสวยกว่าการตอบสนองแบบ itertools หลายตัวที่ใช้งานได้จริงถ้าคุณต้องการพิมพ์รายการย่อยที่สองก่อนที่จะเข้าถึงรายการแรกนั่นคือคุณสามารถตั้งค่า i0 = next (g2); i1 = ถัดไป (G2); และใช้ i1 ก่อนที่จะใช้ i0 และมันก็ไม่แตก !!
Peter Gerdes

5

พิจารณาใช้matplotlib.cbookชิ้น

ตัวอย่างเช่น:

import matplotlib.cbook as cbook
segments = cbook.pieces(np.arange(20), 3)
for s in segments:
     print s

ดูเหมือนว่าคุณจะสร้างสองบัญชีโดยไม่ได้ตั้งใจ คุณสามารถติดต่อทีมเพื่อรวมเข้าด้วยกันซึ่งจะช่วยให้คุณได้รับสิทธิพิเศษในการแก้ไขโดยตรงจากการมีส่วนร่วมของคุณ
Georgy
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.