# [Numpy-discussion] Matrix vs array in ma.minimum

David Cournapeau cournape at gmail.com
Thu Jan 14 23:06:36 EST 2010

```On Fri, Jan 15, 2010 at 11:59 AM, Pierre GM <pgmdevlist at gmail.com> wrote:
> On Jan 14, 2010, at 8:52 PM, David Cournapeau wrote:
>> Pierre GM wrote:
>>
>>>
>>> Er, no.
>>> np.ma.minimum(a, b) returns the lowest value of a and b element-wsie, or the the lowest element of a is b is None. The behavior is inherited from the very first implementation of maskedarray in numeric. This itself is unexpected, since np.minimum requires at least 2 input arguments.
>>>
>>> As you observed, the current function breaks down w/ np.matrix objects when only one argument is given (and when the axis is None): we call umath.minimum.reduce on the ravelled matirx, which returns the ravelled matrix. One would expect a scalar, so yes, this behavior is also unexpected.
>>>
>>> Now, which way should we go ? Keep np.ma.minimum as it is (fixing the bug so that a scalar is returned if the function is called with only 1 argument and an axis  None) ? Adapt it to match np.minimum ?
>>
>> I am not a user of Masked Array, so I don't know what is the most
>> desirable behavior.
>
> I'm not a regular user of np.minimum.

Damn, I thought I coul
>
>> The problem appears when using pylab.imshow on
>> matrices, because matplotlib (and not matlab :) ) uses masked arrays
>> when normalizing the values.
>
>
> David, you mind pointing me to the relevan part of the code and/or give me an example ?

Here is a self-contained example reproducing the matplotlib pb:

import numpy as np
from numpy import ma
import matplotlib.cbook as cbook

class Normalize:
"""
Normalize a given value to the 0-1 range
"""
def __init__(self, vmin=None, vmax=None, clip=False):
"""
If *vmin* or *vmax* is not given, they are taken from the input's
minimum and maximum value respectively.  If *clip* is *True* and
the given value falls outside the range, the returned value
will be 0 or 1, whichever is closer. Returns 0 if::

vmin==vmax

Works with scalars or arrays, including masked arrays.  If
*clip* is *True*, masked values are set to 1; otherwise they
remain masked.  Clipping silently defeats the purpose of setting
the over, under, and masked colors in the colormap, so it is
likely to lead to surprises; therefore the default is
*clip* = *False*.
"""
self.vmin = vmin
self.vmax = vmax
self.clip = clip

def __call__(self, value, clip=None):
if clip is None:
clip = self.clip

if cbook.iterable(value):
vtype = 'array'
val = ma.asarray(value).astype(np.float)
else:
vtype = 'scalar'
val = ma.array([value]).astype(np.float)

self.autoscale_None(val)
vmin, vmax = self.vmin, self.vmax
if vmin > vmax:
raise ValueError("minvalue must be less than or equal to maxvalue")
elif vmin==vmax:
return 0.0 * val
else:
if clip:
val = ma.array(np.clip(val.filled(vmax), vmin, vmax),
result = (val-vmin) * (1.0/(vmax-vmin))
if vtype == 'scalar':
result = result[0]
return result

def inverse(self, value):
if not self.scaled():
raise ValueError("Not invertible until scaled")
vmin, vmax = self.vmin, self.vmax

if cbook.iterable(value):
val = ma.asarray(value)
return vmin + val * (vmax - vmin)
else:
return vmin + value * (vmax - vmin)

def autoscale(self, A):
'''
Set *vmin*, *vmax* to min, max of *A*.
'''
self.vmin = ma.minimum(A)
self.vmax = ma.maximum(A)

def autoscale_None(self, A):
' autoscale only None-valued vmin or vmax'
if self.vmin is None: self.vmin = ma.minimum(A)
if self.vmax is None: self.vmax = ma.maximum(A)

def scaled(self):
'return true if vmin and vmax set'
return (self.vmin is not None and self.vmax is not None)

if __name__ == "__main__":
x = np.random.randn(10, 10)
mx = np.matrix(x)
print Normalize()(x)
print Normalize()(mx)

```