ActiveState Code

Recipe 425397: Split a list into roughly equal-sized pieces


Break a list into roughly equal sized pieces.

Python
1
2
3
4
5
6
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]]

Comments

  1. 1. At 9:13 a.m. on 21 jun 2005, Jeremy Dunck said:

    Naming suggestion. 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]]

  2. 2. At 8:06 a.m. on 22 jun 2005, Paul Watson said:

    All integer approach. 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
    
  3. 3. At 4:30 p.m. on 22 jun 2005, Greg Jorgensen said:

    alternate implementation using integers. 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]]
    
  4. 4. At 3:50 a.m. on 23 jun 2005, Marc Keller said:

    again, an integer approach. 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
    
  5. 5. At 3:04 a.m. on 18 jul 2007, Sebastian Hempel said:

    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
    

Sign in to comment