Simple List division problem

M

marcstuart

How do I divide a list into a set group of sublist's- if the list is
not evenly dividable ?
consider this example:

x = [1,2,3,4,5,6,7,8,9,10]
y = 3 # number of lists I want to break x into
z = y/x


what I would like to get is 3 sublists

print z[0] = [1,2,3]
print z[2] = [4,5,6]
print z[3] = [7,8,9,10]

obviously not even, one list will have 4 elements, the other 2 will
have 3.,
the overriding logic, is that I will get 3 lists and find a way for
python to try to break it evenly, if not one list can have a greater
amount of elements

Would I use itertools ? How would I do this ?

Thanks
 
M

marcstuart

I have gotten a little further, but not in the order of the original
list

def divide_list(lst, n):
return [lst[i::n] for i in range(n)]

x = [1,2,3,4,5,6,7,8,9,10]
y = 3
z = divide_list(x,y)

print z[0]
print z[1]
print z[2]


this prints:

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



closer, but I would like to maintain the original order of the list.
 
G

Gary Herron

marcstuart said:
How do I divide a list into a set group of sublist's- if the list is
not evenly dividable ?
consider this example:

x = [1,2,3,4,5,6,7,8,9,10]
y = 3 # number of lists I want to break x into
z = y/x


what I would like to get is 3 sublists

print z[0] = [1,2,3]
print z[2] = [4,5,6]
print z[3] = [7,8,9,10]

obviously not even, one list will have 4 elements, the other 2 will
have 3.,
the overriding logic, is that I will get 3 lists and find a way for
python to try to break it evenly, if not one list can have a greater
amount of elements

Would I use itertools ? How would I do this ?

Thanks

Calculate the size of a normal sublist, and the amount of extra that
goes with the last sublist.
Then extract y-1 normal sized sublists and one possibly larger sublist.
x = [1,2,3,4,5,6,7,8,9,10]
y = 3
s = len(x)/y
s # size of normal sublists 3
e = len(x) - y*s # extra elements for last sublist
e 1
z = [x[s*i:s*i+s] for i in range(y-1)] + [x[-s-e:]] #extract y-1 normal + 1 larger sublists
z
[[1, 2, 3], [4, 5, 6], [7, 8, 9, 10]]

Done!

Gary Herron
 
F

Fredrik Lundh

marcstuart said:
How do I divide a list into a set group of sublist's- if the list is
not evenly dividable ? consider this example:

x = [1,2,3,4,5,6,7,8,9,10]
y = 3 # number of lists I want to break x into
z = y/x

what I would like to get is 3 sublists

print z[0] = [1,2,3]
print z[2] = [4,5,6]
print z[3] = [7,8,9,10]

obviously not even, one list will have 4 elements, the other 2 will
have 3.,

here's one way to do it:

# chop it up
n = len(x) / y
z = [x[i:i+n] for i in xrange(0, len(x), n)]

# if the last piece is too short, add it to one before it
if len(z[-1]) < n and len(z) > 1:
z[-2].extend(z.pop(-1))

</F>
 
M

mensanator

How do I divide a list into a set group of sublist's- if the list is
not evenly dividable ?
consider this example:

x = [1,2,3,4,5,6,7,8,9,10]
y = 3      # number of lists I want to break x into
z = y/x

what I would like to get is 3 sublists

print z[0] = [1,2,3]
print z[2] = [4,5,6]
print z[3] = [7,8,9,10]

obviously not even, one list will have 4 elements, the other 2 will
have 3.,
the overriding logic, is that I will get 3 lists and find a way for
python to try to break it evenly, if not one list can have a greater
amount of elements

Would I use itertools ? How would I do this ?

Thanks

def list_split(x,y):
dm = divmod(len(x),y)
if dm[1] != 0:
z = [x[i*y:i*y+y] for i in xrange(len(x)/y) if len(x[i*y:])>=2*y]
z.append(x[(len(x)/y-1)*y:])
else:
z = [x[i*y:i*y+y] for i in xrange(len(x)/y)]
return z
list_split([1,2,3,4,5,6,7,8,9,10],3) [[1, 2, 3], [4, 5, 6], [7, 8, 9, 10]]
list_split([1,2,3,4,5,6,7,8,9,10],5) [[1, 2, 3, 4, 5], [6, 7, 8, 9, 10]]
list_split([1,2,3,4,5,6,7,8,9,10],4) [[1, 2, 3, 4], [5, 6, 7, 8, 9, 10]]
list_split([1,2,3,4,5,6,7,8,9,10],2)
[[1, 2], [3, 4], [5, 6], [7, 8], [9, 10]]
 
P

Paul Rubin

marcstuart said:
what I would like to get is 3 sublists

print z[0] = [1,2,3]
print z[2] = [4,5,6]
print z[3] = [7,8,9,10]

Are you SURE you want that? In almost every situation I've seen,

print z[0] = [1,2,3]
print z[2] = [4,5,6]
print z[3] = [7,8,9]
print z[4] = [10]

is preferable.
 
D

Dustan

