|
Title: Split a list into roughly equal-sized pieces
Submitter: Nick Matsakis
(other recipes)
Last Updated: 2005/06/10
Version no: 1.0
Category:
|
|
|
Description:
Break a list into roughly equal sized pieces.
Source: Text Source
def split_seq(seq, size):
newseq = []
splitsize = 1.0/size*len(seq)
for i in range(size):
newseq.append(seq[int(round(i*splitsize)):int(round((i+1)*splitsize))])
return newseq
Discussion:
Inspired by http://aspn.activestate.com/ASPN/Cookbook/Python/Recipe/425044
This requires that you know the length of the list beforehand, of course, so you can't use it with an arbitrary sequence as is. It's simple, but it's easy to create fencepost errors when implementing it.
>>> split_seq(range(10), 3)
[[0, 1, 2], [3, 4, 5, 6], [7, 8, 9]]
|
|
Add comment
|
|
Number of comments: 5
Naming suggestion, Jeremy Dunck, 2005/06/21
The second parameter, "size", would be better named "numPieces", since split_seq([]..., x) always returns a list with x pieces.
>>> split_seq(range(10), 1)
[[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]]
>>> split_seq(range(10), 2)
[[0, 1, 2, 3, 4], [5, 6, 7, 8, 9]]
Add comment
All integer approach, Paul Watson, 2005/06/22
Here is an all integer method that builds a list of ranges first, then splices the sequence.
def split_seq(seq, numpieces):
seqlen = len(seq)
d, m = divmod(seqlen, numpieces)
rlist = range(0, ((d + 1) * (m + 1)), (d + 1))
if d != 0: rlist += range(rlist[-1] + d, seqlen, d) + [seqlen]
newseq = []
for i in range(len(rlist) - 1):
newseq.append(seq[rlist[i]:rlist[i + 1]])
newseq += [[]] * max(0, (numpieces - seqlen))
return newseq
Add comment
alternate implementation using integers, Greg Jorgensen, 2005/06/22
This version uses integer math and distributes the remaindered items evenly over the first few splits.
def split_seq(seq, p):
newseq = []
n = len(seq) / p # min items per subsequence
r = len(seq) % p # remaindered items
b,e = 0, n + min(1, r) # first split
for i in range(p):
newseq.append(seq[b:e])
r = max(0, r-1) # use up remainders
b,e = e, e + n + min(1, r) # min(1,r) is always 0 or 1
return newseq
>>> split_seq(range(10), 3)
[[0, 1, 2, 3], [4, 5, 6], [7, 8, 9]]
>>> split_seq(range(11), 3)
[[0, 1, 2, 3], [4, 5, 6, 7], [8, 9, 10]]
>>> split_seq(range(10), 4)
[[0, 1, 2], [3, 4, 5], [6, 7], [8, 9]]
Add comment
again, an integer approach, Marc Keller, 2005/06/23
def splitCeil(seq, m):
"""Distribute the seq elements in lists in m groups
according to quasi equitative distribution (decreasing order):
splitCeil(range(13), 4) --> seq = range(13), m=4
result : [[0, 1, 2, 3], [4, 5, 6], [7, 8, 9], [10, 11, 12]]
"""
n,b,newseq=len(seq),0,[]
for k in range(m):
q,r=divmod(n-k,m)
a, b = b, b + q + (r!=0)
newseq.append(seq[a:b])
return newseq
def splitFloor(seq, m):
"""Distribute the seq elements in lists in m groups
according to quasi equitative distribution (increasing order):
seq = range(13), m=4
result : [[0, 1, 2], [3, 4, 5], [6, 7, 8], [9, 10, 11, 12]]
"""
n,b,newseq=len(seq),0,[]
for k in range(m):
a, b = b, b + (n+k)//m
newseq.append(seq[a:b])
return newseq
Add comment
Sebastian Hempel, 2007/07/18
Another approach using slicing for the calculation of the list lengths.
def slice_it(li, cols=2):
start = 0
for i in xrange(cols):
stop = start + len(li[i::cols])
yield li[start:stop]
start = stop
Add comment
|
|
|
|