~ubuntu-branches/ubuntu/saucy/python-scipy/saucy

« back to all changes in this revision

Viewing changes to scipy/sandbox/maskedarray/core.py

  • Committer: Bazaar Package Importer
  • Author(s): Ondrej Certik
  • Date: 2008-06-16 22:58:01 UTC
  • mfrom: (2.1.24 intrepid)
  • Revision ID: james.westby@ubuntu.com-20080616225801-irdhrpcwiocfbcmt
Tags: 0.6.0-12
* The description updated to match the current SciPy (Closes: #489149).
* Standards-Version bumped to 3.8.0 (no action needed)
* Build-Depends: netcdf-dev changed to libnetcdf-dev

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
# pylint: disable-msg=E1002
 
2
"""MA: a facility for dealing with missing observations
 
3
MA is generally used as a numpy.array look-alike.
 
4
by Paul F. Dubois.
 
5
 
 
6
Copyright 1999, 2000, 2001 Regents of the University of California.
 
7
Released for unlimited redistribution.
 
8
Adapted for numpy_core 2005 by Travis Oliphant and
 
9
(mainly) Paul Dubois.
 
10
 
 
11
Subclassing of the base ndarray 2006 by Pierre Gerard-Marchant.
 
12
pgmdevlist_AT_gmail_DOT_com
 
13
Improvements suggested by Reggie Dugard (reggie_AT_merfinllc_DOT_com)
 
14
 
 
15
:author: Pierre Gerard-Marchant
 
16
:contact: pierregm_at_uga_dot_edu
 
17
:version: $Id: core.py 3245 2007-08-15 13:38:19Z pierregm $
 
18
"""
 
19
__author__ = "Pierre GF Gerard-Marchant ($Author: pierregm $)"
 
20
__version__ = '1.0'
 
21
__revision__ = "$Revision: 3245 $"
 
22
__date__     = '$Date: 2007-08-15 06:38:19 -0700 (Wed, 15 Aug 2007) $'
 
23
 
 
24
__all__ = ['MAError', 'MaskType', 'MaskedArray',
 
25
           'bool_', 'complex_', 'float_', 'int_', 'object_',
 
26
           'abs', 'absolute', 'add', 'all', 'allclose', 'allequal', 'alltrue',
 
27
               'amax', 'amin', 'anom', 'anomalies', 'any', 'arange',
 
28
               'arccos', 'arccosh', 'arcsin', 'arcsinh', 'arctan', 'arctan2',
 
29
               'arctanh', 'argmax', 'argmin', 'argsort', 'around',
 
30
               'array', 'asarray',
 
31
           'bitwise_and', 'bitwise_or', 'bitwise_xor',
 
32
           'ceil', 'choose', 'compressed', 'concatenate', 'conjugate',
 
33
               'cos', 'cosh', 'count',
 
34
           'diagonal', 'divide', 'dump', 'dumps',
 
35
           'empty', 'empty_like', 'equal', 'exp',
 
36
           'fabs', 'fmod', 'filled', 'floor', 'floor_divide',
 
37
           'getmask', 'getmaskarray', 'greater', 'greater_equal', 'hypot',
 
38
           'ids', 'inner', 'innerproduct',
 
39
               'isMA', 'isMaskedArray', 'is_mask', 'is_masked', 'isarray',
 
40
           'left_shift', 'less', 'less_equal', 'load', 'loads', 'log', 'log10',
 
41
               'logical_and', 'logical_not', 'logical_or', 'logical_xor',
 
42
           'make_mask', 'make_mask_none', 'mask_or', 'masked',
 
43
               'masked_array', 'masked_equal', 'masked_greater',
 
44
               'masked_greater_equal', 'masked_inside', 'masked_less',
 
45
               'masked_less_equal', 'masked_not_equal', 'masked_object',
 
46
               'masked_outside', 'masked_print_option', 'masked_singleton',
 
47
               'masked_values', 'masked_where', 'max', 'maximum', 'mean', 'min',
 
48
               'minimum', 'multiply',
 
49
           'negative', 'nomask', 'nonzero', 'not_equal',
 
50
           'ones', 'outer', 'outerproduct',
 
51
           'power', 'product', 'ptp', 'put', 'putmask',
 
52
           'rank', 'ravel', 'remainder', 'repeat', 'reshape', 'resize',
 
53
               'right_shift', 'round_',
 
54
           'shape', 'sin', 'sinh', 'size', 'sometrue', 'sort', 'sqrt', 'std',
 
55
               'subtract', 'sum', 'swapaxes',
 
56
           'take', 'tan', 'tanh', 'transpose', 'true_divide',
 
57
           'var', 'where',
 
58
           'zeros']
 
59
 
 
60
import sys
 
61
import types
 
62
import cPickle
 
63
import operator
 
64
#
 
65
import numpy
 
66
from numpy import bool_, complex_, float_, int_, object_, str_
 
67
 
 
68
import numpy.core.umath as umath
 
69
import numpy.core.fromnumeric  as fromnumeric
 
70
import numpy.core.numeric as numeric
 
71
import numpy.core.numerictypes as ntypes
 
72
from numpy import bool_, dtype, typecodes, amax, amin, ndarray
 
73
from numpy import expand_dims as n_expand_dims
 
74
import warnings
 
75
 
 
76
 
 
77
MaskType = bool_
 
78
nomask = MaskType(0)
 
79
 
 
80
divide_tolerance = 1.e-35
 
81
numpy.seterr(all='ignore')
 
82
 
 
83
# TODO: There's still a problem with N.add.reduce not working...
 
84
# TODO: ...neither does N.add.accumulate
 
85
 
 
86
#####--------------------------------------------------------------------------
 
87
#---- --- Exceptions ---
 
88
#####--------------------------------------------------------------------------
 
89
class MAError(Exception):
 
90
    "Class for MA related errors."
 
91
    def __init__ (self, args=None):
 
92
        "Creates an exception."
 
93
        Exception.__init__(self,args)
 
94
        self.args = args
 
95
    def __str__(self):
 
96
        "Calculates the string representation."
 
97
        return str(self.args)
 
98
    __repr__ = __str__
 
99
 
 
100
#####--------------------------------------------------------------------------
 
101
#---- --- Filling options ---
 
102
#####--------------------------------------------------------------------------
 
103
# b: boolean - c: complex - f: floats - i: integer - O: object - S: string
 
104
default_filler = {'b': True,
 
105
                  'c' : 1.e20 + 0.0j,
 
106
                  'f' : 1.e20,
 
107
                  'i' : 999999,
 
108
                  'O' : '?',
 
109
                  'S' : 'N/A',
 
110
                  'u' : 999999,
 
111
                  'V' : '???',
 
112
                  }
 
113
max_filler = ntypes._minvals
 
114
max_filler.update([(k,-numeric.inf) for k in [numpy.float32, numpy.float64]])
 
115
min_filler = ntypes._maxvals
 
116
min_filler.update([(k,numeric.inf) for k in [numpy.float32, numpy.float64]])
 
117
if 'float128' in ntypes.typeDict:
 
118
    max_filler.update([(numpy.float128,-numeric.inf)])
 
119
    min_filler.update([(numpy.float128, numeric.inf)])
 
120
 
 
121
 
 
122
def default_fill_value(obj):
 
123
    "Calculates the default fill value for an object `obj`."
 
124
    if hasattr(obj,'dtype'):
 
125
        defval = default_filler[obj.dtype.kind]
 
126
    elif isinstance(obj, numeric.dtype):
 
127
        defval = default_filler[obj.kind]
 
128
    elif isinstance(obj, float):
 
129
        defval = default_filler['f']
 
130
    elif isinstance(obj, int) or isinstance(obj, long):
 
131
        defval = default_filler['i']
 
132
    elif isinstance(obj, str):
 
133
        defval = default_filler['S']
 
134
    elif isinstance(obj, complex):
 
135
        defval = default_filler['c']
 
136
    else:
 
137
        defval = default_filler['O']
 
138
    return defval
 
139
 
 
140
def minimum_fill_value(obj):
 
141
    "Calculates the default fill value suitable for taking the minimum of `obj`."
 
142
    if hasattr(obj, 'dtype'):
 
143
        objtype = obj.dtype
 
144
        filler = min_filler[objtype]
 
145
        if filler is None:
 
146
            raise TypeError, 'Unsuitable type for calculating minimum.'
 
147
        return filler
 
148
    elif isinstance(obj, float):
 
149
        return min_filler[ntypes.typeDict['float_']]
 
150
    elif isinstance(obj, int):
 
151
        return min_filler[ntypes.typeDict['int_']]
 
152
    elif isinstance(obj, long):
 
153
        return min_filler[ntypes.typeDict['uint']]
 
154
    elif isinstance(obj, numeric.dtype):
 
155
        return min_filler[obj]
 
156
    else:
 
157
        raise TypeError, 'Unsuitable type for calculating minimum.'
 
158
 
 
159
def maximum_fill_value(obj):
 
160
    "Calculates the default fill value suitable for taking the maximum of `obj`."
 
161
    if hasattr(obj, 'dtype'):
 
162
        objtype = obj.dtype
 
163
        filler = max_filler[objtype]
 
164
        if filler is None:
 
165
            raise TypeError, 'Unsuitable type for calculating minimum.'
 
166
        return filler
 
167
    elif isinstance(obj, float):
 
168
        return max_filler[ntypes.typeDict['float_']]
 
169
    elif isinstance(obj, int):
 
170
        return max_filler[ntypes.typeDict['int_']]
 
171
    elif isinstance(obj, long):
 
172
        return max_filler[ntypes.typeDict['uint']]
 
173
    elif isinstance(obj, numeric.dtype):
 
174
        return max_filler[obj]
 
175
    else:
 
176
        raise TypeError, 'Unsuitable type for calculating minimum.'
 
177
 
 
178
def set_fill_value(a, fill_value):
 
179
    "Sets the fill value of `a` if it is a masked array."
 
180
    if isinstance(a, MaskedArray):
 
181
        a.set_fill_value(fill_value)
 
182
 
 
183
def get_fill_value(a):
 
184
    """Returns the fill value of `a`, if any.
 
185
    Otherwise, returns the default fill value for that type.
 
186
    """
 
187
    if isinstance(a, MaskedArray):
 
188
        result = a.fill_value
 
189
    else:
 
190
        result = default_fill_value(a)
 
191
    return result
 
192
 
 
193
def common_fill_value(a, b):
 
194
    "Returns the common fill_value of `a` and `b`, if any, or `None`."
 
195
    t1 = get_fill_value(a)
 
196
    t2 = get_fill_value(b)
 
197
    if t1 == t2:
 
198
        return t1
 
199
    return None
 
200
 
 
201
#................................................
 
202
def filled(a, value = None):
 
203
    """Returns `a` as an array with masked data replaced by `value`.
 
204
If `value` is `None` or the special element `masked`, `get_fill_value(a)`
 
205
is used instead.
 
206
 
 
207
If `a` is already a contiguous numeric array, `a` itself is returned.
 
208
 
 
209
`filled(a)` can be used to be sure that the result is numeric when passing
 
210
an object a to other software ignorant of MA, in particular to numpy itself.
 
211
    """
 
212
    if hasattr(a, 'filled'):
 
213
        return a.filled(value)
 
214
    elif isinstance(a, ndarray): # and a.flags['CONTIGUOUS']:
 
215
        return a
 
216
    elif isinstance(a, dict):
 
217
        return numeric.array(a, 'O')
 
218
    else:
 
219
        return numeric.array(a)
 
220
 
 
221
def get_masked_subclass(*arrays):
 
222
    """Returns the youngest subclass of MaskedArray from a list of arrays,
 
223
 or MaskedArray. In case of siblings, the first takes over."""
 
224
    if len(arrays) == 1:
 
225
        arr = arrays[0]
 
226
        if isinstance(arr, MaskedArray):
 
227
            rcls = type(arr)
 
228
        else:
 
229
            rcls = MaskedArray
 
230
    else:
 
231
        arrcls = [type(a) for a in arrays]
 
232
        rcls = arrcls[0]
 
233
        if not issubclass(rcls, MaskedArray):
 
234
            rcls = MaskedArray
 
235
        for cls in arrcls[1:]:
 
236
            if issubclass(cls, rcls):
 
237
                rcls = cls
 
238
    return rcls
 
239
 
 
240
#####--------------------------------------------------------------------------
 
241
#---- --- Ufuncs ---
 
242
#####--------------------------------------------------------------------------
 
243
ufunc_domain = {}
 
244
ufunc_fills = {}
 
245
 
 
246
class domain_check_interval:
 
247
    """Defines a valid interval,
 
248
so that `domain_check_interval(a,b)(x) = true` where `x < a` or `x > b`."""
 
249
    def __init__(self, a, b):
 
250
        "domain_check_interval(a,b)(x) = true where x < a or y > b"
 
251
        if (a > b):
 
252
            (a, b) = (b, a)
 
253
        self.a = a
 
254
        self.b = b
 
255
 
 
256
    def __call__ (self, x):
 
257
        "Execute the call behavior."
 
258
        return umath.logical_or(umath.greater (x, self.b),
 
259
                                umath.less(x, self.a))
 
260
#............................
 
261
class domain_tan:
 
262
    """Defines a valid interval for the `tan` function,
 
263
so that `domain_tan(eps) = True where `abs(cos(x)) < eps`"""
 
264
    def __init__(self, eps):
 
265
        "domain_tan(eps) = true where abs(cos(x)) < eps)"
 
266
        self.eps = eps
 
267
    def __call__ (self, x):
 
268
        "Execute the call behavior."
 
269
        return umath.less(umath.absolute(umath.cos(x)), self.eps)
 
270
#............................
 
271
class domain_safe_divide:
 
272
    """defines a domain for safe division."""
 
273
    def __init__ (self, tolerance=divide_tolerance):
 
274
        self.tolerance = tolerance
 
275
    def __call__ (self, a, b):
 
276
        return umath.absolute(a) * self.tolerance >= umath.absolute(b)
 
277
#............................
 
278
class domain_greater:
 
279
    "domain_greater(v)(x) = true where x <= v"
 
280
    def __init__(self, critical_value):
 
281
        "domain_greater(v)(x) = true where x <= v"
 
282
        self.critical_value = critical_value
 
283
 
 
284
    def __call__ (self, x):
 
285
        "Execute the call behavior."
 
286
        return umath.less_equal(x, self.critical_value)
 
287
#............................
 
288
class domain_greater_equal:
 
289
    "domain_greater_equal(v)(x) = true where x < v"
 
290
    def __init__(self, critical_value):
 
291
        "domain_greater_equal(v)(x) = true where x < v"
 
292
        self.critical_value = critical_value
 
293
 
 
294
    def __call__ (self, x):
 
295
        "Execute the call behavior."
 
296
        return umath.less(x, self.critical_value)
 
297
#..............................................................................
 
298
class masked_unary_operation:
 
299
    """Defines masked version of unary operations,
 
300
where invalid values are pre-masked.
 
301
 
 
302
:IVariables:
 
303
    - `f` : function.
 
304
    - `fill` : Default filling value *[0]*.
 
305
    - `domain` : Default domain *[None]*.
 
306
    """
 
307
    def __init__ (self, mufunc, fill=0, domain=None):
 
308
        """ masked_unary_operation(aufunc, fill=0, domain=None)
 
309
            aufunc(fill) must be defined
 
310
            self(x) returns aufunc(x)
 
311
            with masked values where domain(x) is true or getmask(x) is true.
 
312
        """
 
313
        self.f = mufunc
 
314
        self.fill = fill
 
315
        self.domain = domain
 
316
        self.__doc__ = getattr(mufunc, "__doc__", str(mufunc))
 
317
        self.__name__ = getattr(mufunc, "__name__", str(mufunc))
 
318
        ufunc_domain[mufunc] = domain
 
319
        ufunc_fills[mufunc] = fill
 
320
    #
 
321
    def __call__ (self, a, *args, **kwargs):
 
322
        "Execute the call behavior."
 
323
# numeric tries to return scalars rather than arrays when given scalars.
 
324
        m = getmask(a)
 
325
        d1 = filled(a, self.fill)
 
326
        if self.domain is not None:
 
327
            m = mask_or(m, numeric.asarray(self.domain(d1)))
 
328
        # Take care of the masked singletong first ...
 
329
        if m.ndim == 0 and m:
 
330
            return masked
 
331
        # Get the result....
 
332
        if isinstance(a, MaskedArray):
 
333
            result = self.f(d1, *args, **kwargs).view(type(a))
 
334
        else:
 
335
            result = self.f(d1, *args, **kwargs).view(MaskedArray)
 
336
        # Fix the mask if we don't have a scalar
 
337
        if result.ndim > 0:
 
338
            result._mask = m
 
339
        return result
 
340
    #
 
341
    def __str__ (self):
 
342
        return "Masked version of %s. [Invalid values are masked]" % str(self.f)
 
343
#..............................................................................
 
344
class masked_binary_operation:
 
345
    """Defines masked version of binary operations,
 
346
where invalid values are pre-masked.
 
347
 
 
348
:IVariables:
 
349
    - `f` : function.
 
350
    - `fillx` : Default filling value for first array*[0]*.
 
351
    - `filly` : Default filling value for second array*[0]*.
 
352
    - `domain` : Default domain *[None]*.
 
353
    """
 
354
    def __init__ (self, mbfunc, fillx=0, filly=0):
 
355
        """abfunc(fillx, filly) must be defined.
 
356
           abfunc(x, filly) = x for all x to enable reduce.
 
357
        """
 
358
        self.f = mbfunc
 
359
        self.fillx = fillx
 
360
        self.filly = filly
 
361
        self.__doc__ = getattr(mbfunc, "__doc__", str(mbfunc))
 
362
        self.__name__ = getattr(mbfunc, "__name__", str(mbfunc))
 
363
        ufunc_domain[mbfunc] = None
 
364
        ufunc_fills[mbfunc] = (fillx, filly)
 
365
    #
 
366
    def __call__ (self, a, b, *args, **kwargs):
 
367
        "Execute the call behavior."
 
368
        m = mask_or(getmask(a), getmask(b))
 
369
        if (not m.ndim) and m:
 
370
            return masked
 
371
        d1 = filled(a, self.fillx)
 
372
        d2 = filled(b, self.filly)
 
373
# CHECK : Do we really need to fill the arguments ? Pro'ly not        
 
374
#        result = self.f(a, b, *args, **kwargs).view(get_masked_subclass(a,b))
 
375
        result = self.f(d1, d2, *args, **kwargs).view(get_masked_subclass(a,b))
 
376
        if result.ndim > 0:
 
377
            result._mask = m
 
378
        return result
 
379
    #
 
380
    def reduce (self, target, axis=0, dtype=None):
 
381
        """Reduces `target` along the given `axis`."""
 
382
        if isinstance(target, MaskedArray):
 
383
            tclass = type(target)
 
384
        else:
 
385
            tclass = MaskedArray
 
386
        m = getmask(target)
 
387
        t = filled(target, self.filly)
 
388
        if t.shape == ():
 
389
            t = t.reshape(1)
 
390
            if m is not nomask:
 
391
                m = make_mask(m, copy=1)
 
392
                m.shape = (1,)
 
393
        if m is nomask:
 
394
            return self.f.reduce(t, axis).view(tclass)
 
395
        t = t.view(tclass)
 
396
        t._mask = m
 
397
        # XXX: "or t.dtype" below is a workaround for what appears
 
398
        # XXX: to be a bug in reduce.
 
399
        tr = self.f.reduce(filled(t, self.filly), axis, dtype=dtype or t.dtype)
 
400
        mr = umath.logical_and.reduce(m, axis)
 
401
        tr = tr.view(tclass)
 
402
        if mr.ndim > 0:
 
403
            tr._mask = mr
 
404
            return tr
 
405
        elif mr:
 
406
            return masked
 
407
        return tr
 
408
 
 
409
    def outer (self, a, b):
 
410
        "Returns the function applied to the outer product of a and b."
 
411
        ma = getmask(a)
 
412
        mb = getmask(b)
 
413
        if ma is nomask and mb is nomask:
 
414
            m = nomask
 
415
        else:
 
416
            ma = getmaskarray(a)
 
417
            mb = getmaskarray(b)
 
418
            m = umath.logical_or.outer(ma, mb)
 
419
        if (not m.ndim) and m:
 
420
            return masked
 
421
        rcls = get_masked_subclass(a,b)
 
422
        d = self.f.outer(filled(a, self.fillx), filled(b, self.filly)).view(rcls)
 
423
        if d.ndim > 0:
 
424
            d._mask = m
 
425
        return d
 
426
 
 
427
    def accumulate (self, target, axis=0):
 
428
        """Accumulates `target` along `axis` after filling with y fill value."""
 
429
        if isinstance(target, MaskedArray):
 
430
            tclass = type(target)
 
431
        else:
 
432
            tclass = masked_array
 
433
        t = filled(target, self.filly)
 
434
        return self.f.accumulate(t, axis).view(tclass)
 
435
 
 
436
    def __str__ (self):
 
437
        return "Masked version of " + str(self.f)
 
438
#..............................................................................
 
439
class domained_binary_operation:
 
440
    """Defines binary operations that have a domain, like divide.
 
441
 
 
442
These are complicated so they are a separate class.
 
443
They have no reduce, outer or accumulate.
 
444
 
 
445
:IVariables:
 
446
    - `f` : function.
 
447
    - `fillx` : Default filling value for first array*[0]*.
 
448
    - `filly` : Default filling value for second array*[0]*.
 
449
    - `domain` : Default domain *[None]*.
 
450
    """
 
451
    def __init__ (self, dbfunc, domain, fillx=0, filly=0):
 
452
        """abfunc(fillx, filly) must be defined.
 
453
           abfunc(x, filly) = x for all x to enable reduce.
 
454
        """
 
455
        self.f = dbfunc
 
456
        self.domain = domain
 
457
        self.fillx = fillx
 
458
        self.filly = filly
 
459
        self.__doc__ = getattr(dbfunc, "__doc__", str(dbfunc))
 
460
        self.__name__ = getattr(dbfunc, "__name__", str(dbfunc))
 
461
        ufunc_domain[dbfunc] = domain
 
462
        ufunc_fills[dbfunc] = (fillx, filly)
 
463
 
 
464
    def __call__(self, a, b):
 
465
        "Execute the call behavior."
 
466
        ma = getmask(a)
 
467
        mb = getmask(b)
 
468
        d1 = filled(a, self.fillx)
 
469
        d2 = filled(b, self.filly)
 
470
        t = numeric.asarray(self.domain(d1, d2))
 
471
 
 
472
        if fromnumeric.sometrue(t, None):
 
473
            d2 = numeric.where(t, self.filly, d2)
 
474
            mb = mask_or(mb, t)
 
475
        m = mask_or(ma, mb)
 
476
        if (not m.ndim) and m:
 
477
            return masked       
 
478
        result =  self.f(d1, d2).view(get_masked_subclass(a,b))
 
479
        if result.ndim > 0:
 
480
            result._mask = m
 
481
        return result
 
482
 
 
483
    def __str__ (self):
 
484
        return "Masked version of " + str(self.f)
 
485
 
 
486
#..............................................................................
 
487
# Unary ufuncs
 
488
exp = masked_unary_operation(umath.exp)
 
489
conjugate = masked_unary_operation(umath.conjugate)
 
490
sin = masked_unary_operation(umath.sin)
 
491
cos = masked_unary_operation(umath.cos)
 
492
tan = masked_unary_operation(umath.tan)
 
493
arctan = masked_unary_operation(umath.arctan)
 
494
arcsinh = masked_unary_operation(umath.arcsinh)
 
495
sinh = masked_unary_operation(umath.sinh)
 
496
cosh = masked_unary_operation(umath.cosh)
 
497
tanh = masked_unary_operation(umath.tanh)
 
498
abs = absolute = masked_unary_operation(umath.absolute)
 
499
fabs = masked_unary_operation(umath.fabs)
 
500
negative = masked_unary_operation(umath.negative)
 
501
floor = masked_unary_operation(umath.floor)
 
502
ceil = masked_unary_operation(umath.ceil)
 
503
around = masked_unary_operation(fromnumeric.round_)
 
504
logical_not = masked_unary_operation(umath.logical_not)
 
505
# Domained unary ufuncs
 
506
sqrt = masked_unary_operation(umath.sqrt, 0.0, domain_greater_equal(0.0))
 
507
log = masked_unary_operation(umath.log, 1.0, domain_greater(0.0))
 
508
log10 = masked_unary_operation(umath.log10, 1.0, domain_greater(0.0))
 
509
tan = masked_unary_operation(umath.tan, 0.0, domain_tan(1.e-35))
 
510
arcsin = masked_unary_operation(umath.arcsin, 0.0,
 
511
                                domain_check_interval(-1.0, 1.0))
 
512
arccos = masked_unary_operation(umath.arccos, 0.0,
 
513
                                domain_check_interval(-1.0, 1.0))
 
514
arccosh = masked_unary_operation(umath.arccosh, 1.0, domain_greater_equal(1.0))
 
515
arctanh = masked_unary_operation(umath.arctanh, 0.0,
 
516
                                 domain_check_interval(-1.0+1e-15, 1.0-1e-15))
 
517
# Binary ufuncs
 
518
add = masked_binary_operation(umath.add)
 
519
subtract = masked_binary_operation(umath.subtract)
 
520
multiply = masked_binary_operation(umath.multiply, 1, 1)
 
521
arctan2 = masked_binary_operation(umath.arctan2, 0.0, 1.0)
 
522
equal = masked_binary_operation(umath.equal)
 
523
equal.reduce = None
 
524
not_equal = masked_binary_operation(umath.not_equal)
 
525
not_equal.reduce = None
 
526
less_equal = masked_binary_operation(umath.less_equal)
 
527
less_equal.reduce = None
 
528
greater_equal = masked_binary_operation(umath.greater_equal)
 
529
greater_equal.reduce = None
 
530
less = masked_binary_operation(umath.less)
 
531
less.reduce = None
 
532
greater = masked_binary_operation(umath.greater)
 
533
greater.reduce = None
 
534
logical_and = masked_binary_operation(umath.logical_and)
 
535
alltrue = masked_binary_operation(umath.logical_and, 1, 1).reduce
 
536
logical_or = masked_binary_operation(umath.logical_or)
 
537
sometrue = logical_or.reduce
 
538
logical_xor = masked_binary_operation(umath.logical_xor)
 
539
bitwise_and = masked_binary_operation(umath.bitwise_and)
 
540
bitwise_or = masked_binary_operation(umath.bitwise_or)
 
541
bitwise_xor = masked_binary_operation(umath.bitwise_xor)
 
542
hypot = masked_binary_operation(umath.hypot)
 
543
# Domained binary ufuncs
 
544
divide = domained_binary_operation(umath.divide, domain_safe_divide(), 0, 1)
 
545
true_divide = domained_binary_operation(umath.true_divide,
 
546
                                        domain_safe_divide(), 0, 1)
 
547
floor_divide = domained_binary_operation(umath.floor_divide,
 
548
                                         domain_safe_divide(), 0, 1)
 
549
remainder = domained_binary_operation(umath.remainder,
 
550
                                      domain_safe_divide(), 0, 1)
 
551
fmod = domained_binary_operation(umath.fmod, domain_safe_divide(), 0, 1)
 
552
 
 
553
 
 
554
#####--------------------------------------------------------------------------
 
555
#---- --- Mask creation functions ---
 
556
#####--------------------------------------------------------------------------
 
557
def getmask(a):
 
558
    """Returns the mask of `a`, if any, or `nomask`.
 
559
Returns `nomask` if `a` is not a masked array.
 
560
To get an array for sure use getmaskarray."""
 
561
    if hasattr(a, "_mask"):
 
562
        return a._mask
 
563
    else:
 
564
        return nomask
 
565
 
 
566
def getmaskarray(a):
 
567
    """Returns the mask of `a`, if any.
 
568
Otherwise, returns an array of `False`, with the same shape as `a`.
 
569
    """
 
570
    m = getmask(a)
 
571
    if m is nomask:
 
572
        return make_mask_none(fromnumeric.shape(a))
 
573
    else:
 
574
        return m
 
575
 
 
576
def is_mask(m):
 
577
    """Returns `True` if `m` is a legal mask.
 
578
Does not check contents, only type.
 
579
    """
 
580
    try:
 
581
        return m.dtype.type is MaskType
 
582
    except AttributeError:
 
583
        return False
 
584
#
 
585
def make_mask(m, copy=False, small_mask=True, flag=None):
 
586
    """make_mask(m, copy=0, small_mask=0)
 
587
Returns `m` as a mask, creating a copy if necessary or requested.
 
588
The function can accept any sequence of integers or `nomask`.
 
589
Does not check that contents must be 0s and 1s.
 
590
If `small_mask=True`, returns `nomask` if `m` contains no true elements.
 
591
 
 
592
:Parameters:
 
593
    - `m` (ndarray) : Mask.
 
594
    - `copy` (boolean, *[False]*) : Returns a copy of `m` if true.
 
595
    - `small_mask` (boolean, *[False]*): Flattens mask to `nomask` if `m` is all false.
 
596
    """
 
597
    if flag is not None:
 
598
        warnings.warn("The flag 'flag' is now called 'small_mask'!",
 
599
                      DeprecationWarning)
 
600
        small_mask = flag
 
601
    if m is nomask:
 
602
        return nomask
 
603
    elif isinstance(m, ndarray):
 
604
        m = filled(m, True)
 
605
        if m.dtype.type is MaskType:
 
606
            if copy:
 
607
                result = numeric.array(m, dtype=MaskType, copy=copy)
 
608
            else:
 
609
                result = m
 
610
        else:
 
611
            result = numeric.array(m, dtype=MaskType)
 
612
    else:
 
613
        result = numeric.array(filled(m, True), dtype=MaskType)
 
614
    # Bas les masques !
 
615
    if small_mask and not result.any():
 
616
        return nomask
 
617
    else:
 
618
        return result
 
619
 
 
620
def make_mask_none(s):
 
621
    "Returns a mask of shape `s`, filled with `False`."
 
622
    result = numeric.zeros(s, dtype=MaskType)
 
623
    return result
 
624
 
 
625
def mask_or (m1, m2, copy=False, small_mask=True):
 
626
    """Returns the combination of two masks `m1` and `m2`.
 
627
The masks are combined with the `logical_or` operator, treating `nomask` as false.
 
628
The result may equal m1 or m2 if the other is nomask.
 
629
 
 
630
:Parameters:
 
631
    - `m` (ndarray) : Mask.
 
632
    - `copy` (boolean, *[False]*) : Returns a copy of `m` if true.
 
633
    - `small_mask` (boolean, *[False]*): Flattens mask to `nomask` if `m` is all false.
 
634
     """
 
635
    if m1 is nomask:
 
636
        return make_mask(m2, copy=copy, small_mask=small_mask)
 
637
    if m2 is nomask:
 
638
        return make_mask(m1, copy=copy, small_mask=small_mask)
 
639
    if m1 is m2 and is_mask(m1):
 
640
        return m1
 
641
    return make_mask(umath.logical_or(m1, m2), copy=copy, small_mask=small_mask)
 
642
 
 
643
#####--------------------------------------------------------------------------
 
644
#--- --- Masking functions ---
 
645
#####--------------------------------------------------------------------------
 
646
def masked_where(condition, a, copy=True):
 
647
    """Returns `x` as an array masked where `condition` is true.
 
648
Masked values of `x` or `condition` are kept.
 
649
 
 
650
:Parameters:
 
651
    - `condition` (ndarray) : Masking condition.
 
652
    - `x` (ndarray) : Array to mask.
 
653
    - `copy` (boolean, *[False]*) : Returns a copy of `m` if true.
 
654
    """
 
655
    cond = filled(condition,1)
 
656
    a = numeric.array(a, copy=copy, subok=True)
 
657
    if hasattr(a, '_mask'):
 
658
        cond = mask_or(cond, a._mask)
 
659
        cls = type(a)
 
660
    else:
 
661
        cls = MaskedArray
 
662
    result = a.view(cls)
 
663
    result._mask = cond
 
664
    return result
 
665
 
 
666
def masked_greater(x, value, copy=1):
 
667
    "Shortcut to `masked_where`, with ``condition = (x > value)``."
 
668
    return masked_where(greater(x, value), x, copy=copy)
 
669
 
 
670
def masked_greater_equal(x, value, copy=1):
 
671
    "Shortcut to `masked_where`, with ``condition = (x >= value)``."
 
672
    return masked_where(greater_equal(x, value), x, copy=copy)
 
673
 
 
674
def masked_less(x, value, copy=True):
 
675
    "Shortcut to `masked_where`, with ``condition = (x < value)``."
 
676
    return masked_where(less(x, value), x, copy=copy)
 
677
 
 
678
def masked_less_equal(x, value, copy=True):
 
679
    "Shortcut to `masked_where`, with ``condition = (x <= value)``."
 
680
    return masked_where(less_equal(x, value), x, copy=copy)
 
681
 
 
682
def masked_not_equal(x, value, copy=True):
 
683
    "Shortcut to `masked_where`, with ``condition = (x != value)``."
 
684
    return masked_where((x != value), x, copy=copy)
 
685
 
 
686
#
 
687
def masked_equal(x, value, copy=True):
 
688
    """Shortcut to `masked_where`, with ``condition = (x == value)``.
 
689
For floating point, consider `masked_values(x, value)` instead.
 
690
    """
 
691
    return masked_where((x == value), x, copy=copy)
 
692
#    d = filled(x, 0)
 
693
#    c = umath.equal(d, value)
 
694
#    m = mask_or(c, getmask(x))
 
695
#    return array(d, mask=m, copy=copy)
 
696
 
 
697
def masked_inside(x, v1, v2, copy=True):
 
698
    """Shortcut to `masked_where`, where `condition` is True for x inside
 
699
the interval `[v1,v2]` ``(v1 <= x <= v2)``.
 
700
The boundaries `v1` and `v2` can be given in either order.
 
701
    """
 
702
    if v2 < v1:
 
703
        (v1, v2) = (v2, v1)
 
704
    xf = filled(x)
 
705
    condition = (xf >= v1) & (xf <= v2)
 
706
    return masked_where(condition, x, copy=copy)
 
707
 
 
708
def masked_outside(x, v1, v2, copy=True):
 
709
    """Shortcut to `masked_where`, where `condition` is True for x outside
 
710
the interval `[v1,v2]` ``(x < v1)|(x > v2)``.
 
711
The boundaries `v1` and `v2` can be given in either order.
 
712
    """
 
713
    if v2 < v1:
 
714
        (v1, v2) = (v2, v1)
 
715
    xf = filled(x)
 
716
    condition = (xf < v1) | (xf > v2)
 
717
    return masked_where(condition, x, copy=copy)
 
718
 
 
719
#
 
720
def masked_object(x, value, copy=True):
 
721
    """Masks the array `x` where the data are exactly equal to `value`.
 
722
This function is suitable only for `object` arrays: for floating point,
 
723
please use `masked_values` instead.
 
724
The mask is set to `nomask` if posible.
 
725
 
 
726
:parameter copy (Boolean, *[True]*):  Returns a copy of `x` if true. """
 
727
    if isMaskedArray(x):
 
728
        condition = umath.equal(x._data, value)
 
729
        mask = x._mask
 
730
    else:
 
731
        condition = umath.equal(fromnumeric.asarray(x), value)
 
732
        mask = nomask
 
733
    mask = mask_or(mask, make_mask(condition, small_mask=True))
 
734
    return masked_array(x, mask=mask, copy=copy, fill_value=value)
 
735
 
 
736
def masked_values(x, value, rtol=1.e-5, atol=1.e-8, copy=True):
 
737
    """Masks the array `x` where the data are approximately equal to `value`
 
738
(that is, ``abs(x - value) <= atol+rtol*abs(value)``).
 
739
Suitable only for floating points. For integers, please use `masked_equal`.
 
740
The mask is set to `nomask` if posible.
 
741
 
 
742
:Parameters:
 
743
    - `rtol` (Float, *[1e-5]*): Tolerance parameter.
 
744
    - `atol` (Float, *[1e-8]*): Tolerance parameter.
 
745
    - `copy` (boolean, *[False]*) : Returns a copy of `x` if True.
 
746
    """
 
747
    abs = umath.absolute
 
748
    xnew = filled(x, value)
 
749
    if issubclass(xnew.dtype.type, numeric.floating):
 
750
        condition = umath.less_equal(abs(xnew-value), atol+rtol*abs(value))
 
751
        try:
 
752
            mask = x._mask
 
753
        except AttributeError:
 
754
            mask = nomask
 
755
    else:
 
756
        condition = umath.equal(xnew, value)
 
757
        mask = nomask
 
758
    mask = mask_or(mask, make_mask(condition, small_mask=True))
 
759
    return masked_array(xnew, mask=mask, copy=copy, fill_value=value)
 
760
 
 
761
#####--------------------------------------------------------------------------
 
762
#---- --- Printing options ---
 
763
#####--------------------------------------------------------------------------
 
764
class _MaskedPrintOption:
 
765
    """Handles the string used to represent missing data in a masked array."""
 
766
    def __init__ (self, display):
 
767
        "Creates the masked_print_option object."
 
768
        self._display = display
 
769
        self._enabled = True
 
770
 
 
771
    def display(self):
 
772
        "Displays the string to print for masked values."
 
773
        return self._display
 
774
 
 
775
    def set_display (self, s):
 
776
        "Sets the string to print for masked values."
 
777
        self._display = s
 
778
 
 
779
    def enabled(self):
 
780
        "Is the use of the display value enabled?"
 
781
        return self._enabled
 
782
 
 
783
    def enable(self, small_mask=1):
 
784
        "Set the enabling small_mask to `small_mask`."
 
785
        self._enabled = small_mask
 
786
 
 
787
    def __str__ (self):
 
788
        return str(self._display)
 
789
 
 
790
    __repr__ = __str__
 
791
 
 
792
#if you single index into a masked location you get this object.
 
793
masked_print_option = _MaskedPrintOption('--')
 
794
 
 
795
#####--------------------------------------------------------------------------
 
796
#---- --- MaskedArray class ---
 
797
#####--------------------------------------------------------------------------
 
798
##def _getoptions(a_out, a_in):
 
799
##    "Copies standards options of a_in to a_out."
 
800
##    for att in [']
 
801
#class _mathmethod(object):
 
802
#    """Defines a wrapper for arithmetic methods.
 
803
#Instead of directly calling a ufunc, the corresponding method of  the `array._data`
 
804
#object is called instead.
 
805
#    """
 
806
#    def __init__ (self, methodname, fill_self=0, fill_other=0, domain=None):
 
807
#        """
 
808
#:Parameters:
 
809
#    - `methodname` (String) : Method name.
 
810
#    - `fill_self` (Float *[0]*) : Fill value for the instance.
 
811
#    - `fill_other` (Float *[0]*) : Fill value for the target.
 
812
#    - `domain` (Domain object *[None]*) : Domain of non-validity.
 
813
#        """
 
814
#        self.methodname = methodname
 
815
#        self.fill_self = fill_self
 
816
#        self.fill_other = fill_other
 
817
#        self.domain = domain
 
818
#        self.obj = None
 
819
#        self.__doc__ = self.getdoc()
 
820
#    #
 
821
#    def getdoc(self):
 
822
#        "Returns the doc of the function (from the doc of the method)."
 
823
#        try:
 
824
#            return getattr(MaskedArray, self.methodname).__doc__
 
825
#        except:
 
826
#            return getattr(ndarray, self.methodname).__doc__
 
827
#    #
 
828
#    def __get__(self, obj, objtype=None):
 
829
#        self.obj = obj
 
830
#        return self
 
831
#    #
 
832
#    def __call__ (self, other, *args):
 
833
#        "Execute the call behavior."
 
834
#        instance = self.obj
 
835
#        m_self = instance._mask
 
836
#        m_other = getmask(other)
 
837
#        base = instance.filled(self.fill_self)
 
838
#        target = filled(other, self.fill_other)
 
839
#        if self.domain is not None:
 
840
#            # We need to force the domain to a ndarray only.
 
841
#            if self.fill_other > self.fill_self:
 
842
#                domain = self.domain(base, target)
 
843
#            else:
 
844
#                domain = self.domain(target, base)
 
845
#            if domain.any():
 
846
#                #If `other` is a subclass of ndarray, `filled` must have the
 
847
#                # same subclass, else we'll lose some info.
 
848
#                #The easiest then is to fill `target` instead of creating
 
849
#                # a pure ndarray.
 
850
#                #Oh, and we better make a copy!
 
851
#                if isinstance(other, ndarray):
 
852
#                    # We don't want to modify other: let's copy target, then
 
853
#                    target = target.copy()
 
854
#                    target[fromnumeric.asarray(domain)] = self.fill_other
 
855
#                else:
 
856
#                    target = numeric.where(fromnumeric.asarray(domain),
 
857
#                                           self.fill_other, target)
 
858
#                m_other = mask_or(m_other, domain)
 
859
#        m = mask_or(m_self, m_other)
 
860
#        method = getattr(base, self.methodname)
 
861
#        result = method(target, *args).view(type(instance))
 
862
#        try:
 
863
#            result._mask = m
 
864
#        except AttributeError:
 
865
#            if m:
 
866
#                result = masked
 
867
#        return result
 
868
#...............................................................................
 
869
class _arraymethod(object):
 
870
    """Defines a wrapper for basic array methods.
 
871
Upon call, returns a masked array, where the new `_data` array is the output
 
872
of the corresponding method called on the original `_data`.
 
873
 
 
874
If `onmask` is True, the new mask is the output of the method calld on the initial mask.
 
875
If `onmask` is False, the new mask is just a reference to the initial mask.
 
876
 
 
877
:Parameters:
 
878
    `funcname` : String
 
879
        Name of the function to apply on data.
 
880
    `onmask` : Boolean *[True]*
 
881
        Whether the mask must be processed also (True) or left alone (False).
 
882
    """
 
883
    def __init__(self, funcname, onmask=True):
 
884
        self._name = funcname
 
885
        self._onmask = onmask
 
886
        self.obj = None
 
887
        self.__doc__ = self.getdoc()
 
888
    #
 
889
    def getdoc(self):
 
890
        "Returns the doc of the function (from the doc of the method)."
 
891
        methdoc = getattr(ndarray, self._name, None)
 
892
        methdoc = getattr(numpy, self._name, methdoc)
 
893
#        methdoc = getattr(MaskedArray, self._name, methdoc)
 
894
        if methdoc is not None:
 
895
            return methdoc.__doc__
 
896
#        try:
 
897
#            return getattr(MaskedArray, self._name).__doc__
 
898
#        except:
 
899
#            try:
 
900
#                return getattr(numpy, self._name).__doc__
 
901
#            except:
 
902
#                return getattr(ndarray, self._name).__doc
 
903
    #
 
904
    def __get__(self, obj, objtype=None):
 
905
        self.obj = obj
 
906
        return self
 
907
    #
 
908
    def __call__(self, *args, **params):
 
909
        methodname = self._name
 
910
        data = self.obj._data
 
911
        mask = self.obj._mask
 
912
        cls = type(self.obj)
 
913
        result = getattr(data, methodname)(*args, **params).view(cls)
 
914
        result._smallmask = self.obj._smallmask
 
915
        if result.ndim:
 
916
            if not self._onmask:
 
917
                result._mask = mask
 
918
            elif mask is not nomask:
 
919
                result.__setmask__(getattr(mask, methodname)(*args, **params))
 
920
        return result
 
921
#..........................................................
 
922
 
 
923
class flatiter(object):
 
924
    "Defines an interator."
 
925
    def __init__(self, ma):
 
926
        self.ma = ma
 
927
        self.ma_iter = numpy.asarray(ma).flat
 
928
 
 
929
        if ma._mask is nomask:
 
930
            self.maskiter = None
 
931
        else:
 
932
            self.maskiter = ma._mask.flat
 
933
 
 
934
    def __iter__(self):
 
935
        return self
 
936
 
 
937
    ### This won't work is ravel makes a copy
 
938
    def __setitem__(self, index, value):
 
939
        a = self.ma.ravel()
 
940
        a[index] = value
 
941
 
 
942
    def next(self):
 
943
        d = self.ma_iter.next()
 
944
        if self.maskiter is not None and self.maskiter.next():
 
945
            d = masked
 
946
        return d
 
947
 
 
948
 
 
949
class MaskedArray(numeric.ndarray):
 
950
    """Arrays with possibly masked values.
 
951
Masked values of True exclude the corresponding element from any computation.
 
952
 
 
953
Construction:
 
954
    x = array(data, dtype=None, copy=True, order=False,
 
955
              mask = nomask, fill_value=None, small_mask=True)
 
956
 
 
957
If copy=False, every effort is made not to copy the data:
 
958
If `data` is a MaskedArray, and argument mask=nomask, then the candidate data
 
959
is `data._data` and the mask used is `data._mask`.
 
960
If `data` is a numeric array, it is used as the candidate raw data.
 
961
If `dtype` is not None and is different from data.dtype.char then a data copy is required.
 
962
Otherwise, the candidate is used.
 
963
 
 
964
If a data copy is required, the raw (unmasked) data stored is the result of:
 
965
numeric.array(data, dtype=dtype.char, copy=copy)
 
966
 
 
967
If `mask` is `nomask` there are no masked values.
 
968
Otherwise mask must be convertible to an array of booleans with the same shape as x.
 
969
If `small_mask` is True, a mask consisting of zeros (False) only is compressed to `nomask`.
 
970
Otherwise, the mask is not compressed.
 
971
 
 
972
fill_value is used to fill in masked values when necessary, such as when
 
973
printing and in method/function filled().
 
974
The fill_value is not used for computation within this module.
 
975
    """
 
976
    __array_priority__ = 10.1
 
977
    _defaultmask = nomask
 
978
    _defaulthardmask = False
 
979
    _baseclass =  numeric.ndarray
 
980
    def __new__(cls, data=None, mask=nomask, dtype=None, copy=False, fill_value=None,
 
981
                keep_mask=True, small_mask=True, hard_mask=False, flag=None,
 
982
                subok=True, **options):
 
983
        """array(data, dtype=None, copy=True, mask=nomask, fill_value=None)
 
984
 
 
985
If `data` is already a ndarray, its dtype becomes the default value of dtype.
 
986
        """
 
987
        if flag is not None:
 
988
            warnings.warn("The flag 'flag' is now called 'small_mask'!",
 
989
                          DeprecationWarning)
 
990
            small_mask = flag
 
991
        # Process data............
 
992
        _data = numeric.array(data, dtype=dtype, copy=copy, subok=subok)
 
993
        _baseclass = getattr(data, '_baseclass', type(_data))
 
994
        _basedict = getattr(data, '_basedict', getattr(data, '__dict__', None))
 
995
        if not isinstance(data, MaskedArray): 
 
996
            _data = _data.view(cls)
 
997
        elif not subok:
 
998
            _data = data.view(cls)
 
999
        else:
 
1000
            _data = _data.view(type(data))
 
1001
        # Backwards compat .......
 
1002
        if hasattr(data,'_mask') and not isinstance(data, ndarray):
 
1003
            _data._mask = data._mask
 
1004
            _sharedmask = True
 
1005
        # Process mask ...........
 
1006
        if mask is nomask:
 
1007
            if not keep_mask:
 
1008
                _data._mask = nomask
 
1009
            if copy:
 
1010
                _data._mask = _data._mask.copy()
 
1011
        else:
 
1012
            mask = numeric.array(mask, dtype=MaskType, copy=copy)
 
1013
            if mask.shape != _data.shape:
 
1014
                (nd, nm) = (_data.size, mask.size) 
 
1015
                if nm == 1:
 
1016
                    mask = numeric.resize(mask, _data.shape)
 
1017
                elif nm == nd:
 
1018
                    mask = fromnumeric.reshape(mask, _data.shape)
 
1019
                else:
 
1020
                    msg = "Mask and data not compatible: data size is %i, "+\
 
1021
                          "mask size is %i."
 
1022
                    raise MAError, msg % (nd, nm)
 
1023
            if _data._mask is nomask:
 
1024
                _data._mask = mask
 
1025
                _data._sharedmask = True
 
1026
            else:
 
1027
                # Make a copy of the mask to avoid propagation
 
1028
                _data._sharedmask = False
 
1029
                if not keep_mask:
 
1030
                    _data._mask = mask
 
1031
                else:
 
1032
                    _data._mask = umath.logical_or(mask, _data._mask) 
 
1033
                    
 
1034
                    
 
1035
        # Update fill_value.......
 
1036
        _data._fill_value = getattr(data, '_fill_value', fill_value)
 
1037
        if _data._fill_value is None:
 
1038
            _data._fill_value = default_fill_value(_data)
 
1039
        # Process extra options ..
 
1040
        _data._hardmask = hard_mask
 
1041
        _data._smallmask = small_mask
 
1042
        _data._baseclass = _baseclass
 
1043
        _data._basedict = _basedict
 
1044
        return _data
 
1045
    #........................
 
1046
    def __array_finalize__(self,obj):
 
1047
        """Finalizes the masked array.
 
1048
        """
 
1049
        # Finalize mask ...............
 
1050
        self._mask = getattr(obj, '_mask', nomask)
 
1051
        if self._mask is not nomask:
 
1052
            self._mask.shape = self.shape
 
1053
        # Get the remaining options ...
 
1054
        self._hardmask = getattr(obj, '_hardmask', self._defaulthardmask)
 
1055
        self._smallmask = getattr(obj, '_smallmask', True)
 
1056
        self._sharedmask = True
 
1057
        self._baseclass = getattr(obj, '_baseclass', type(obj))
 
1058
        self._fill_value = getattr(obj, '_fill_value', None)
 
1059
        # Update special attributes ...
 
1060
        self._basedict = getattr(obj, '_basedict', getattr(obj, '__dict__', None))
 
1061
        if self._basedict is not None:
 
1062
            self.__dict__.update(self._basedict)
 
1063
        return
 
1064
    #..................................
 
1065
    def __array_wrap__(self, obj, context=None):
 
1066
        """Special hook for ufuncs.
 
1067
Wraps the numpy array and sets the mask according to context.
 
1068
        """
 
1069
        #TODO : Should we check for type result 
 
1070
        result = obj.view(type(self))
 
1071
        #..........
 
1072
        if context is not None:
 
1073
            result._mask = result._mask.copy()
 
1074
            (func, args, _) = context
 
1075
            m = reduce(mask_or, [getmask(arg) for arg in args])
 
1076
            # Get domain mask
 
1077
            domain = ufunc_domain.get(func, None)
 
1078
            if domain is not None:
 
1079
                if len(args) > 2:
 
1080
                    d = reduce(domain, args)
 
1081
                else:
 
1082
                    d = domain(*args)
 
1083
                if m is nomask:
 
1084
                    if d is not nomask:
 
1085
                        m = d
 
1086
                else:
 
1087
                    m |= d
 
1088
            if not m.ndim and m:
 
1089
                if m:
 
1090
                    if result.shape == ():
 
1091
                        return masked
 
1092
                    result._mask = numeric.ones(result.shape, bool_)
 
1093
            else:
 
1094
                result._mask = m
 
1095
        #....
 
1096
#        result._mask = m
 
1097
        result._fill_value = self._fill_value
 
1098
        result._hardmask = self._hardmask
 
1099
        result._smallmask = self._smallmask
 
1100
        result._baseclass = self._baseclass
 
1101
        return result
 
1102
    #.............................................
 
1103
    def __getitem__(self, indx):
 
1104
        """x.__getitem__(y) <==> x[y]
 
1105
Returns the item described by i. Not a copy as in previous versions.
 
1106
        """
 
1107
        # This test is useful, but we should keep things light...
 
1108
#        if getmask(indx) is not nomask:
 
1109
#            msg = "Masked arrays must be filled before they can be used as indices!"
 
1110
#            raise IndexError, msg
 
1111
        # super() can't work here if the underlying data is a matrix...
 
1112
        dout = (self._data).__getitem__(indx)
 
1113
        m = self._mask
 
1114
        if hasattr(dout, 'shape') and len(dout.shape) > 0:
 
1115
            # Not a scalar: make sure that dout is a MA
 
1116
            dout = dout.view(type(self))
 
1117
            dout._smallmask = self._smallmask
 
1118
            if m is not nomask:
 
1119
                # use _set_mask to take care of the shape
 
1120
                dout.__setmask__(m[indx])
 
1121
        elif m is not nomask and m[indx]:
 
1122
            return masked
 
1123
        return dout
 
1124
    #........................
 
1125
    def __setitem__(self, indx, value):
 
1126
        """x.__setitem__(i, y) <==> x[i]=y
 
1127
Sets item described by index. If value is masked, masks those locations.
 
1128
        """
 
1129
        if self is masked:
 
1130
            raise MAError, 'Cannot alter the masked element.'
 
1131
#        if getmask(indx) is not nomask:
 
1132
#            msg = "Masked arrays must be filled before they can be used as indices!"
 
1133
#            raise IndexError, msg
 
1134
        #....
 
1135
        if value is masked:
 
1136
            m = self._mask
 
1137
            if m is nomask:
 
1138
                m = make_mask_none(self.shape)
 
1139
#            else:
 
1140
#                m = m.copy()
 
1141
            m[indx] = True
 
1142
            self.__setmask__(m)
 
1143
            return
 
1144
        #....
 
1145
        dval = numeric.asarray(value).astype(self.dtype)
 
1146
        valmask = getmask(value)
 
1147
        if self._mask is nomask:
 
1148
            if valmask is not nomask:
 
1149
                self._mask = make_mask_none(self.shape)
 
1150
                self._mask[indx] = valmask
 
1151
        elif not self._hardmask:
 
1152
            _mask = self._mask.copy()
 
1153
            if valmask is nomask:
 
1154
                _mask[indx] = False
 
1155
            else:
 
1156
                _mask[indx] = valmask
 
1157
            self._set_mask(_mask)
 
1158
        elif hasattr(indx, 'dtype') and (indx.dtype==bool_):
 
1159
            indx = indx * umath.logical_not(self._mask)
 
1160
        else:
 
1161
            mindx = mask_or(self._mask[indx], valmask, copy=True)
 
1162
            dindx = self._data[indx]
 
1163
            if dindx.size > 1:
 
1164
                dindx[~mindx] = dval
 
1165
            elif mindx is nomask:
 
1166
                dindx = dval
 
1167
            dval = dindx
 
1168
            self._mask[indx] = mindx
 
1169
        # Set data ..........
 
1170
        #dval = filled(value).astype(self.dtype)
 
1171
        ndarray.__setitem__(self._data,indx,dval)
 
1172
    #............................................
 
1173
    def __getslice__(self, i, j):
 
1174
        """x.__getslice__(i, j) <==> x[i:j]
 
1175
Returns the slice described by i, j.
 
1176
The use of negative indices is not supported."""
 
1177
        return self.__getitem__(slice(i,j))
 
1178
    #........................
 
1179
    def __setslice__(self, i, j, value):
 
1180
        """x.__setslice__(i, j, value) <==> x[i:j]=value
 
1181
Sets a slice i:j to `value`.
 
1182
If `value` is masked, masks those locations."""
 
1183
        self.__setitem__(slice(i,j), value)
 
1184
    #............................................
 
1185
    def __setmask__(self, mask, copy=False):
 
1186
        newmask = make_mask(mask, copy=copy, small_mask=self._smallmask)
 
1187
#        self.unshare_mask()
 
1188
        if self._mask is nomask:
 
1189
            self._mask = newmask
 
1190
        elif self._hardmask:
 
1191
            if newmask is not nomask:
 
1192
                self._mask.__ior__(newmask)
 
1193
        else:
 
1194
            # This one is tricky: if we set the mask that way, we may break the
 
1195
            # propagation. But if we don't, we end up with a mask full of False
 
1196
            # and a test on nomask fails...
 
1197
            if newmask is nomask:
 
1198
                self._mask = nomask
 
1199
            else:
 
1200
                self._mask.flat = newmask
 
1201
        if self._mask.shape:
 
1202
            self._mask = numeric.reshape(self._mask, self.shape)
 
1203
    _set_mask = __setmask__
 
1204
    
 
1205
    def _get_mask(self):
 
1206
        """Returns the current mask."""
 
1207
        return self._mask
 
1208
 
 
1209
    mask = property(fget=_get_mask, fset=__setmask__, doc="Mask")
 
1210
    #............................................
 
1211
    def harden_mask(self):
 
1212
        "Forces the mask to hard."
 
1213
        self._hardmask = True
 
1214
        
 
1215
    def soften_mask(self):
 
1216
        "Forces the mask to soft."
 
1217
        self._hardmask = False     
 
1218
        
 
1219
    def unshare_mask(self):
 
1220
        "Copies the mask and set the sharedmask flag to False."
 
1221
        if self._sharedmask:
 
1222
            self._mask = self._mask.copy()
 
1223
            self._sharedmask = False
 
1224
        
 
1225
    #............................................
 
1226
    def _get_data(self):
 
1227
        "Returns the current data (as a view of the original underlying data)>"
 
1228
        return self.view(self._baseclass)
 
1229
    _data = property(fget=_get_data)        
 
1230
    #............................................
 
1231
    def _get_flat(self):
 
1232
        """Calculates the flat value.
 
1233
        """
 
1234
        return flatiter(self)
 
1235
    #
 
1236
    def _set_flat (self, value):
 
1237
        "x.flat = value"
 
1238
        y = self.ravel()
 
1239
        y[:] = value
 
1240
    #
 
1241
    flat = property(fget=_get_flat, fset=_set_flat, doc="Flat version")
 
1242
    #............................................
 
1243
    def get_fill_value(self):
 
1244
        "Returns the filling value."
 
1245
        if self._fill_value is None:
 
1246
            self._fill_value = default_fill_value(self)
 
1247
        return self._fill_value
 
1248
 
 
1249
    def set_fill_value(self, value=None):
 
1250
        """Sets the filling value to `value`.
 
1251
If None, uses the default, based on the data type."""
 
1252
        if value is None:
 
1253
            value = default_fill_value(self)
 
1254
        self._fill_value = value
 
1255
 
 
1256
    fill_value = property(fget=get_fill_value, fset=set_fill_value,
 
1257
                          doc="Filling value")
 
1258
 
 
1259
    def filled(self, fill_value=None):
 
1260
        """Returns an array of the same class as `_data`,
 
1261
 with masked values filled with `fill_value`.
 
1262
Subclassing is preserved.
 
1263
 
 
1264
If `fill_value` is None, uses self.fill_value.
 
1265
        """
 
1266
        m = self._mask
 
1267
        if m is nomask or not m.any():
 
1268
            return self._data
 
1269
        #
 
1270
        if fill_value is None:
 
1271
            fill_value = self.fill_value
 
1272
        #
 
1273
        if self is masked_singleton:
 
1274
            result = numeric.asanyarray(fill_value)
 
1275
        else:
 
1276
            result = self._data.copy()
 
1277
            try:
 
1278
                result[m] = fill_value
 
1279
            except (TypeError, AttributeError):
 
1280
                fill_value = numeric.array(fill_value, dtype=object)
 
1281
                d = result.astype(object)
 
1282
                result = fromnumeric.choose(m, (d, fill_value))
 
1283
            except IndexError:
 
1284
                #ok, if scalar
 
1285
                if self._data.shape:
 
1286
                    raise
 
1287
                elif m:
 
1288
                    result = numeric.array(fill_value, dtype=self.dtype)
 
1289
                else:
 
1290
                    result = self._data
 
1291
        return result
 
1292
 
 
1293
    def compressed(self):
 
1294
        "A 1-D array of all the non-masked data."
 
1295
        d = self.ravel()
 
1296
        if self._mask is nomask:
 
1297
            return d
 
1298
        elif not self._smallmask and not self._mask.any():
 
1299
            return d
 
1300
        else:
 
1301
            return d[numeric.logical_not(d._mask)]
 
1302
    #............................................
 
1303
    def __str__(self):
 
1304
        """x.__str__() <==> str(x)
 
1305
Calculates the string representation, using masked for fill if it is enabled.
 
1306
Otherwise, fills with fill value.
 
1307
        """
 
1308
        if masked_print_option.enabled():
 
1309
            f = masked_print_option
 
1310
            if self is masked:
 
1311
                return str(f)
 
1312
            m = self._mask
 
1313
            if m is nomask:
 
1314
                res = self._data
 
1315
            else:
 
1316
                if m.shape == ():
 
1317
                    if m:
 
1318
                        return str(f)
 
1319
                    else:
 
1320
                        return str(self._data)
 
1321
                # convert to object array to make filled work
 
1322
#CHECK: the two lines below seem more robust than the self._data.astype
 
1323
#                res = numeric.empty(self._data.shape, object_)
 
1324
#                numeric.putmask(res,~m,self._data)
 
1325
                res = self._data.astype("|O8")
 
1326
                res[m] = f
 
1327
        else:
 
1328
            res = self.filled(self.fill_value)
 
1329
        return str(res)
 
1330
 
 
1331
    def __repr__(self):
 
1332
        """x.__repr__() <==> repr(x)
 
1333
Calculates the repr representation, using masked for fill if it is enabled.
 
1334
Otherwise fill with fill value.
 
1335
        """
 
1336
        with_mask = """\
 
1337
masked_%(name)s(data =
 
1338
 %(data)s,
 
1339
      mask =
 
1340
 %(mask)s,
 
1341
      fill_value=%(fill)s)
 
1342
"""
 
1343
        with_mask1 = """\
 
1344
masked_%(name)s(data = %(data)s,
 
1345
      mask = %(mask)s,
 
1346
      fill_value=%(fill)s)
 
1347
"""
 
1348
        n = len(self.shape)
 
1349
        name = repr(self._data).split('(')[0]
 
1350
        if n <= 1:
 
1351
            return with_mask1 % {
 
1352
                'name': name,
 
1353
                'data': str(self),
 
1354
                'mask': str(self._mask),
 
1355
                'fill': str(self.fill_value),
 
1356
                }
 
1357
        return with_mask % {
 
1358
            'name': name,
 
1359
            'data': str(self),
 
1360
            'mask': str(self._mask),
 
1361
            'fill': str(self.fill_value),
 
1362
            }
 
1363
    #............................................
 
1364
    def __iadd__(self, other):
 
1365
        "Adds other to self in place."
 
1366
        ndarray.__iadd__(self._data,other)
 
1367
        m = getmask(other)
 
1368
        if self._mask is nomask:
 
1369
            self._mask = m
 
1370
        elif m is not nomask:
 
1371
            self._mask += m
 
1372
        return self
 
1373
    #....
 
1374
    def __isub__(self, other):
 
1375
        "Subtracts other from self in place."
 
1376
        ndarray.__isub__(self._data,other)
 
1377
        m = getmask(other)
 
1378
        if self._mask is nomask:
 
1379
            self._mask = m
 
1380
        elif m is not nomask:
 
1381
            self._mask += m
 
1382
        return self
 
1383
    #....
 
1384
    def __imul__(self, other):
 
1385
        "Multiplies self by other in place."
 
1386
        ndarray.__imul__(self._data,other)
 
1387
        m = getmask(other)
 
1388
        if self._mask is nomask:
 
1389
            self._mask = m
 
1390
        elif m is not nomask:
 
1391
            self._mask += m
 
1392
        return self
 
1393
    #....
 
1394
    def __idiv__(self, other):
 
1395
        "Divides self by other in place."
 
1396
        dom_mask = domain_safe_divide().__call__(self, filled(other,1))
 
1397
        other_mask = getmask(other)
 
1398
        new_mask = mask_or(other_mask, dom_mask)
 
1399
        ndarray.__idiv__(self._data, other)
 
1400
        self._mask = mask_or(self._mask, new_mask)
 
1401
        return self
 
1402
    #............................................
 
1403
    def __float__(self):
 
1404
        "Converts self to float."
 
1405
        if self._mask is not nomask:
 
1406
            warnings.warn("Warning: converting a masked element to nan.")
 
1407
            return numpy.nan
 
1408
            #raise MAError, 'Cannot convert masked element to a Python float.'
 
1409
        return float(self.item())
 
1410
 
 
1411
    def __int__(self):
 
1412
        "Converts self to int."
 
1413
        if self._mask is not nomask:
 
1414
            raise MAError, 'Cannot convert masked element to a Python int.'
 
1415
        return int(self.item())
 
1416
    #............................................
 
1417
    def count(self, axis=None):
 
1418
        """Counts the non-masked elements of the array along a given axis,
 
1419
and returns a masked array where the mask is True where all data are masked.
 
1420
If `axis` is None, counts all the non-masked elements, and returns either a
 
1421
scalar or the masked singleton."""
 
1422
        m = self._mask
 
1423
        s = self.shape
 
1424
        ls = len(s)
 
1425
        if m is nomask:
 
1426
            if ls == 0:
 
1427
                return 1
 
1428
            if ls == 1:
 
1429
                return s[0]
 
1430
            if axis is None:
 
1431
                return self.size
 
1432
            else:
 
1433
                n = s[axis]
 
1434
                t = list(s)
 
1435
                del t[axis]
 
1436
                return numeric.ones(t) * n
 
1437
        n1 = fromnumeric.size(m, axis)
 
1438
        n2 = m.astype(int_).sum(axis)
 
1439
        if axis is None:
 
1440
            return (n1-n2)
 
1441
        else:
 
1442
            return masked_array(n1 - n2)
 
1443
    #............................................
 
1444
    def reshape (self, *s):
 
1445
        """Reshapes the array to shape s.
 
1446
Returns a new masked array.
 
1447
If you want to modify the shape in place, please use `a.shape = s`"""
 
1448
        result = self._data.reshape(*s).view(type(self))
 
1449
        result.__dict__.update(self.__dict__)
 
1450
        if result._mask is not nomask:
 
1451
            result._mask = self._mask.copy()
 
1452
            result._mask.shape = result.shape
 
1453
        return result
 
1454
    #
 
1455
    repeat = _arraymethod('repeat')
 
1456
    #
 
1457
    def resize(self, newshape, refcheck=True, order=False):
 
1458
        """Attempts to modify size and shape of self inplace.
 
1459
        The array must own its own memory and not be referenced by other arrays.
 
1460
        Returns None.
 
1461
        """
 
1462
        try:
 
1463
            self._data.resize(newshape, refcheck, order)
 
1464
            if self.mask is not nomask:
 
1465
                self._mask.resize(newshape, refcheck, order)
 
1466
        except ValueError:
 
1467
            raise ValueError("Cannot resize an array that has been referenced "
 
1468
                             "or is referencing another array in this way.\n"
 
1469
                             "Use the resize function.")
 
1470
        return None
 
1471
    #
 
1472
    flatten = _arraymethod('flatten')
 
1473
    #
 
1474
    def put(self, indices, values, mode='raise'):
 
1475
        """Sets storage-indexed locations to corresponding values.
 
1476
a.put(values, indices, mode) sets a.flat[n] = values[n] for each n in indices.
 
1477
`values` can be scalar or an array shorter than indices, and it will be repeated,
 
1478
if necessary.
 
1479
If `values` has some masked values, the initial mask is updated in consequence,
 
1480
else the corresponding values are unmasked.
 
1481
        """
 
1482
        m = self._mask
 
1483
        # Hard mask: Get rid of the values/indices that fall on masked data
 
1484
        if self._hardmask and self._mask is not nomask:
 
1485
            mask = self._mask[indices]
 
1486
            indices = numeric.asarray(indices)
 
1487
            values = numeric.asanyarray(values)
 
1488
            values.resize(indices.shape)
 
1489
            indices = indices[~mask]
 
1490
            values = values[~mask]
 
1491
        #....
 
1492
        self._data.put(indices, values, mode=mode)
 
1493
        #....
 
1494
        if m is nomask:
 
1495
            m = getmask(values)
 
1496
        else:
 
1497
            m = m.copy()
 
1498
            if getmask(values) is nomask:
 
1499
                m.put(indices, False, mode=mode)
 
1500
            else:
 
1501
                m.put(indices, values._mask, mode=mode)
 
1502
            m = make_mask(m, copy=False, small_mask=True)
 
1503
        self._mask = m
 
1504
    #............................................
 
1505
    def ids (self):
 
1506
        """Return the address of the data and mask areas."""
 
1507
        return (self.ctypes.data, self._mask.ctypes.data)    
 
1508
    #............................................
 
1509
    def all(self, axis=None, out=None):
 
1510
        """a.all(axis) returns True if all entries along the axis are True.
 
1511
    Returns False otherwise. If axis is None, uses the flatten array.
 
1512
    Masked data are considered as True during computation.
 
1513
    Outputs a masked array, where the mask is True if all data are masked along the axis.
 
1514
    Note: the out argument is not really operational...
 
1515
        """
 
1516
        d = self.filled(True).all(axis=axis, out=out).view(type(self))
 
1517
        if d.ndim > 0:
 
1518
            d.__setmask__(self._mask.all(axis))
 
1519
        return d
 
1520
 
 
1521
    def any(self, axis=None, out=None):
 
1522
        """a.any(axis) returns True if some or all entries along the axis are True.
 
1523
    Returns False otherwise. If axis is None, uses the flatten array.
 
1524
    Masked data are considered as False during computation.
 
1525
    Outputs a masked array, where the mask is True if all data are masked along the axis.
 
1526
    Note: the out argument is not really operational...
 
1527
        """
 
1528
        d = self.filled(False).any(axis=axis, out=out).view(type(self))
 
1529
        if d.ndim > 0:
 
1530
            d.__setmask__(self._mask.all(axis))
 
1531
        return d
 
1532
    
 
1533
    def nonzero(self):
 
1534
        """a.nonzero() returns a tuple of arrays
 
1535
 
 
1536
    Returns a tuple of arrays, one for each dimension of a,
 
1537
    containing the indices of the non-zero elements in that
 
1538
    dimension.  The corresponding non-zero values can be obtained
 
1539
    with
 
1540
        a[a.nonzero()].
 
1541
 
 
1542
    To group the indices by element, rather than dimension, use
 
1543
        transpose(a.nonzero())
 
1544
    instead. The result of this is always a 2d array, with a row for
 
1545
    each non-zero element."""
 
1546
        return numeric.asarray(self.filled(0)).nonzero()
 
1547
    #............................................
 
1548
    def trace(self, offset=0, axis1=0, axis2=1, dtype=None, out=None):
 
1549
        """a.trace(offset=0, axis1=0, axis2=1, dtype=None, out=None)
 
1550
Returns the sum along the offset diagonal of the array's indicated `axis1` and `axis2`.
 
1551
        """
 
1552
        # TODO: What are we doing with `out`?
 
1553
        m = self._mask
 
1554
        if m is nomask:
 
1555
            result = super(MaskedArray, self).trace(offset=offset, axis1=axis1,
 
1556
                                                    axis2=axis2, out=out)
 
1557
            return result.astype(dtype)
 
1558
        else:
 
1559
            D = self.diagonal(offset=offset, axis1=axis1, axis2=axis2)
 
1560
            return D.astype(dtype).sum(axis=None)
 
1561
    #............................................
 
1562
    def sum(self, axis=None, dtype=None):
 
1563
        """a.sum(axis=None, dtype=None)
 
1564
Sums the array `a` over the given axis `axis`.
 
1565
Masked values are set to 0.
 
1566
If `axis` is None, applies to a flattened version of the array.
 
1567
    """
 
1568
        if self._mask is nomask:
 
1569
            mask = nomask
 
1570
        else:
 
1571
            mask = self._mask.all(axis)
 
1572
            if (not mask.ndim) and mask:
 
1573
                return masked
 
1574
        result = self.filled(0).sum(axis, dtype=dtype).view(type(self))
 
1575
        if result.ndim > 0:
 
1576
            result.__setmask__(mask)
 
1577
        return result
 
1578
 
 
1579
    def cumsum(self, axis=None, dtype=None):
 
1580
        """a.cumprod(axis=None, dtype=None)
 
1581
Returns the cumulative sum of the elements of array `a` along the given axis `axis`.
 
1582
Masked values are set to 0.
 
1583
If `axis` is None, applies to a flattened version of the array.
 
1584
        """
 
1585
        result = self.filled(0).cumsum(axis=axis, dtype=dtype).view(type(self))
 
1586
        result.__setmask__(self.mask)
 
1587
        return result
 
1588
 
 
1589
    def prod(self, axis=None, dtype=None):
 
1590
        """a.prod(axis=None, dtype=None)
 
1591
Returns the product of the elements of array `a` along the given axis `axis`.
 
1592
Masked elements are set to 1.
 
1593
If `axis` is None, applies to a flattened version of the array.
 
1594
        """
 
1595
        if self._mask is nomask:
 
1596
            mask = nomask
 
1597
        else:
 
1598
            mask = self._mask.all(axis)
 
1599
            if (not mask.ndim) and mask:
 
1600
                return masked
 
1601
        result = self.filled(1).prod(axis=axis, dtype=dtype).view(type(self))
 
1602
        if result.ndim:
 
1603
            result.__setmask__(mask)
 
1604
        return result
 
1605
    product = prod
 
1606
 
 
1607
    def cumprod(self, axis=None, dtype=None):
 
1608
        """a.cumprod(axis=None, dtype=None)
 
1609
Returns the cumulative product of ethe lements of array `a` along the given axis `axis`.
 
1610
Masked values are set to 1.
 
1611
If `axis` is None, applies to a flattened version of the array.
 
1612
        """
 
1613
        result = self.filled(1).cumprod(axis=axis, dtype=dtype).view(type(self))
 
1614
        result.__setmask__(self.mask)
 
1615
        return result
 
1616
 
 
1617
    def mean(self, axis=None, dtype=None):
 
1618
        """a.mean(axis=None, dtype=None)
 
1619
 
 
1620
    Averages the array over the given axis.  If the axis is None,
 
1621
    averages over all dimensions of the array.  Equivalent to
 
1622
 
 
1623
      a.sum(axis, dtype) / size(a, axis).
 
1624
 
 
1625
    The optional dtype argument is the data type for intermediate
 
1626
    calculations in the sum.
 
1627
 
 
1628
    Returns a masked array, of the same class as a.
 
1629
        """
 
1630
        if self._mask is nomask:
 
1631
            return super(MaskedArray, self).mean(axis=axis, dtype=dtype)
 
1632
        else:
 
1633
            dsum = self.sum(axis=axis, dtype=dtype)
 
1634
            cnt = self.count(axis=axis)
 
1635
            return dsum*1./cnt
 
1636
 
 
1637
    def anom(self, axis=None, dtype=None):
 
1638
        """a.anom(axis=None, dtype=None)
 
1639
    Returns the anomalies, or deviation from the average.
 
1640
            """
 
1641
        m = self.mean(axis, dtype)
 
1642
        if not axis:
 
1643
            return (self - m)
 
1644
        else:
 
1645
            return (self - expand_dims(m,axis))
 
1646
 
 
1647
    def var(self, axis=None, dtype=None):
 
1648
        """a.var(axis=None, dtype=None)
 
1649
Returns the variance, a measure of the spread of a distribution.
 
1650
 
 
1651
The variance is the average of the squared deviations from the mean,
 
1652
i.e. var = mean((x - x.mean())**2).
 
1653
        """
 
1654
        if self._mask is nomask:
 
1655
            # TODO: Do we keep super, or var _data and take a view ?
 
1656
            return super(MaskedArray, self).var(axis=axis, dtype=dtype)
 
1657
        else:
 
1658
            cnt = self.count(axis=axis)
 
1659
            danom = self.anom(axis=axis, dtype=dtype)
 
1660
            danom *= danom
 
1661
            dvar = numeric.array(danom.sum(axis) / cnt).view(type(self))
 
1662
            if axis is not None:
 
1663
                dvar._mask = mask_or(self._mask.all(axis), (cnt==1))
 
1664
            return dvar
 
1665
 
 
1666
    def std(self, axis=None, dtype=None):
 
1667
        """a.std(axis=None, dtype=None)
 
1668
Returns the standard deviation, a measure of the spread of a distribution.
 
1669
 
 
1670
The standard deviation is the square root of the average of the squared
 
1671
deviations from the mean, i.e. std = sqrt(mean((x - x.mean())**2)).
 
1672
        """
 
1673
        dvar = self.var(axis,dtype)
 
1674
        if axis is not None or dvar is not masked:
 
1675
            dvar = sqrt(dvar)
 
1676
        return dvar
 
1677
    #............................................
 
1678
    def argsort(self, axis=None, fill_value=None, kind='quicksort',
 
1679
                order=None):
 
1680
        """Returns an array of indices that sort 'a' along the specified axis.
 
1681
    Masked values are filled beforehand to `fill_value`.
 
1682
    If `fill_value` is None, uses the default for the data type.
 
1683
    Returns a numpy array.
 
1684
 
 
1685
:Keywords:
 
1686
    `axis` : Integer *[None]*
 
1687
        Axis to be indirectly sorted (default -1)
 
1688
    `kind` : String *['quicksort']*
 
1689
        Sorting algorithm (default 'quicksort')
 
1690
        Possible values: 'quicksort', 'mergesort', or 'heapsort'
 
1691
 
 
1692
    Returns: array of indices that sort 'a' along the specified axis.
 
1693
 
 
1694
    This method executes an indirect sort along the given axis using the
 
1695
    algorithm specified by the kind keyword. It returns an array of indices of
 
1696
    the same shape as 'a' that index data along the given axis in sorted order.
 
1697
 
 
1698
    The various sorts are characterized by average speed, worst case
 
1699
    performance, need for work space, and whether they are stable. A stable
 
1700
    sort keeps items with the same key in the same relative order. The three
 
1701
    available algorithms have the following properties:
 
1702
 
 
1703
    |------------------------------------------------------|
 
1704
    |    kind   | speed |  worst case | work space | stable|
 
1705
    |------------------------------------------------------|
 
1706
    |'quicksort'|   1   | O(n^2)      |     0      |   no  |
 
1707
    |'mergesort'|   2   | O(n*log(n)) |    ~n/2    |   yes |
 
1708
    |'heapsort' |   3   | O(n*log(n)) |     0      |   no  |
 
1709
    |------------------------------------------------------|
 
1710
 
 
1711
    All the sort algorithms make temporary copies of the data when the sort is not
 
1712
    along the last axis. Consequently, sorts along the last axis are faster and use
 
1713
    less space than sorts along other axis.
 
1714
        """
 
1715
        if fill_value is None:
 
1716
            fill_value = default_fill_value(self)
 
1717
        d = self.filled(fill_value).view(ndarray)
 
1718
        return d.argsort(axis=axis, kind=kind, order=order)
 
1719
    #........................
 
1720
    def argmin(self, axis=None, fill_value=None):
 
1721
        """Returns a ndarray of indices for the minimum values of `a` along the
 
1722
    specified axis.
 
1723
    Masked values are treated as if they had the value `fill_value`.
 
1724
    If `fill_value` is None, the default for the data type is used.
 
1725
    Returns a numpy array.
 
1726
 
 
1727
:Keywords:
 
1728
    `axis` : Integer *[None]*
 
1729
        Axis to be indirectly sorted (default -1)
 
1730
    `fill_value` : var *[None]*
 
1731
        Default filling value. If None, uses the minimum default for the data type.
 
1732
        """
 
1733
        if fill_value is None:
 
1734
            fill_value = minimum_fill_value(self)
 
1735
        d = self.filled(fill_value).view(ndarray)
 
1736
        return d.argmin(axis)
 
1737
    #........................
 
1738
    def argmax(self, axis=None, fill_value=None):
 
1739
        """Returns the array of indices for the maximum values of `a` along the
 
1740
    specified axis.
 
1741
    Masked values are treated as if they had the value `fill_value`.
 
1742
    If `fill_value` is None, the maximum default for the data type is used.
 
1743
    Returns a numpy array.
 
1744
 
 
1745
:Keywords:
 
1746
    `axis` : Integer *[None]*
 
1747
        Axis to be indirectly sorted (default -1)
 
1748
    `fill_value` : var *[None]*
 
1749
        Default filling value. If None, uses the data type default.
 
1750
        """
 
1751
        if fill_value is None:
 
1752
            fill_value = maximum_fill_value(self._data)
 
1753
        d = self.filled(fill_value).view(ndarray)
 
1754
        return d.argmax(axis)
 
1755
 
 
1756
    def sort(self, axis=-1, kind='quicksort', order=None, 
 
1757
             endwith=True, fill_value=None):
 
1758
        """
 
1759
        Sort a along the given axis.
 
1760
 
 
1761
    Keyword arguments:
 
1762
 
 
1763
    axis  -- axis to be sorted (default -1)
 
1764
    kind  -- sorting algorithm (default 'quicksort')
 
1765
             Possible values: 'quicksort', 'mergesort', or 'heapsort'.
 
1766
    order -- If a has fields defined, then the order keyword can be the
 
1767
             field name to sort on or a list (or tuple) of field names
 
1768
             to indicate the order that fields should be used to define
 
1769
             the sort.
 
1770
    endwith--Boolean flag indicating whether missing values (if any) should
 
1771
             be forced in the upper indices (at the end of the array) or
 
1772
             lower indices (at the beginning).
 
1773
 
 
1774
    Returns: None.
 
1775
 
 
1776
    This method sorts 'a' in place along the given axis using the algorithm
 
1777
    specified by the kind keyword.
 
1778
 
 
1779
    The various sorts may characterized by average speed, worst case
 
1780
    performance, need for work space, and whether they are stable. A stable
 
1781
    sort keeps items with the same key in the same relative order and is most
 
1782
    useful when used with argsort where the key might differ from the items
 
1783
    being sorted. The three available algorithms have the following properties:
 
1784
 
 
1785
    |------------------------------------------------------|
 
1786
    |    kind   | speed |  worst case | work space | stable|
 
1787
    |------------------------------------------------------|
 
1788
    |'quicksort'|   1   | O(n^2)      |     0      |   no  |
 
1789
    |'mergesort'|   2   | O(n*log(n)) |    ~n/2    |   yes |
 
1790
    |'heapsort' |   3   | O(n*log(n)) |     0      |   no  |
 
1791
    |------------------------------------------------------|
 
1792
 
 
1793
    """
 
1794
        if self._mask is nomask:
 
1795
            ndarray.sort(self,axis=axis, kind=kind, order=order)
 
1796
        else:
 
1797
            if fill_value is None:
 
1798
                if endwith:
 
1799
                    filler = minimum_fill_value(self)
 
1800
                else:
 
1801
                    filler = maximum_fill_value(self)
 
1802
            else:
 
1803
                filler = fill_value
 
1804
            idx = numpy.indices(self.shape)
 
1805
            idx[axis] = self.filled(filler).argsort(axis=axis,kind=kind,order=order)
 
1806
            idx_l = idx.tolist()
 
1807
            tmp_mask = self._mask[idx_l].flat
 
1808
            tmp_data = self._data[idx_l].flat
 
1809
            self.flat = tmp_data
 
1810
            self._mask.flat = tmp_mask
 
1811
        return
 
1812
    #............................................
 
1813
    def min(self, axis=None, fill_value=None):
 
1814
        """Returns the minimum/a along the given axis.
 
1815
If `axis` is None, applies to the flattened array. Masked values are filled 
 
1816
with `fill_value` during processing. If `fill_value is None, it is set to the
 
1817
maximum_fill_value corresponding to the data type."""
 
1818
        mask = self._mask
 
1819
        # Check all/nothing case ......
 
1820
        if mask is nomask:
 
1821
            return super(MaskedArray, self).min(axis=axis)
 
1822
        elif (not mask.ndim) and mask:
 
1823
            return masked
 
1824
        # Get the mask ................
 
1825
        if axis is None:
 
1826
            mask = umath.logical_and.reduce(mask.flat)
 
1827
        else:
 
1828
            mask = umath.logical_and.reduce(mask, axis=axis)
 
1829
        # Get the fil value ...........
 
1830
        if fill_value is None:
 
1831
            fill_value = minimum_fill_value(self)
 
1832
        # Get the data ................
 
1833
        result = self.filled(fill_value).min(axis=axis).view(type(self))
 
1834
        if result.ndim > 0:
 
1835
            result._mask = mask
 
1836
        return result
 
1837
    #........................
 
1838
    def max(self, axis=None, fill_value=None):
 
1839
        """Returns the maximum/a along the given axis.
 
1840
If `axis` is None, applies to the flattened array. Masked values are filled 
 
1841
with `fill_value` during processing. If `fill_value is None, it is set to the
 
1842
maximum_fill_value corresponding to the data type."""
 
1843
        mask = self._mask
 
1844
        # Check all/nothing case ......
 
1845
        if mask is nomask:
 
1846
            return super(MaskedArray, self).max(axis=axis)
 
1847
        elif (not mask.ndim) and mask:
 
1848
            return masked
 
1849
        # Check the mask ..............
 
1850
        if axis is None:
 
1851
            mask = umath.logical_and.reduce(mask.flat)
 
1852
        else:
 
1853
            mask = umath.logical_and.reduce(mask, axis=axis)
 
1854
        # Get the fill value ..........
 
1855
        if fill_value is None:
 
1856
            fill_value = maximum_fill_value(self)
 
1857
        # Get the data ................
 
1858
        result = self.filled(fill_value).max(axis=axis).view(type(self))
 
1859
        if result.ndim > 0:
 
1860
            result._mask = mask
 
1861
        return result
 
1862
    #........................
 
1863
    def ptp(self, axis=None, fill_value=None):
 
1864
        """Returns the visible data range (max-min) along the given axis.
 
1865
If the axis is `None`, applies on a flattened array. Masked values are filled
 
1866
with `fill_value` for processing. If `fill_value` is None, the maximum is uses
 
1867
the maximum default, the minimum uses the minimum default."""
 
1868
        return self.max(axis, fill_value) - self.min(axis, fill_value)
 
1869
 
 
1870
    # Array methods ---------------------------------------
 
1871
    conj = conjugate = _arraymethod('conjugate')
 
1872
    copy = _arraymethod('copy')
 
1873
    diagonal = _arraymethod('diagonal')
 
1874
    take = _arraymethod('take')
 
1875
    ravel = _arraymethod('ravel')
 
1876
    transpose = _arraymethod('transpose')
 
1877
    T = property(fget=lambda self:self.transpose())
 
1878
    swapaxes = _arraymethod('swapaxes')
 
1879
    clip = _arraymethod('clip', onmask=False)
 
1880
    compress = _arraymethod('compress')
 
1881
    copy = _arraymethod('copy')
 
1882
    squeeze = _arraymethod('squeeze')
 
1883
    #--------------------------------------------
 
1884
    def tolist(self, fill_value=None):
 
1885
        """Copies the data portion of the array to a hierarchical python list and
 
1886
    returns that list. Data items are converted to the nearest compatible Python 
 
1887
    type. 
 
1888
    Masked values are converted to `fill_value`. If `fill_value` is None, the
 
1889
    corresponding entries in the output list will be None.
 
1890
    """
 
1891
        if fill_value is not None:
 
1892
            return self.filled(fill_value).tolist()
 
1893
        result = self.filled().tolist()
 
1894
        if self._mask is nomask:
 
1895
            return result
 
1896
        if self.ndim == 0:
 
1897
            return [None]
 
1898
        elif self.ndim == 1:
 
1899
            maskedidx = self._mask.nonzero()[0].tolist()
 
1900
            [operator.setitem(result,i,None) for i in maskedidx]
 
1901
        else:
 
1902
            for idx in zip(*[i.tolist() for i in self._mask.nonzero()]):
 
1903
                tmp = result
 
1904
                for i in idx[:-1]:
 
1905
                    tmp = tmp[i]
 
1906
                tmp[idx[-1]] = None
 
1907
        return result
 
1908
            
 
1909
            
 
1910
    #........................
 
1911
    def tostring(self, fill_value=None):
 
1912
        """a.tostring(order='C', fill_value=None) -> raw copy of array data as a Python string.
 
1913
 
 
1914
    Keyword arguments:
 
1915
        order      : order of the data item in the copy {"C","F","A"} (default "C")
 
1916
        fill_value : value used in lieu of missing data 
 
1917
 
 
1918
    Construct a Python string containing the raw bytes in the array. The order
 
1919
    of the data in arrays with ndim > 1 is specified by the 'order' keyword and
 
1920
    this keyword overrides the order of the array. The
 
1921
    choices are:
 
1922
 
 
1923
        "C"       -- C order (row major)
 
1924
        "Fortran" -- Fortran order (column major)
 
1925
        "Any"     -- Current order of array.
 
1926
        None      -- Same as "Any"
 
1927
    
 
1928
    Masked data are filled with fill_value. If fill_value is None, the data-type-
 
1929
    dependent default is used."""
 
1930
        return self.filled(fill_value).tostring()   
 
1931
    #--------------------------------------------
 
1932
    # Backwards Compatibility. Heck...
 
1933
    @property
 
1934
    def data(self):
 
1935
        """Returns the `_data` part of the MaskedArray."""
 
1936
        return self._data
 
1937
    def raw_data(self):
 
1938
        """Returns the `_data` part of the MaskedArray.
 
1939
You should really use `data` instead..."""
 
1940
        return self._data
 
1941
    #--------------------------------------------
 
1942
    # Pickling
 
1943
    def __getstate__(self):
 
1944
        "Returns the internal state of the masked array, for pickling purposes."
 
1945
        state = (1,
 
1946
                 self.shape,
 
1947
                 self.dtype,
 
1948
                 self.flags.fnc,
 
1949
                 self._data.tostring(),
 
1950
                 getmaskarray(self).tostring(),
 
1951
                 self._fill_value,
 
1952
                 )
 
1953
        return state    
 
1954
    #
 
1955
    def __setstate__(self, state):
 
1956
        """Restores the internal state of the masked array, for pickling purposes.
 
1957
    `state` is typically the output of the ``__getstate__`` output, and is a 5-tuple:
 
1958
    
 
1959
        - class name
 
1960
        - a tuple giving the shape of the data
 
1961
        - a typecode for the data
 
1962
        - a binary string for the data
 
1963
        - a binary string for the mask.
 
1964
            """
 
1965
        (ver, shp, typ, isf, raw, msk, flv) = state
 
1966
        ndarray.__setstate__(self, (shp, typ, isf, raw))
 
1967
        self._mask.__setstate__((shp, dtype(bool), isf, msk))
 
1968
        self.fill_value = flv
 
1969
    #
 
1970
    def __reduce__(self):
 
1971
        """Returns a 3-tuple for pickling a MaskedArray."""
 
1972
        return (_mareconstruct,
 
1973
                (self.__class__, self._baseclass, (0,), 'b', ),
 
1974
                self.__getstate__())
 
1975
    
 
1976
    
 
1977
def _mareconstruct(subtype, baseclass, baseshape, basetype,):
 
1978
    """Internal function that builds a new MaskedArray from the information stored
 
1979
in a pickle."""
 
1980
    _data = ndarray.__new__(baseclass, baseshape, basetype)
 
1981
    _mask = ndarray.__new__(ndarray, baseshape, 'b1')
 
1982
    return subtype.__new__(subtype, _data, mask=_mask, dtype=basetype, small_mask=False)
 
1983
#MaskedArray.__dump__ = dump
 
1984
#MaskedArray.__dumps__ = dumps
 
1985
    
 
1986
    
 
1987
 
 
1988
#####--------------------------------------------------------------------------
 
1989
#---- --- Shortcuts ---
 
1990
#####---------------------------------------------------------------------------
 
1991
def isMaskedArray(x):
 
1992
    "Is x a masked array, that is, an instance of MaskedArray?"
 
1993
    return isinstance(x, MaskedArray)
 
1994
isarray = isMaskedArray
 
1995
isMA = isMaskedArray  #backward compatibility
 
1996
#masked = MaskedArray(0, int, mask=1)
 
1997
masked_singleton = MaskedArray(0, dtype=int_, mask=True)
 
1998
masked = masked_singleton
 
1999
 
 
2000
masked_array = MaskedArray
 
2001
def array(data, dtype=None, copy=False, order=False, mask=nomask, subok=True,
 
2002
          keep_mask=True, small_mask=True, hard_mask=None, fill_value=None):
 
2003
    """array(data, dtype=None, copy=True, order=False, mask=nomask,
 
2004
             keep_mask=True, small_mask=True, fill_value=None)
 
2005
Acts as shortcut to MaskedArray, with options in a different order for convenience.
 
2006
And backwards compatibility...
 
2007
    """
 
2008
    #TODO: we should try to put 'order' somwehere
 
2009
    return MaskedArray(data, mask=mask, dtype=dtype, copy=copy, subok=subok,
 
2010
                       keep_mask=keep_mask, small_mask=small_mask,
 
2011
                       hard_mask=hard_mask, fill_value=fill_value)
 
2012
 
 
2013
def is_masked(x):
 
2014
    """Returns whether x has some masked values."""
 
2015
    m = getmask(x)
 
2016
    if m is nomask:
 
2017
        return False
 
2018
    elif m.any():
 
2019
        return True
 
2020
    return False
 
2021
 
 
2022
 
 
2023
#####---------------------------------------------------------------------------
 
2024
#---- --- Extrema functions ---
 
2025
#####---------------------------------------------------------------------------
 
2026
class _extrema_operation(object):
 
2027
    "Generic class for maximum/minimum functions."
 
2028
    def __call__(self, a, b=None):
 
2029
        "Executes the call behavior."
 
2030
        if b is None:
 
2031
            return self.reduce(a)
 
2032
        return where(self.compare(a, b), a, b)
 
2033
    #.........
 
2034
    def reduce(self, target, axis=None):
 
2035
        """Reduces target along the given axis."""
 
2036
        m = getmask(target)
 
2037
        if axis is not None:
 
2038
            kargs = { 'axis' : axis }
 
2039
        else:
 
2040
            kargs = {}
 
2041
            target = target.ravel()
 
2042
            if not (m is nomask):
 
2043
                m = m.ravel()
 
2044
        if m is nomask:
 
2045
            t = self.ufunc.reduce(target, **kargs)
 
2046
        else:
 
2047
            target = target.filled(self.fill_value_func(target)).view(type(target))
 
2048
            t = self.ufunc.reduce(target, **kargs)
 
2049
            m = umath.logical_and.reduce(m, **kargs)
 
2050
            if hasattr(t, '_mask'):
 
2051
                t._mask = m
 
2052
            elif m:
 
2053
                t = masked
 
2054
        return t
 
2055
    #.........
 
2056
    def outer (self, a, b):
 
2057
        "Returns the function applied to the outer product of a and b."
 
2058
        ma = getmask(a)
 
2059
        mb = getmask(b)
 
2060
        if ma is nomask and mb is nomask:
 
2061
            m = nomask
 
2062
        else:
 
2063
            ma = getmaskarray(a)
 
2064
            mb = getmaskarray(b)
 
2065
            m = logical_or.outer(ma, mb)
 
2066
        result = self.ufunc.outer(filled(a), filled(b))
 
2067
        result._mask = m
 
2068
        return result
 
2069
#............................
 
2070
class _minimum_operation(_extrema_operation):
 
2071
    "Object to calculate minima"
 
2072
    def __init__ (self):
 
2073
        """minimum(a, b) or minimum(a)
 
2074
In one argument case, returns the scalar minimum.
 
2075
        """
 
2076
        self.ufunc = umath.minimum
 
2077
        self.afunc = amin
 
2078
        self.compare = less
 
2079
        self.fill_value_func = minimum_fill_value
 
2080
#............................
 
2081
class _maximum_operation(_extrema_operation):
 
2082
    "Object to calculate maxima"
 
2083
    def __init__ (self):
 
2084
        """maximum(a, b) or maximum(a)
 
2085
           In one argument case returns the scalar maximum.
 
2086
        """
 
2087
        self.ufunc = umath.maximum
 
2088
        self.afunc = amax
 
2089
        self.compare = greater
 
2090
        self.fill_value_func = maximum_fill_value
 
2091
#..........................................................
 
2092
def min(array, axis=None, out=None):
 
2093
    """Returns the minima along the given axis.
 
2094
If `axis` is None, applies to the flattened array."""
 
2095
    if out is not None:
 
2096
        raise TypeError("Output arrays Unsupported for masked arrays")
 
2097
    if axis is None:
 
2098
        return minimum(array)
 
2099
    else:
 
2100
        return minimum.reduce(array, axis)
 
2101
#............................
 
2102
def max(obj, axis=None, out=None):
 
2103
    """Returns the maxima along the given axis.
 
2104
If `axis` is None, applies to the flattened array."""
 
2105
    if out is not None:
 
2106
        raise TypeError("Output arrays Unsupported for masked arrays")
 
2107
    if axis is None:
 
2108
        return maximum(obj)
 
2109
    else:
 
2110
        return maximum.reduce(obj, axis)
 
2111
#.............................
 
2112
def ptp(obj, axis=None):
 
2113
    """a.ptp(axis=None) =  a.max(axis)-a.min(axis)"""
 
2114
    try:
 
2115
        return obj.max(axis)-obj.min(axis)
 
2116
    except AttributeError:
 
2117
        return max(obj, axis=axis) - min(obj, axis=axis)
 
2118
 
 
2119
 
 
2120
#####---------------------------------------------------------------------------
 
2121
#---- --- Definition of functions from the corresponding methods ---
 
2122
#####---------------------------------------------------------------------------
 
2123
class _frommethod:
 
2124
    """Defines functions from existing MaskedArray methods.
 
2125
:ivar _methodname (String): Name of the method to transform.
 
2126
    """
 
2127
    def __init__(self, methodname):
 
2128
        self._methodname = methodname
 
2129
        self.__doc__ = self.getdoc()
 
2130
    def getdoc(self):
 
2131
        "Returns the doc of the function (from the doc of the method)."
 
2132
        try:
 
2133
            return getattr(MaskedArray, self._methodname).__doc__
 
2134
        except:
 
2135
            return getattr(numpy, self._methodname).__doc__
 
2136
    def __call__(self, a, *args, **params):
 
2137
        if isinstance(a, MaskedArray):
 
2138
            return getattr(a, self._methodname).__call__(*args, **params)
 
2139
        #FIXME ----
 
2140
        #As x is not a MaskedArray, we transform it to a ndarray with asarray
 
2141
        #... and call the corresponding method.
 
2142
        #Except that sometimes it doesn't work (try reshape([1,2,3,4],(2,2)))
 
2143
        #we end up with a "SystemError: NULL result without error in PyObject_Call"
 
2144
        #A dirty trick is then to call the initial numpy function...
 
2145
        method = getattr(fromnumeric.asarray(a), self._methodname)
 
2146
        try:
 
2147
            return method(*args, **params)
 
2148
        except SystemError:
 
2149
            return getattr(numpy,self._methodname).__call__(a, *args, **params)
 
2150
 
 
2151
all = _frommethod('all')
 
2152
anomalies = anom = _frommethod('anom')
 
2153
any = _frommethod('any')
 
2154
conjugate = _frommethod('conjugate')
 
2155
ids = _frommethod('ids')
 
2156
nonzero = _frommethod('nonzero')
 
2157
diagonal = _frommethod('diagonal')
 
2158
maximum = _maximum_operation()
 
2159
mean = _frommethod('mean')
 
2160
minimum = _minimum_operation ()
 
2161
product = _frommethod('prod')
 
2162
ptp = _frommethod('ptp')
 
2163
ravel = _frommethod('ravel')
 
2164
repeat = _frommethod('repeat')
 
2165
std = _frommethod('std')
 
2166
sum = _frommethod('sum')
 
2167
swapaxes = _frommethod('swapaxes')
 
2168
take = _frommethod('take')
 
2169
var = _frommethod('var')
 
2170
 
 
2171
#..............................................................................
 
2172
def power(a, b, third=None):
 
2173
    """Computes a**b elementwise.
 
2174
    Masked values are set to 1."""
 
2175
    if third is not None:
 
2176
        raise MAError, "3-argument power not supported."
 
2177
    ma = getmask(a)
 
2178
    mb = getmask(b)
 
2179
    m = mask_or(ma, mb)
 
2180
    fa = filled(a, 1)
 
2181
    fb = filled(b, 1)
 
2182
    if fb.dtype.char in typecodes["Integer"]:
 
2183
        return masked_array(umath.power(fa, fb), m)
 
2184
    md = make_mask((fa < 0), small_mask=1)
 
2185
    m = mask_or(m, md)
 
2186
    if m is nomask:
 
2187
        return masked_array(umath.power(fa, fb))
 
2188
    else:
 
2189
        fa[m] = 1
 
2190
        return masked_array(umath.power(fa, fb), m)
 
2191
 
 
2192
#..............................................................................
 
2193
def argsort(a, axis=None, kind='quicksort', order=None, fill_value=None):
 
2194
    """Returns an array of indices that sort 'a' along the specified axis.
 
2195
    Masked values are filled beforehand to `fill_value`.
 
2196
    If `fill_value` is None, uses the default for the data type.
 
2197
    Returns a numpy array.
 
2198
 
 
2199
:Keywords:
 
2200
    `axis` : Integer *[None]*
 
2201
        Axis to be indirectly sorted (default -1)
 
2202
    `kind` : String *['quicksort']*
 
2203
        Sorting algorithm (default 'quicksort')
 
2204
        Possible values: 'quicksort', 'mergesort', or 'heapsort'
 
2205
 
 
2206
    Returns: array of indices that sort 'a' along the specified axis.
 
2207
 
 
2208
    This method executes an indirect sort along the given axis using the
 
2209
    algorithm specified by the kind keyword. It returns an array of indices of
 
2210
    the same shape as 'a' that index data along the given axis in sorted order.
 
2211
 
 
2212
    The various sorts are characterized by average speed, worst case
 
2213
    performance, need for work space, and whether they are stable. A stable
 
2214
    sort keeps items with the same key in the same relative order. The three
 
2215
    available algorithms have the following properties:
 
2216
 
 
2217
    |------------------------------------------------------|
 
2218
    |    kind   | speed |  worst case | work space | stable|
 
2219
    |------------------------------------------------------|
 
2220
    |'quicksort'|   1   | O(n^2)      |     0      |   no  |
 
2221
    |'mergesort'|   2   | O(n*log(n)) |    ~n/2    |   yes |
 
2222
    |'heapsort' |   3   | O(n*log(n)) |     0      |   no  |
 
2223
    |------------------------------------------------------|
 
2224
 
 
2225
    All the sort algorithms make temporary copies of the data when the sort is not
 
2226
    along the last axis. Consequently, sorts along the last axis are faster and use
 
2227
    less space than sorts along other axis.
 
2228
    """
 
2229
    if fill_value is None:
 
2230
        fill_value = default_fill_value(a)
 
2231
    d = filled(a, fill_value)
 
2232
    if axis is None:
 
2233
        return d.argsort(kind=kind, order=order)
 
2234
    return d.argsort(axis, kind=kind, order=order)
 
2235
 
 
2236
def argmin(a, axis=None, fill_value=None):
 
2237
    """Returns the array of indices for the minimum values of `a` along the
 
2238
    specified axis.
 
2239
    Masked values are treated as if they had the value `fill_value`.
 
2240
    If `fill_value` is None, the default for the data type is used.
 
2241
    Returns a numpy array.
 
2242
 
 
2243
:Keywords:
 
2244
    `axis` : Integer *[None]*
 
2245
        Axis to be indirectly sorted (default -1)
 
2246
    `fill_value` : var *[None]*
 
2247
        Default filling value. If None, uses the data type default.
 
2248
    """
 
2249
    if fill_value is None:
 
2250
        fill_value = default_fill_value(a)
 
2251
    d = filled(a, fill_value)
 
2252
    return d.argmin(axis=axis)
 
2253
 
 
2254
def argmax(a, axis=None, fill_value=None):
 
2255
    """Returns the array of indices for the maximum values of `a` along the
 
2256
    specified axis.
 
2257
    Masked values are treated as if they had the value `fill_value`.
 
2258
    If `fill_value` is None, the default for the data type is used.
 
2259
    Returns a numpy array.
 
2260
 
 
2261
:Keywords:
 
2262
    `axis` : Integer *[None]*
 
2263
        Axis to be indirectly sorted (default -1)
 
2264
    `fill_value` : var *[None]*
 
2265
        Default filling value. If None, uses the data type default.
 
2266
    """
 
2267
    if fill_value is None:
 
2268
        fill_value = default_fill_value(a)
 
2269
        try:
 
2270
            fill_value = - fill_value
 
2271
        except:
 
2272
            pass
 
2273
    d = filled(a, fill_value)
 
2274
    return d.argmax(axis=axis)
 
2275
 
 
2276
def sort(a, axis=-1, kind='quicksort', order=None, endwith=True, fill_value=None):
 
2277
    """
 
2278
    Sort a along the given axis.
 
2279
 
 
2280
Keyword arguments:
 
2281
 
 
2282
axis  -- axis to be sorted (default -1)
 
2283
kind  -- sorting algorithm (default 'quicksort')
 
2284
         Possible values: 'quicksort', 'mergesort', or 'heapsort'.
 
2285
order -- If a has fields defined, then the order keyword can be the
 
2286
         field name to sort on or a list (or tuple) of field names
 
2287
         to indicate the order that fields should be used to define
 
2288
         the sort.
 
2289
endwith--Boolean flag indicating whether missing values (if any) should
 
2290
         be forced in the upper indices (at the end of the array) or
 
2291
         lower indices (at the beginning).
 
2292
 
 
2293
Returns: None.
 
2294
 
 
2295
This method sorts 'a' in place along the given axis using the algorithm
 
2296
specified by the kind keyword.
 
2297
 
 
2298
The various sorts may characterized by average speed, worst case
 
2299
performance, need for work space, and whether they are stable. A stable
 
2300
sort keeps items with the same key in the same relative order and is most
 
2301
useful when used with argsort where the key might differ from the items
 
2302
being sorted. The three available algorithms have the following properties:
 
2303
 
 
2304
|------------------------------------------------------|
 
2305
|    kind   | speed |  worst case | work space | stable|
 
2306
|------------------------------------------------------|
 
2307
|'quicksort'|   1   | O(n^2)      |     0      |   no  |
 
2308
|'mergesort'|   2   | O(n*log(n)) |    ~n/2    |   yes |
 
2309
|'heapsort' |   3   | O(n*log(n)) |     0      |   no  |
 
2310
|------------------------------------------------------|
 
2311
 
 
2312
All the sort algorithms make temporary copies of the data when the sort is
 
2313
not along the last axis. Consequently, sorts along the last axis are faster
 
2314
and use less space than sorts along other axis.
 
2315
 
 
2316
"""
 
2317
    a = numeric.asanyarray(a)
 
2318
    if fill_value is None:
 
2319
        if endwith:
 
2320
            filler = minimum_fill_value(a)
 
2321
        else:
 
2322
            filler = maximum_fill_value(a)
 
2323
    else:
 
2324
        filler = fill_value
 
2325
#    return
 
2326
    indx = numpy.indices(a.shape).tolist()
 
2327
    indx[axis] = filled(a,filler).argsort(axis=axis,kind=kind,order=order)
 
2328
    return a[indx]
 
2329
 
 
2330
def compressed(x):
 
2331
    """Returns a compressed version of a masked array (or just the array if it
 
2332
    wasn't masked first)."""
 
2333
    if getmask(x) is None:
 
2334
        return x
 
2335
    else:
 
2336
        return x.compressed()
 
2337
 
 
2338
def count(a, axis = None):
 
2339
    "Count of the non-masked elements in a, or along a certain axis."
 
2340
    a = masked_array(a)
 
2341
    return a.count(axis)
 
2342
 
 
2343
def concatenate(arrays, axis=0):
 
2344
    "Concatenates the arrays along the given axis"
 
2345
    d = numeric.concatenate([filled(a) for a in arrays], axis)
 
2346
    rcls = get_masked_subclass(*arrays)
 
2347
    data = d.view(rcls)
 
2348
    for x in arrays:
 
2349
        if getmask(x) is not nomask:
 
2350
            break
 
2351
    else:
 
2352
        return data
 
2353
    dm = numeric.concatenate([getmaskarray(a) for a in arrays], axis)
 
2354
    dm = make_mask(dm, copy=False, small_mask=True)
 
2355
    data._mask = dm
 
2356
    return data
 
2357
 
 
2358
def expand_dims(x,axis):
 
2359
    """Expand the shape of a by including newaxis before given axis."""
 
2360
    result = n_expand_dims(x,axis)
 
2361
    if isinstance(x, MaskedArray):
 
2362
        new_shape = result.shape
 
2363
        result = x.view()
 
2364
        result.shape = new_shape
 
2365
        if result._mask is not nomask:
 
2366
            result._mask.shape = new_shape
 
2367
    return result
 
2368
 
 
2369
#......................................
 
2370
def left_shift (a, n):
 
2371
    "Left shift n bits"
 
2372
    m = getmask(a)
 
2373
    if m is nomask:
 
2374
        d = umath.left_shift(filled(a), n)
 
2375
        return masked_array(d)
 
2376
    else:
 
2377
        d = umath.left_shift(filled(a, 0), n)
 
2378
        return masked_array(d, mask=m)
 
2379
 
 
2380
def right_shift (a, n):
 
2381
    "Right shift n bits"
 
2382
    m = getmask(a)
 
2383
    if m is nomask:
 
2384
        d = umath.right_shift(filled(a), n)
 
2385
        return masked_array(d)
 
2386
    else:
 
2387
        d = umath.right_shift(filled(a, 0), n)
 
2388
        return masked_array(d, mask=m)
 
2389
#......................................
 
2390
def put(a, indices, values, mode='raise'):
 
2391
    """Sets storage-indexed locations to corresponding values.
 
2392
    Values and indices are filled if necessary."""
 
2393
    # We can't use 'frommethod', the order of arguments is different
 
2394
    try:
 
2395
        return a.put(indices, values, mode=mode)
 
2396
    except AttributeError:
 
2397
        return fromnumeric.asarray(a).put(indices, values, mode=mode)
 
2398
 
 
2399
def putmask(a, mask, values): #, mode='raise'):
 
2400
    """`putmask(a, mask, v)` results in `a = v` for all places where `mask` is true.
 
2401
If `v` is shorter than `mask`, it will be repeated as necessary.
 
2402
In particular `v` can be a scalar or length 1 array."""
 
2403
    # We can't use 'frommethod', the order of arguments is different
 
2404
    try:
 
2405
        return a.putmask(values, mask)
 
2406
    except AttributeError:
 
2407
        return fromnumeric.asarray(a).putmask(values, mask)
 
2408
 
 
2409
def transpose(a,axes=None):
 
2410
    """Returns a view of the array with dimensions permuted according to axes.
 
2411
If `axes` is None (default), returns array with dimensions reversed.
 
2412
    """
 
2413
    #We can't use 'frommethod', as 'transpose' doesn't take keywords
 
2414
    try:
 
2415
        return a.transpose(axes)
 
2416
    except AttributeError:
 
2417
        return fromnumeric.asarray(a).transpose(axes)
 
2418
 
 
2419
def reshape(a, new_shape):
 
2420
    """Changes the shape of the array `a` to `new_shape`."""
 
2421
    #We can't use 'frommethod', it whine about some parameters. Dmmit.
 
2422
    try:
 
2423
        return a.reshape(new_shape)
 
2424
    except AttributeError:
 
2425
        return fromnumeric.asarray(a).reshape(new_shape)
 
2426
 
 
2427
def resize(x, new_shape):
 
2428
    """resize(a,new_shape) returns a new array with the specified shape.
 
2429
    The total size of the original array can be any size.
 
2430
    The new array is filled with repeated copies of a. If a was masked, the new
 
2431
    array will be masked, and the new mask will be a repetition of the old one.
 
2432
    """
 
2433
    # We can't use _frommethods here, as N.resize is notoriously whiny.
 
2434
    m = getmask(x)
 
2435
    if m is not nomask:
 
2436
        m = fromnumeric.resize(m, new_shape)
 
2437
    result = fromnumeric.resize(x, new_shape).view(get_masked_subclass(x))
 
2438
    if result.ndim:
 
2439
        result._mask = m
 
2440
    return result
 
2441
 
 
2442
 
 
2443
#................................................
 
2444
def rank(obj):
 
2445
    """Gets the rank of sequence a (the number of dimensions, not a matrix rank)
 
2446
The rank of a scalar is zero."""
 
2447
    return fromnumeric.rank(filled(obj))
 
2448
#
 
2449
def shape(obj):
 
2450
    """Returns the shape of `a` (as a function call which also works on nested sequences).
 
2451
    """
 
2452
    return fromnumeric.shape(filled(obj))
 
2453
#
 
2454
def size(obj, axis=None):
 
2455
    """Returns the number of elements in the array along the given axis,
 
2456
or in the sequence if `axis` is None.
 
2457
    """
 
2458
    return fromnumeric.size(filled(obj), axis)
 
2459
#................................................
 
2460
 
 
2461
#####--------------------------------------------------------------------------
 
2462
#---- --- Extra functions ---
 
2463
#####--------------------------------------------------------------------------
 
2464
def where (condition, x, y):
 
2465
    """where(condition, x, y) is x where condition is nonzero, y otherwise.
 
2466
       condition must be convertible to an integer array.
 
2467
       Answer is always the shape of condition.
 
2468
       The type depends on x and y. It is integer if both x and y are
 
2469
       the value masked.
 
2470
    """
 
2471
    fc = filled(not_equal(condition, 0), 0)
 
2472
    xv = filled(x)
 
2473
    xm = getmask(x)
 
2474
    yv = filled(y)
 
2475
    ym = getmask(y)
 
2476
    d = numeric.choose(fc, (yv, xv))
 
2477
    md = numeric.choose(fc, (ym, xm))
 
2478
    m = getmask(condition)
 
2479
    m = make_mask(mask_or(m, md), copy=False, small_mask=True)
 
2480
    return masked_array(d, mask=m)
 
2481
 
 
2482
def choose (indices, t, out=None, mode='raise'):
 
2483
    "Returns array shaped like indices with elements chosen from t"
 
2484
    #TODO: implement options `out` and `mode`, if possible.
 
2485
    def fmask (x):
 
2486
        "Returns the filled array, or True if ``masked``."
 
2487
        if x is masked:
 
2488
            return 1
 
2489
        return filled(x)
 
2490
    def nmask (x):
 
2491
        "Returns the mask, True if ``masked``, False if ``nomask``."
 
2492
        if x is masked:
 
2493
            return 1
 
2494
        m = getmask(x)
 
2495
        if m is nomask:
 
2496
            return 0
 
2497
        return m
 
2498
    c = filled(indices, 0)
 
2499
    masks = [nmask(x) for x in t]
 
2500
    a = [fmask(x) for x in t]
 
2501
    d = numeric.choose(c, a)
 
2502
    m = numeric.choose(c, masks)
 
2503
    m = make_mask(mask_or(m, getmask(indices)), copy=0, small_mask=1)
 
2504
    return masked_array(d, mask=m)
 
2505
 
 
2506
def round_(a, decimals=0, out=None):
 
2507
    """Returns reference to result. Copies a and rounds to 'decimals' places.
 
2508
 
 
2509
    Keyword arguments:
 
2510
        decimals -- number of decimals to round to (default 0). May be negative.
 
2511
        out -- existing array to use for output (default copy of a).
 
2512
 
 
2513
    Return:
 
2514
        Reference to out, where None specifies a copy of the original array a.
 
2515
 
 
2516
    Round to the specified number of decimals. When 'decimals' is negative it
 
2517
    specifies the number of positions to the left of the decimal point. The
 
2518
    real and imaginary parts of complex numbers are rounded separately.
 
2519
    Nothing is done if the array is not of float type and 'decimals' is greater
 
2520
    than or equal to 0."""
 
2521
    result = fromnumeric.round_(filled(a), decimals, out)
 
2522
    if isinstance(a,MaskedArray):
 
2523
        result = result.view(type(a))
 
2524
        result._mask = a._mask
 
2525
    else:
 
2526
        result = result.view(MaskedArray)
 
2527
    return result
 
2528
 
 
2529
def arange(start, stop=None, step=1, dtype=None):
 
2530
    """Just like range() except it returns a array whose type can be specified
 
2531
    by the keyword argument dtype.
 
2532
    """
 
2533
    return array(numeric.arange(start, stop, step, dtype),mask=nomask)
 
2534
 
 
2535
def inner(a, b):
 
2536
    """inner(a,b) returns the dot product of two arrays, which has
 
2537
    shape a.shape[:-1] + b.shape[:-1] with elements computed by summing the
 
2538
    product of the elements from the last dimensions of a and b.
 
2539
    Masked elements are replace by zeros.
 
2540
    """
 
2541
    fa = filled(a, 0)
 
2542
    fb = filled(b, 0)
 
2543
    if len(fa.shape) == 0:
 
2544
        fa.shape = (1,)
 
2545
    if len(fb.shape) == 0:
 
2546
        fb.shape = (1,)
 
2547
    return masked_array(numeric.inner(fa, fb))
 
2548
innerproduct = inner
 
2549
 
 
2550
def outer(a, b):
 
2551
    """outer(a,b) = {a[i]*b[j]}, has shape (len(a),len(b))"""
 
2552
    fa = filled(a, 0).ravel()
 
2553
    fb = filled(b, 0).ravel()
 
2554
    d = numeric.outer(fa, fb)
 
2555
    ma = getmask(a)
 
2556
    mb = getmask(b)
 
2557
    if ma is nomask and mb is nomask:
 
2558
        return masked_array(d)
 
2559
    ma = getmaskarray(a)
 
2560
    mb = getmaskarray(b)
 
2561
    m = make_mask(1-numeric.outer(1-ma, 1-mb), copy=0)
 
2562
    return masked_array(d, mask=m)
 
2563
outerproduct = outer
 
2564
 
 
2565
def allequal (a, b, fill_value=True):
 
2566
    """
 
2567
Returns `True` if all entries of  a and b are equal, using
 
2568
fill_value as a truth value where either or both are masked.
 
2569
    """
 
2570
    m = mask_or(getmask(a), getmask(b))
 
2571
    if m is nomask:
 
2572
        x = filled(a)
 
2573
        y = filled(b)
 
2574
        d = umath.equal(x, y)
 
2575
        return d.all()
 
2576
    elif fill_value:
 
2577
        x = filled(a)
 
2578
        y = filled(b)
 
2579
        d = umath.equal(x, y)
 
2580
        dm = array(d, mask=m, copy=False)
 
2581
        return dm.filled(True).all(None)
 
2582
    else:
 
2583
        return False
 
2584
 
 
2585
def allclose (a, b, fill_value=True, rtol=1.e-5, atol=1.e-8):
 
2586
    """ Returns `True` if all elements of `a` and `b` are equal subject to given tolerances.
 
2587
If `fill_value` is True, masked values are considered equal.
 
2588
If `fill_value` is False, masked values considered unequal.
 
2589
The relative error rtol should be positive and << 1.0
 
2590
The absolute error `atol` comes into play for those elements of `b`
 
2591
 that are very small or zero; it says how small `a` must be also.
 
2592
    """
 
2593
    m = mask_or(getmask(a), getmask(b))
 
2594
    d1 = filled(a)
 
2595
    d2 = filled(b)
 
2596
    x = filled(array(d1, copy=0, mask=m), fill_value).astype(float)
 
2597
    y = filled(array(d2, copy=0, mask=m), 1).astype(float)
 
2598
    d = umath.less_equal(umath.absolute(x-y), atol + rtol * umath.absolute(y))
 
2599
    return fromnumeric.alltrue(fromnumeric.ravel(d))
 
2600
 
 
2601
#..............................................................................
 
2602
def asarray(a, dtype=None):
 
2603
    """asarray(data, dtype) = array(data, dtype, copy=0)
 
2604
Returns `a` as an masked array.
 
2605
No copy is performed if `a` is already an array.
 
2606
Subclasses are converted to base class MaskedArray.
 
2607
    """
 
2608
    return masked_array(a, dtype=dtype, copy=False, keep_mask=True)
 
2609
 
 
2610
def empty(new_shape, dtype=float):
 
2611
    """empty((d1,...,dn),dtype=float,order='C')
 
2612
Returns a new array of shape (d1,...,dn) and given type with all its
 
2613
entries uninitialized. This can be faster than zeros."""
 
2614
    return numeric.empty(new_shape, dtype).view(MaskedArray)
 
2615
 
 
2616
def empty_like(a):
 
2617
    """empty_like(a)
 
2618
Returns an empty (uninitialized) array of the shape and typecode of a.
 
2619
Note that this does NOT initialize the returned array.
 
2620
If you require your array to be initialized, you should use zeros_like()."""
 
2621
    return numeric.empty_like(a).view(MaskedArray)
 
2622
 
 
2623
def ones(new_shape, dtype=float):
 
2624
    """ones(shape, dtype=None)
 
2625
Returns an array of the given dimensions, initialized to all ones."""
 
2626
    return numeric.ones(new_shape, dtype).view(MaskedArray)
 
2627
 
 
2628
def zeros(new_shape, dtype=float):
 
2629
    """zeros(new_shape, dtype=None)
 
2630
Returns an array of the given dimensions, initialized to all zeros."""
 
2631
    return numeric.zeros(new_shape, dtype).view(MaskedArray)
 
2632
 
 
2633
#####--------------------------------------------------------------------------
 
2634
#---- --- Pickling ---
 
2635
#####--------------------------------------------------------------------------
 
2636
def dump(a,F):
 
2637
    """Pickles the MaskedArray `a` to the file `F`.
 
2638
`F` can either be the handle of an exiting file, or a string representing a file name.
 
2639
    """
 
2640
    if not hasattr(F,'readline'):
 
2641
        F = open(F,'w')
 
2642
    return cPickle.dump(a,F)
 
2643
 
 
2644
def dumps(a):
 
2645
    """Returns a string corresponding to the pickling of the MaskedArray."""
 
2646
    return cPickle.dumps(a)
 
2647
 
 
2648
def load(F):
 
2649
    """Wrapper around ``cPickle.load`` which accepts either a file-like object or
 
2650
 a filename."""
 
2651
    if not hasattr(F, 'readline'):
 
2652
        F = open(F,'r')
 
2653
    return cPickle.load(F)
 
2654
 
 
2655
def loads(strg):
 
2656
    "Loads a pickle from the current string."""
 
2657
    return cPickle.loads(strg)
 
2658
 
 
2659
 
 
2660
################################################################################
 
2661
 
 
2662
if __name__ == '__main__':
 
2663
    from testutils import assert_equal, assert_almost_equal
 
2664
    if 1:
 
2665
        x = arange(10)
 
2666
        assert(x.ctypes.data == x.filled().ctypes.data)
 
2667
    if 0:
 
2668
        a = array([1,2,3,4],mask=[0,0,0,0],small_mask=True)
 
2669
        a[1] = masked
 
2670
        a[1] = 1
 
2671
        assert(a.ravel()._mask, [0,0,0,0])
 
2672
        assert(a.compressed(), a)
 
2673
        a[0] = masked
 
2674
        assert(a.compressed()._mask, [0,0,0])
 
2675
    if 0:
 
2676
        x = array(0, mask=0)
 
2677
        I = x.ctypes.data
 
2678
        J = x.filled().ctypes.data
 
2679
        print (I,J)
 
2680
        x = array([0,0], mask=0)
 
2681
        (I,J) = (x.ctypes.data, x.filled().ctypes.data)
 
2682
        print (I,J)
 
2683
    if 1:
 
2684
        x = array(numpy.arange(12))
 
2685
        x[[1,-2]] = masked
 
2686
        xlist = x.tolist()
 
2687
        assert(xlist[1] is None)
 
2688
        assert(xlist[-2] is None)
 
2689
        #
 
2690
        x.shape = (3,4) 
 
2691
        xlist = x.tolist()
 
2692
        #
 
2693
        assert_equal(xlist[0],[0,None,2,3])
 
2694
        assert_equal(xlist[1],[4,5,6,7])
 
2695
        assert_equal(xlist[2],[8,9,None,11])
 
2696
        
 
2697
        
 
2698
        
 
 
b'\\ No newline at end of file'