array manipulation without for loops

Alex Martelli aleax at mac.com
Sun Jun 25 17:18:25 EDT 2006


Sheldon <shejo284 at gmail.com> wrote:

> The following script (using your function) raised no exception so it
> worked! Elegant Alex, thanx.
> 
> res = equalize_arrays(msgtmp,ppstmp,255) # class
>             (ppstmp,msgtmp) = res.equalize() # class method
>             for i in range(int(main.xsize)):
>                 for j in range(int(main.ysize)):
>                     if msgtmp[i,j] == 255 and ppstmp[i,j] != 255:
>                         raise "equalize error!"
>                     if ppstmp[i,j] == 255 and msgtmp[i,j] != 255:
>                         raise "equalize error!"
> I read up on the putmask function and I don't understand this part:
> 
> >>> print x
> [10 1 30 3 50]
> >>> putmask(x, [1,0,1,0,1], [-1,-2])
> >>> print x
> [-1 1 -1 3 -1]
> 
> Can you explain why the -2 didn't factor in?

Because it always happens in places where the mask is 0, of course --
the third argument gets conceptually "repeated" to get the length of the
mask, giving [-1, -2, -1, -2, -1] -- and the "-2" always occur where the
mask is 0, so they don't matter.  Exactly as I would expect from:

>>> print Numeric.putmask.__doc__
putmask(a, mask, v) results in a = v for all places mask is true.
       If v is shorter than mask it will be repeated as necessary.
       In particular v can be a scalar or length 1 array.
    
and I just can't see where you might have formed any different
expectations from this documentation.  Use a different mask, say
[1,0,0,1,1] -- and the -2 in 4th place will be set into x, just like the
-1 ocurrences at the start and end.

Similarly, say:

>>> Numeric.compress([1,0,1,0,1], [-1, -2]*3)
array([-1, -1, -1])

even though here we have to explicitly use the "*3" part for repetition
since compress, differently from putmask, doesn't implicitly repeat the
last argument, the idea is similar: pick only elements corresponding to
a true value in the mask argument.

If what you want is to put -1 where the first 1 in the mask occurs, -2
where the 2nd 1 in the mask occurs, and so forth, you need some
auxiliary manipulation of the indices to prepare the proper "values"
array, for example:

import Numeric

class SequenceRepeater(object):
    def __init__(self, seq, thelen):
        self.seq = seq
        self.len = thelen
    def __len__(self):
        return self.len
    def __getitem__(self, i):
        if i<0: i += self.len
        return self.seq[i % len(self.seq)]

def strangeput(anarray, amask, somevalues):
    repeater = SequenceRepeater(somevalues, len(amask))
    somevalues = Numeric.take(repeater, Numeric.cumsum(amask)-1)
    Numeric.putmask(anarray, amask, somevalues)

if __name__ == '__main__':
    x = Numeric.zeros(5)
    strangeput(x, [1, 0, 1, 0, 1], [-1, -2])
    print x


brain:~/pynut alex$ python pr.py 
[-1  0 -2  0 -1]


There may be simpler and faster approaches for this, of course, but I
had this SequenceRepeater auxiliary class in my "mixed bag of useful
stuff" so I just copied-and-pasted a solution based on it!-)


Alex



More information about the Python-list mailing list