marcstuart said:
what I would like to get is 3 sublists
print z[0] = [1,2,3]
print z[2] = [4,5,6]
print z[3] = [7,8,9,10]

Are you SURE you want that? In almost every situation I've seen,

print z[0] = [1,2,3]
print z[2] = [4,5,6]
print z[3] = [7,8,9]
print z[4] = [10]

is preferable.

Even more preferable is:

print z[0] = [1,2,3]
print z[1] = [4,5,6]
print z[2] = [7,8,9]
print z[3] = [10]
 
T

thebjorn

marcstuart said:
How do I divide a list into a set group of sublist's- if the list is
not evenly dividable ? consider this example:
x = [1,2,3,4,5,6,7,8,9,10]
y = 3 # number of lists I want to break x into
z = y/x
what I would like to get is 3 sublists
print z[0] = [1,2,3]
print z[2] = [4,5,6]
print z[3] = [7,8,9,10]
obviously not even, one list will have 4 elements, the other 2 will
have 3.,

here's one way to do it:

# chop it up
n = len(x) / y
z = [x[i:i+n] for i in xrange(0, len(x), n)]

# if the last piece is too short, add it to one before it
if len(z[-1]) < n and len(z) > 1:
z[-2].extend(z.pop(-1))

</F>

Eh...

def chop(lst, length):
n = len(lst) / length
z = [lst[i:i+n] for i in xrange(0, len(lst), n)]
if len(z[-1]) < n and len(z) > 1:
z[-2].extend(z.pop(-1))
return z

gives
chop(range(1,9), 3) [[1, 2], [3, 4], [5, 6], [7, 8]]
chop(range(1,8), 3) [[1, 2], [3, 4], [5, 6, 7]]
chop(range(1,6), 3) [[1], [2], [3], [4], [5]]
chop([1], 3)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "beforemeth.py", line 9, in chop
if len(z[-1]) < n and len(z) > 1:
ValueError: xrange() arg 3 must not be zero

Perhaps something like this?

def chop(lst, length):
from itertools import islice
it = iter(lst)
z = [list(islice(it, length)) for i in xrange(1 + len(lst) //
length)]
if len(z) > 1:
z[-2].extend(z.pop()) # the last item will be empty or contain
"overflow" elements.
return z

-- bjorn
 
T

thebjorn

marcstuart said:
How do I divide a list into a set group of sublist's- if the list is
not evenly dividable ? consider this example:
x = [1,2,3,4,5,6,7,8,9,10]
y = 3 # number of lists I want to break x into
z = y/x
what I would like to get is 3 sublists
print z[0] = [1,2,3]
print z[2] = [4,5,6]
print z[3] = [7,8,9,10]
obviously not even, one list will have 4 elements, the other 2 will
have 3.,
here's one way to do it:
# chop it up
n = len(x) / y
z = [x[i:i+n] for i in xrange(0, len(x), n)]
# if the last piece is too short, add it to one before it
if len(z[-1]) < n and len(z) > 1:
z[-2].extend(z.pop(-1))

Eh...

def chop(lst, length):
n = len(lst) / length
z = [lst[i:i+n] for i in xrange(0, len(lst), n)]
if len(z[-1]) < n and len(z) > 1:
z[-2].extend(z.pop(-1))
return z

gives

[[1, 2], [3, 4], [5, 6], [7, 8]]>>> chop(range(1,8), 3)

[[1, 2], [3, 4], [5, 6, 7]]>>> chop(range(1,6), 3)

[[1], [2], [3], [4], [5]]>>> chop([1], 3)

Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "beforemeth.py", line 9, in chop
if len(z[-1]) < n and len(z) > 1:
ValueError: xrange() arg 3 must not be zero

Perhaps something like this?

def chop(lst, length):
from itertools import islice
it = iter(lst)
z = [list(islice(it, length)) for i in xrange(1 + len(lst) //
length)]
if len(z) > 1:
z[-2].extend(z.pop()) # the last item will be empty or contain
"overflow" elements.
return z

-- bjorn

Bad for to reply to myself, I know, but I just realized that the OP
wanted to control the _number_ of chunks, not the size of the
chunks... Building on the above would give something like

from itertools import islice
from operator import add

def chop(lst, nchunks):
chunksize, extra = divmod(len(lst), nchunks)
if not chunksize:
raise ValueError('More chunks than elements in list.')
it = iter(lst)
z = [list(islice(it, chunksize)) for i in xrange(nchunks + extra)]
z, extra = z[:nchunks], z[nchunks:]
z[-1].extend(reduce(add, extra, [])) # because sum ain't add :-(
return z

-- bjorn
 
F

Fredrik Lundh

thebjorn said:

oh, forgot that it was "pulling requirements out of thin air" week on
c.l.python.
def chop(lst, length):
n = len(lst) / length
z = [lst[i:i+n] for i in xrange(0, len(lst), n)]
if len(z[-1]) < n and len(z) > 1:
z[-2].extend(z.pop(-1))
return z

gives
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "beforemeth.py", line 9, in chop
if len(z[-1]) < n and len(z) > 1:
ValueError: xrange() arg 3 must not be zero

