Velocity Reviews > a sequence question

# a sequence question

Chris Wright
Guest
Posts: n/a

 01-28-2005
Hi,

1) I want to iterate over a list "N at a time"
sort of like:

# Two at a time... won't work, obviously

>>> for a, b in [1,2,3,4]:

.... print a,b
....
Traceback (most recent call last):
File "<interactive input>", line 1, in ?
TypeError: unpack non-sequence
>>>

Is there a nifty way to do with with list comprehensions,
or do I just have to loop over the list ?

cheers and thanks

chris wright

Diez B. Roggisch
Guest
Posts: n/a

 01-28-2005
l = [1,2,3,4]

for a, b in zip(l[::2], l[1::2]):
print a,b

--
Regards,

Diez B. Roggisch

Roy Smith
Guest
Posts: n/a

 01-28-2005
In article <lzrKd.138680\$(E-Mail Removed)>,
Chris Wright <(E-Mail Removed)> wrote:

> Hi,
>
> 1) I want to iterate over a list "N at a time"

You could do it with slicing and zip:

>>> l = [1, 2, 3, 4, 5, 6, 7, 8]
>>> zip (l[::2], l[1::2])

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

To my eyes, that's a bit cryptic, but it works and it's certainly
compact. I don't use either zip() or extended slicing a lot; perhaps if
I used them more often, the above would be more obvious to me if I read
it in somebody else's code.

The interesting thing would be generalizing this to the "N at a time"
case. I think this works:

def nzip (list0, n):
args = []
for i in range(n):
slice = list0[i::n]
args.append (slice)
return zip (*args)

l = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12]
print nzip (l, 3)

Roy-Smiths-Computerlay\$ ./nzip.py
[(1, 2, 3), (4, 5, 6), (7, 8, 9), (10, 11, 12)]

but I haven't given any thought to what happens if the length of the
list isn't a multiple of n (exercise for the reader). It's also
annoying that the above generates a bunch of temporary lists. It would
be cool if there was a way to have the intermediates be generator
expressions, but I'm not that good with that stuff, so I'll leave that
as an exercise for other readers

F. Petitjean
Guest
Posts: n/a

 01-28-2005
Le Fri, 28 Jan 2005 13:59:45 GMT, Chris Wright a écrit :
> Hi,
>
> 1) I want to iterate over a list "N at a time"
>
>
> Is there a nifty way to do with with list comprehensions,
> or do I just have to loop over the list ?
>
> cheers and thanks

seq = xrange(1, 9) # an iterable [1, 2, ... 8]
N = 2
it = (iter(seq,)*N # a tuple containing N times the *same* iterator on
seq
print zip(*it) # the list you are after
from itertools import izip
help(izip)
it = (iter(seq),)*2
for tup in izip(*it):
print tup
>
> chris wright

Duncan Booth
Guest
Posts: n/a

 01-28-2005
Chris Wright wrote:

> 1) I want to iterate over a list "N at a time"
> sort of like:
>
> # Two at a time... won't work, obviously
>
> >>> for a, b in [1,2,3,4]:

> ... print a,b
> ...

Try this:

l = [1, 2, 3, 4]
for a, b in zip(*[iter(l)]*2):
print a, b

zip(*[iter(seq)]*N) will group by N (but if there are any odd items at the
end it will ignore them).

map(None, *[iter(seq)]*N) will group by N padding the last item with None
if it needs to.

Michael Hartl
Guest
Posts: n/a

 01-28-2005
