Neater way of making indexes?
Author Message
Neater way of making indexes?

The function returns a list of ordered indexes for nested for-loops of
given input ranges.

Quote:
>>> from itertools import *
>>> def indexes(upto):

multi=lambda alist:reduce(lambda x,y:x*y,alist,1)
g=[ cycle(chain(*[repeat(each,multi(upto[i+1:])) for each in
range(upto[i])]))
for i in range(len(upto))]
return list(islice(izip(*g),multi(upto)))

Quote:
>>> indexes((4,))

[(0,), (1,), (2,), (3,)]
Quote:
>>> indexes((3,2,4))

[(0, 0, 0), (0, 0, 1), (0, 0, 2), (0, 0, 3), (0, 1, 0), (0, 1, 1), (0,
1, 2), (0, 1, 3), (1, 0, 0), (1, 0, 1), (1, 0, 2), (1, 0, 3), (1, 1,
0), (1, 1, 1), (1, 1, 2), (1, 1, 3), (2, 0, 0), (2, 0, 1), (2, 0, 2),
(2, 0, 3), (2, 1, 0), (2, 1, 1), (2, 1, 2), (2, 1, 3)]

I don't like this code too much. Any suggestions?

Jane.

Wed, 16 Nov 2005 09:09:20 GMT
Neater way of making indexes?
Have a look at this, it's slightly different from your example in the
way it is called, and it's a generator since these lists can get quite
large.  It's also longer, but should be more readable, and hopefully
more understandable.  Your method may be faster since you seem to use
more builtin looping functions.  I didn't attempt to profile them.

from __future__ import generators
import operator

# Think of indices as a len(indices) digit number where each digit is of
a different base.
# The base of the number in position i is limits[i].  The next method
basically increments
# indices by one and takes into account carrying, just like you learned
# rightmost digit is incremented by one, and if it exceeds its base,
then it is reset to zero,
# and a 1 is "carried" to the digit to the left.  This repeats until a
digit can be incremented
# without exceeding its base.
def next(indices, limits):
done = False
index = len(indices)-1

while not done and index >= 0:
indices[index] += 1

if indices[index] >= limits[index]:
indices[index] = 0
index -= 1
else:
done = True

# This is just a simple wrapper method around the next method in order
to make the
# interface similar to the one you presented.  If you don't want this to
be a generator
# then replace the yield statement with an append to a list collecting
the results.
def indexes(*limits):        # you probably want to call this the proper
plural of index, indices
indices = [0]*len(limits)
num = reduce(operator.mul, limits, 1)
while num > 0:
yield tuple(indices)
next(indices, limits)
num -= 1

Quote:
>>> indexes(4)

<generator object at 0x008AB1D8>
Quote:
>>> list(indexes(4))

[(0,), (1,), (2,), (3,)]
Quote:
>>> list(indexes(3, 2, 4))

[(0, 0, 0), (0, 0, 1), (0, 0, 2), (0, 0, 3), (0, 1, 0), ..., (2, 1, 2),
(2, 1, 3)]

Quote:
> The function returns a list of ordered indexes for nested for-loops of
> given input ranges.

> >>> from itertools import *
> >>> def indexes(upto):
>     multi=lambda alist:reduce(lambda x,y:x*y,alist,1)
>     g=[ cycle(chain(*[repeat(each,multi(upto[i+1:])) for each in
> range(upto[i])]))
>         for i in range(len(upto))]
>     return list(islice(izip(*g),multi(upto)))

> >>> indexes((4,))
> [(0,), (1,), (2,), (3,)]
> >>> indexes((3,2,4))
> [(0, 0, 0), (0, 0, 1), (0, 0, 2), (0, 0, 3), (0, 1, 0), (0, 1, 1), (0,
> 1, 2), (0, 1, 3), (1, 0, 0), (1, 0, 1), (1, 0, 2), (1, 0, 3), (1, 1,
> 0), (1, 1, 1), (1, 1, 2), (1, 1, 3), (2, 0, 0), (2, 0, 1), (2, 0, 2),
> (2, 0, 3), (2, 1, 0), (2, 1, 1), (2, 1, 2), (2, 1, 3)]

> I don't like this code too much. Any suggestions?

> Jane.

Wed, 16 Nov 2005 11:51:54 GMT
Neater way of making indexes?
Quoth Jane Austine:

Quote:
> The function returns a list of ordered indexes for nested for-loops of
> given input ranges.

> >>> from itertools import *
> >>> def indexes(upto):
>     multi=lambda alist:reduce(lambda x,y:x*y,alist,1)
>     g=[ cycle(chain(*[repeat(each,multi(upto[i+1:])) for each in
> range(upto[i])]))
>         for i in range(len(upto))]
>     return list(islice(izip(*g),multi(upto)))