well, it doesn't. there's no xrange on that line.
> Perhaps something like this?
> from itertools import islice

or just use an if-statement, or the max function. but I guess those
tools are too old and boring for c.l.python these days...

</F>
 
P

Paul Rubin

thebjorn said:
Perhaps something like this?

def chop(lst, length):
from itertools import islice
it = iter(lst)
z = [list(islice(it, length)) for i in xrange(1 + len(lst) // length)]
if len(z) > 1:
z[-2].extend(z.pop()) # the last item will be empty or contain "overflow" elements.
return z

def chop(lst, length):
def chop1():
t = len(lst) // length - 1
for i in xrange(t):
yield lst[i*length: (i+1)*length]
yield lst[t*length:]
return list(chop1())
 
T

thebjorn

oh, forgot that it was "pulling requirements out of thin air" week on
c.l.python.

Well, the OP requirements were to control the number of chunks, not
the size of them, so I guess we both got it wrong initially.
def chop(lst, length):
n = len(lst) / length
z = [lst[i:i+n] for i in xrange(0, len(lst), n)]
if len(z[-1]) < n and len(z) > 1:
z[-2].extend(z.pop(-1))
return z
gives
chop([1], 3)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "beforemeth.py", line 9, in chop
if len(z[-1]) < n and len(z) > 1:
ValueError: xrange() arg 3 must not be zero

well, it doesn't. there's no xrange on that line.

It's from this line

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

(I executed the file with python -i beforemeth.py to get to an
interactive prompt, I'm sure you're familiar with the technique. You
could have just debugged your own program to find it though, or just
looked at the code -- not that many xrange calls in there, eh?)
or just use an if-statement, or the max function. but I guess those
tools are too old and boring for c.l.python these days...

I didn't realize correct code was too new-school. Perhaps you should
publish a list of modules you don't like, or perhaps I should just use
a sufficiently venerable version of Python? Ok, here you go:

C:\Python22>python
'import site' failed; use -v for traceback
Python 2.2.3 (#42, May 30 2003, 18:12:08) [MSC 32 bit (Intel)] on
win32
Type "help", "copyright", "credits" or "license" for more information..... chunksize = len(lst) // nchunks
.... if not chunksize:
.... raise ValueError('More chunks than elements in list.')
.... res = []
.... begin, end = 0, chunksize
.... for i in range(nchunks-1):
.... res.append(lst[begin:end])
.... begin, end = end, end+chunksize
.... res.append(lst[begin:])
.... return res
....
chop2(range(1,6), 2) [[1, 2], [3, 4, 5]]
chop2(range(1,6), 3) [[1], [2], [3, 4, 5]]
chop2(range(1,11), 3) [[1, 2, 3], [4, 5, 6], [7, 8, 9, 10]]

Sufficiently old-school (or do I need to take out the // division
also?)

Shall we perhaps drop some of the attitude? (you used to be so much
nicer before you wrote sre ;-)

-- bjorn
 
P

Pierre Quentel

How do I divide a list into a set group of sublist's- if the list is
not evenly dividable ?
consider this example:

x = [1,2,3,4,5,6,7,8,9,10]
y = 3 # number of lists I want to break x into
z = y/x

what I would like to get is 3 sublists

print z[0] = [1,2,3]
print z[2] = [4,5,6]
print z[3] = [7,8,9,10]

obviously not even, one list will have 4 elements, the other 2 will
have 3.,
the overriding logic, is that I will get 3 lists and find a way for
python to try to break it evenly, if not one list can have a greater
amount of elements

Would I use itertools ? How would I do this ?

Thanks

Hi,

If you want to split the list in 4, do you want
[1,2],[3,4],[5,6],[7,8,9,10] : all extra items in the last sublist
or
[1,2],[3,4],[5,6,7],[8,9,10] : one extra item in each of the last
sublists ?

Assuming you want the second version :

=======================
def split_list(lst,nb):
# ln = length of smaller sublists
# extra = number of longer sublists (they have ln+1 items)
ln,extra = divmod(len(lst),nb)
pos = ln*(nb-extra) # position where larger sublists begin
return [ lst[i*ln:(i+1)*ln] for i in xrange(nb-extra) ] \
+ [lst[pos+i*(ln+1):pos+(i+1)*(ln+1)] for i in xrange(extra)]

======================

x = range(1,11)

print split_list(x,1)
[[1, 2, 3, 4, 5, 6, 7, 8, 9, 10]] print split_list(x,2)
[[1, 2, 3, 4, 5], [6, 7, 8, 9, 10]] print split_list(x,3)
[[1, 2, 3], [4, 5, 6], [7, 8, 9, 10]] print split_list(x,4)
[[1, 2], [3, 4], [5, 6, 7], [8, 9, 10]] print split_list(x,5)
[[1, 2], [3, 4], [5, 6], [7, 8], [9, 10]] print split_list(x,10)
[[1], [2], [3], [4], [5], [6], [7], [8], [9], [10]]
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

Forum statistics

Threads
473,764
Messages
2,569,567
Members
45,041
Latest member
RomeoFarnh

Latest Threads

Top