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
in 1st grade.  The
# 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  
 
 [ 5 post ] 

 Relevant Pages 

1. Making Code Neat

2. Clarion makes bad cdx index files, why?

3. Making sure that all indexes are open - 5.01

4. making even and odd index parameter?

5. Dynamic index and indexed variables

6. HELP ! corrupted index (in index key)

7. CDX Index file bloats up but shrink after re-indexing

8. Why INDEX ON &INDEX->FIEL

9. RubyChangeRequest #U002: new proper nameforHash#indexes, Array#indexes

10. RubyChangeRequest #U002: new proper namefor Hash#indexes, Array#indexes

11. Hash#index !==> Hash#indexes

12. Hash#index !==> Hash#indexes

 

 
Powered by phpBB® Forum Software