For problems like this I use a partition function defined in a utils.py
file that I use (based on Peter Norvig's utils file at
http://aima.cs.berkeley.edu/python/utils.py). Using partition, the
problem you posed can be solved by writing

#for a, b in partition([1, 2, 3, 4], 2):
# print a, b

The implementation of partition I use is simple-minded; the previous
posts in this thread suggest some more sophisticated ways to attack it
using generators.

#def partition(seq, partsize):
# """Partition a sequence into subsequences of length partsize."""
# ls = len(seq)
# assert ls % partsize == 0, ('length %s, partition size %s\n'
# % (ls, partsize))
# return [seq[ii+partsize)] for i in range(0, ls, partsize)]
Michael

Nick Coghlan
Guest
Posts: n/a

 01-29-2005
Duncan Booth wrote:
> Try this:
>
> l = [1, 2, 3, 4]
> for a, b in zip(*[iter(l)]*2):
> print a, b
>
> zip(*[iter(seq)]*N) will group by N (but if there are any odd items at the
> end it will ignore them).
>
> map(None, *[iter(seq)]*N) will group by N padding the last item with None
> if it needs to.

For anyone else who was as bemused as I was that Duncan's and F. Petitjean's
suggestions actually *work*, this was what I had to do to figure out *why* they
work:

Py> l = [1, 2, 3, 4]
Py> itr = iter(l)
Py> zip(itr) # Put all items from iterator in position 1
[(1,), (2,), (3,), (4,)]
Py> itr = iter(l)
Py> zip(itr, itr) # Put every second item in position 2
[(1, 2), (3, 4)]

Using zip(*[iter(l)]*N) or zip(*(iter(l),)*N) simply extends the above to the
general case.

I'd definitely recommend hiding this trick inside a function. Perhaps something
like (using Michael's function name):

from itertools import izip, repeat, chain

def partition(seq, part_len):
return izip(*((iter(seq),) * part_len))

itr = iter(seq)
if (len(seq) % part_len != 0):
return izip(*((itr,) * part_len))

Py> list(partition(range(10), 2))
[(0, 1), (2, 3), (4, 5), (6, 7), (8, 9)]
Py> list(partition(range(10), 3))
[(0, 1, 2), (3, 4, 5), (6, 7, ]
[(0, 1), (2, 3), (4, 5), (6, 7), (8, 9)]
[(0, 1, 2), (3, 4, 5), (6, 7, , (9, None, None)]
[(0, 1, 2), (3, 4, 5), (6, 7, , (9, False, False)]
[(0, 3, 6, 9), (1, 4, 7, None), (2, 5, 8, None)]

Not sure how useful that last example is, but I thought it was cute

Cheers,
Nick.

--
Nick Coghlan | http://www.velocityreviews.com/forums/(E-Mail Removed) | Brisbane, Australia
---------------------------------------------------------------
http://boredomandlaziness.skystorm.net

gene.tani@gmail.com
Guest
Posts: n/a

Steven Bethard
Guest
Posts: n/a

 02-01-2005
Nick Coghlan wrote:
> I'd definitely recommend hiding this trick inside a function. Perhaps
> something like (using Michael's function name):
>
> from itertools import izip, repeat, chain
>
> def partition(seq, part_len):
> return izip(*((iter(seq),) * part_len))
>
> itr = iter(seq)
> if (len(seq) % part_len != 0):
> return izip(*((itr,) * part_len))

I think you can write that second one so that it works for iterables
without a __len__:

.... itr = itertools.chain(
.... iter(iterable), itertools.repeat(pad_val, part_len - 1))
.... return itertools.izip(*[itr]*part_len)
....
[(0, 1), (2, 3), (4, 5), (6, 7), (8, 9)]
[(0, 1, 2), (3, 4, 5), (6, 7, , (9, None, None)]

I just unconditionally pad the iterable with 1 less than the partition
size... I think that works right, but I haven't tested it any more than
what's shown.

Steve

todddeluca@gmail.com
Guest
Posts: n/a

 02-01-2005

Chris Wright wrote:
> Hi,
>
> 1) I want to iterate over a list "N at a time"
> sort of like:
>
> # Two at a time... won't work, obviously
>
> >>> for a, b in [1,2,3,4]:

> ... print a,b
> ...
> Traceback (most recent call last):
> File "<interactive input>", line 1, in ?
> TypeError: unpack non-sequence
> >>>

>
>
> Is there a nifty way to do with with list comprehensions,
> or do I just have to loop over the list ?
>
> cheers and thanks
>
> chris wright

I wouldn't call this nifty, but it does use list comprehensions:
(n-(len(l)%n))%n is the amount of padding
(len(l)+(n-(len(l)%n))%n)/n is the number of groups (calculated by
adding the padding to the length of l and then dividing by n)

>>> l = range(10)
>>> n = 3
>>> [(l+[None]*((n-(len(l)%n))%n))[i*ni+1)*n] for i in

xrange((len(l)+(n-(len(l)%n))%n)/n)]
[[0, 1, 2], [3, 4, 5], [6, 7, 8], [9, None, None]]

Regards,
Todd