3

I am stuck on a problem involving splitting up a list into different sized chunks. I want to have a list that is randomly split into either pairs of 2 or 3.

example:

L = [1,1,1,1,1,1,1,1,1,1,1,1]

and I want to get something out like:

L2 = [(1,1,1),(1,1),(1,1),(1,1,1),(1,1)]

but this I want to be random, so that the distribution of pairs and triplets changes everytime the code is ran.

Georgy
  • 12,464
  • 7
  • 65
  • 73
Maple123
  • 79
  • 5
  • 5
    Possible duplicate of [Best way to split a list into randomly sized chunks?](http://stackoverflow.com/questions/21439011/best-way-to-split-a-list-into-randomly-sized-chunks) – Billal Begueradj Apr 08 '16 at 11:49
  • The answers there are quite elegant. But for this specific case, they sometimes lead to a chunk containing 1 element when the list can be split into multiples of 2s and 3s alone. For ex : The accepted answer there gave `[[1, 1], [1, 1, 1], [1, 1, 1], [1, 1, 1], [1]]` for L2 in one of the iterations when run with `min_chunk=2` and `maxchunk=3` – Vasanth Apr 08 '16 at 12:51

5 Answers5

2

As a more general approach you can use following function:

from itertools import count
import random
def my_split(lst, chunks):
    def chunk_creator():
        total = 0
        while total <= len(lst):
            x = random.choice(chunks)
            yield L[total: x + total]
            total += x
        yield total - x

    def chunk_finder():
        for _ in count():
            chunk = list(chunk_creator())
            total = chunk.pop(-1)
            if total == len(L):
                return chunk[:-1]
    if max(chunks) <= len(L):
        return chunk_finder()
    else:
        return None

Demo :

>>> L = [1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1] 
>>> my_split(L, (2, 3))
... [[1, 1], [1, 1], [1, 1], [1, 1, 1], [1, 1, 1]]
>>> my_split(L, (2, 3))
... [[1, 1, 1], [1, 1, 1], [1, 1, 1], [1, 1, 1]]

Explanation:

This function is consist of to sub functions. First one is chunk_creator which it's job is creating the desire chunks based on the length of your list and return them as an iterator. Note that, the end value is the total variable which is the sum of preceding chunks.

The second function (chunk_finder) will find the desire chunks for us by goring through an in infinity loop (itertools.count()) and checking if the value of total is equal with the length of input list.

Mazdak
  • 105,000
  • 18
  • 159
  • 188
1

There are many ways to solve this, maybe you can write a generator that returns chunks of sizes based on a size function:

import random

def take_chunked(i, size):
    idx = 0
    while idx < len(i):
        s = size(i[idx:])
        yield i[idx:idx+s]
        idx += s

def size_fun(i):
    if len(i) == 4:
        return 2
    if len(i) <= 3:
        return len(i)

    return random.randint(2,3)

Output:

>>> list(take_chunked("helloworld", size_fun))
['he', 'll', 'owo', 'rld']
>>> list(take_chunked("helloworld", size_fun))
['hel', 'low', 'or', 'ld']
>>> list(take_chunked("a", size_fun))
['a']
>>> list(take_chunked("", size_fun))
[]

This version will guarantee that the chunk sizes are either 2 or 3, as long as there are enough items in the list.

André Laszlo
  • 15,169
  • 3
  • 63
  • 81
0

Factorize len(L) into chunks of 2s and 3s and then use a loop to divide the list.

import numpy as np

def rand23():
    return np.random.randint(2,4)

def get_chunk(sz):
    rem = sz
    ch = []
    while ( rem > 0 ):
        if ( rem <= 3 ): #if <= 3 add what is left in the chunk (exit condition)
            ch.append(rem)
            rem = 0
            break
        elif ( rem == 4 ): #4 is the only edge case here
            ch.append(2)
            ch.append(2)
            rem = 0
            break
        else:
            ch.append(rand23())
            rem -= ch[-1]

    return ch

L = [1,1,1,1,1,1,1,1,1,1,1,1]
ch = get_chunk(len(L))
L2 = []
count = 0
#Divide the list into chunks based on ch
for c in ch:
    L2.append(tuple(L[count:count+c]))
    count += c

print L2

Results:( each line is output of an iteration )

[(1, 1, 1), (1, 1), (1, 1), (1, 1), (1, 1, 1)]

[(1, 1), (1, 1), (1, 1), (1, 1), (1, 1), (1, 1)]

[(1, 1, 1), (1, 1, 1), (1, 1, 1), (1, 1, 1)]

[(1, 1), (1, 1), (1, 1, 1), (1, 1, 1), (1, 1)]

[(1, 1, 1), (1, 1), (1, 1), (1, 1), (1, 1, 1)]

[(1, 1, 1), (1, 1, 1), (1, 1), (1, 1), (1, 1)]

[(1, 1), (1, 1), (1, 1, 1), (1, 1), (1, 1, 1)]

PS : You can also implement get_chunk() recursively.

Vasanth
  • 1,238
  • 10
  • 14
0

Hope this helps.

step1: use random module to generate a random number

step2: use random number to decide if it should be pair or 2(for even random number)/3(if random is nt even).

step3: code it.

from random import random

def selector():
    s = int(random() * 100 )
    return (s/2) == 0

L = [1 for i in range(30)]
L2 = []

while L:
    if selector():
        tmp = 2
    else:
        tmp = 3
    #print tmp
    if tmp > 0 and len(L) >= tmp:
        L2.append( [ L.pop() for i in range(tmp)] )
    if len(L) < 3:
        L2.append(set(L))
        L = []

print L, L2
sam
  • 1,819
  • 1
  • 18
  • 30
0

This one is fairly simple and will do the trick :-)

import random

L = [1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1]
L2 = list()

i = 0
j = random.randint(2, 3)

while i < len(L):
    chunk = L[i:j]
    L2.append(chunk)
    i = j
    if len(L) - i == 4:  # case: 4 elements left in L
        j = i + 2
    elif len(L) - i < 4:  # case: 2 or 3 elements left in L
        j = len(L)
    else:
        j = i + random.randint(2, 3)

print L
print L2
Boregore
  • 207
  • 1
  • 11