> >>> indexes((4,))
> [(0,), (1,), (2,), (3,)]
> >>> indexes((3,2,4))
> [(0, 0, 0), (0, 0, 1), (0, 0, 2), (0, 0, 3), (0, 1, 0), (0, 1, 1), (0,
> 1, 2), (0, 1, 3), (1, 0, 0), (1, 0, 1), (1, 0, 2), (1, 0, 3), (1, 1,
> 0), (1, 1, 1), (1, 1, 2), (1, 1, 3), (2, 0, 0), (2, 0, 1), (2, 0, 2),
> (2, 0, 3), (2, 1, 0), (2, 1, 1), (2, 1, 2), (2, 1, 3)]

> I don't like this code too much. Any suggestions?

def indices(*limits):
ind = [0]*len(limits)
while True:
yield tuple(ind)
for i in range(len(limits)-1, -1, -1):
ind[i] += 1
if ind[i] < limits[i]:
break
ind[i] = 0
else:
break

This method is just counting with varying bases, carries as
appropriate, stopping when the carry goes past the most
significant digit.

There's a recipe for a slightly more general problem in the python
Cookbook:

<http://aspn.activestate.com/ASPN/Cookbook/Python/Recipe/159975>

I can't recommend the original version, but Raymond Hettinger's
suggested simplification is quite easy on the eyes.

--
Steven Taschuk             "The world will end if you get this wrong."

Brian Kernighan and Lorrinda Cherry

Wed, 16 Nov 2005 12:23:14 GMT
Neater way of making indexes?

Quote:
>The function returns a list of ordered indexes for nested for-loops of
>given input ranges.

>>>> from itertools import *
>>>> def indexes(upto):
>    multi=lambda alist:reduce(lambda x,y:x*y,alist,1)
>    g=[ cycle(chain(*[repeat(each,multi(upto[i+1:])) for each in
>range(upto[i])]))
>        for i in range(len(upto))]
>    return list(islice(izip(*g),multi(upto)))

>>>> indexes((4,))
>[(0,), (1,), (2,), (3,)]
>>>> indexes((3,2,4))
>[(0, 0, 0), (0, 0, 1), (0, 0, 2), (0, 0, 3), (0, 1, 0), (0, 1, 1), (0,
>1, 2), (0, 1, 3), (1, 0, 0), (1, 0, 1), (1, 0, 2), (1, 0, 3), (1, 1,
>0), (1, 1, 1), (1, 1, 2), (1, 1, 3), (2, 0, 0), (2, 0, 1), (2, 0, 2),
>(2, 0, 3), (2, 1, 0), (2, 1, 1), (2, 1, 2), (2, 1, 3)]

>I don't like this code too much. Any suggestions?

>>> def indexes(*tops):
...     def tuto(tups, *tops):
...         if not tops: return tups
...         return tuto([(i,)+ t for i in xrange(tops[-1]) for t in tups], *tops[:-1])
...     return tuto([()], *tops)
...
>>> indexes(2,3)
[(0, 0), (0, 1), (0, 2), (1, 0), (1, 1), (1, 2)]
>>> for t in indexes(2,3,4): print t
...
(0, 0, 0)
(0, 0, 1)
(0, 0, 2)
(0, 0, 3)
(0, 1, 0)
(0, 1, 1)
(0, 1, 2)
(0, 1, 3)
(0, 2, 0)
(0, 2, 1)
(0, 2, 2)
(0, 2, 3)
(1, 0, 0)
(1, 0, 1)
(1, 0, 2)
(1, 0, 3)
(1, 1, 0)
(1, 1, 1)
(1, 1, 2)
(1, 1, 3)
(1, 2, 0)
(1, 2, 1)
(1, 2, 2)
(1, 2, 3)

Regards,
Bengt Richter

Wed, 16 Nov 2005 16:13:45 GMT
Neater way of making indexes?

<snip unliked code>

Quote:
>I don't like this code too much. Any suggestions?

Repeat this mantra three times:

"the alternatives!, the alternatives!, the alternatives!"

Anton

def indexes(index,bases):
res, remain = [], index
for base in bases[::-1]:
remain, i = divmod(remain, base)
res.append(i)
res.reverse()
return tuple(res)

def test():
from operator import mul
bases = [3,2,4]
n = reduce(mul,bases)
for i in range(n):
print indexes(i,bases),
if not (i+1) % 4: print

if __name__=='__main__':
test()

output:

(0, 0, 0) (0, 0, 1) (0, 0, 2) (0, 0, 3)
(0, 1, 0) (0, 1, 1) (0, 1, 2) (0, 1, 3)
(1, 0, 0) (1, 0, 1) (1, 0, 2) (1, 0, 3)
(1, 1, 0) (1, 1, 1) (1, 1, 2) (1, 1, 3)
(2, 0, 0) (2, 0, 1) (2, 0, 2) (2, 0, 3)
(2, 1, 0) (2, 1, 1) (2, 1, 2) (2, 1, 3)

Wed, 16 Nov 2005 18:18:59 GMT

 Page 1 of 1 [ 5 post ]

Relevant Pages