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.
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
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)
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 $
19
__author__ = "Pierre GF Gerard-Marchant ($Author: pierregm $)"
21
__revision__ = "$Revision: 3245 $"
22
__date__ = '$Date: 2007-08-15 06:38:19 -0700 (Wed, 15 Aug 2007) $'
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',
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',
66
from numpy import bool_, complex_, float_, int_, object_, str_
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
80
divide_tolerance = 1.e-35
81
numpy.seterr(all='ignore')
83
# TODO: There's still a problem with N.add.reduce not working...
84
# TODO: ...neither does N.add.accumulate
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)
96
"Calculates the string representation."
100
#####--------------------------------------------------------------------------
101
#---- --- Filling options ---
102
#####--------------------------------------------------------------------------
103
# b: boolean - c: complex - f: floats - i: integer - O: object - S: string
104
default_filler = {'b': True,
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)])
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']
137
defval = default_filler['O']
140
def minimum_fill_value(obj):
141
"Calculates the default fill value suitable for taking the minimum of `obj`."
142
if hasattr(obj, 'dtype'):
144
filler = min_filler[objtype]
146
raise TypeError, 'Unsuitable type for calculating minimum.'
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]
157
raise TypeError, 'Unsuitable type for calculating minimum.'
159
def maximum_fill_value(obj):
160
"Calculates the default fill value suitable for taking the maximum of `obj`."
161
if hasattr(obj, 'dtype'):
163
filler = max_filler[objtype]
165
raise TypeError, 'Unsuitable type for calculating minimum.'
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]
176
raise TypeError, 'Unsuitable type for calculating minimum.'
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)
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.
187
if isinstance(a, MaskedArray):
188
result = a.fill_value
190
result = default_fill_value(a)
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)
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)`
207
If `a` is already a contiguous numeric array, `a` itself is returned.
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.
212
if hasattr(a, 'filled'):
213
return a.filled(value)
214
elif isinstance(a, ndarray): # and a.flags['CONTIGUOUS']:
216
elif isinstance(a, dict):
217
return numeric.array(a, 'O')
219
return numeric.array(a)
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."""
226
if isinstance(arr, MaskedArray):
231
arrcls = [type(a) for a in arrays]
233
if not issubclass(rcls, MaskedArray):
235
for cls in arrcls[1:]:
236
if issubclass(cls, rcls):
240
#####--------------------------------------------------------------------------
242
#####--------------------------------------------------------------------------
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"
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
#............................
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)"
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
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
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.
304
- `fill` : Default filling value *[0]*.
305
- `domain` : Default domain *[None]*.
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.
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
321
def __call__ (self, a, *args, **kwargs):
322
"Execute the call behavior."
323
# numeric tries to return scalars rather than arrays when given scalars.
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:
332
if isinstance(a, MaskedArray):
333
result = self.f(d1, *args, **kwargs).view(type(a))
335
result = self.f(d1, *args, **kwargs).view(MaskedArray)
336
# Fix the mask if we don't have a scalar
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.
350
- `fillx` : Default filling value for first array*[0]*.
351
- `filly` : Default filling value for second array*[0]*.
352
- `domain` : Default domain *[None]*.
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.
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)
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:
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))
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)
387
t = filled(target, self.filly)
391
m = make_mask(m, copy=1)
394
return self.f.reduce(t, axis).view(tclass)
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)
409
def outer (self, a, b):
410
"Returns the function applied to the outer product of a and b."
413
if ma is nomask and mb is nomask:
418
m = umath.logical_or.outer(ma, mb)
419
if (not m.ndim) and m:
421
rcls = get_masked_subclass(a,b)
422
d = self.f.outer(filled(a, self.fillx), filled(b, self.filly)).view(rcls)
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)
432
tclass = masked_array
433
t = filled(target, self.filly)
434
return self.f.accumulate(t, axis).view(tclass)
437
return "Masked version of " + str(self.f)
438
#..............................................................................
439
class domained_binary_operation:
440
"""Defines binary operations that have a domain, like divide.
442
These are complicated so they are a separate class.
443
They have no reduce, outer or accumulate.
447
- `fillx` : Default filling value for first array*[0]*.
448
- `filly` : Default filling value for second array*[0]*.
449
- `domain` : Default domain *[None]*.
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.
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)
464
def __call__(self, a, b):
465
"Execute the call behavior."
468
d1 = filled(a, self.fillx)
469
d2 = filled(b, self.filly)
470
t = numeric.asarray(self.domain(d1, d2))
472
if fromnumeric.sometrue(t, None):
473
d2 = numeric.where(t, self.filly, d2)
476
if (not m.ndim) and m:
478
result = self.f(d1, d2).view(get_masked_subclass(a,b))
484
return "Masked version of " + str(self.f)
486
#..............................................................................
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))
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)
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)
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)
554
#####--------------------------------------------------------------------------
555
#---- --- Mask creation functions ---
556
#####--------------------------------------------------------------------------
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"):
567
"""Returns the mask of `a`, if any.
568
Otherwise, returns an array of `False`, with the same shape as `a`.
572
return make_mask_none(fromnumeric.shape(a))
577
"""Returns `True` if `m` is a legal mask.
578
Does not check contents, only type.
581
return m.dtype.type is MaskType
582
except AttributeError:
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.
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.
598
warnings.warn("The flag 'flag' is now called 'small_mask'!",
603
elif isinstance(m, ndarray):
605
if m.dtype.type is MaskType:
607
result = numeric.array(m, dtype=MaskType, copy=copy)
611
result = numeric.array(m, dtype=MaskType)
613
result = numeric.array(filled(m, True), dtype=MaskType)
615
if small_mask and not result.any():
620
def make_mask_none(s):
621
"Returns a mask of shape `s`, filled with `False`."
622
result = numeric.zeros(s, dtype=MaskType)
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.
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.
636
return make_mask(m2, copy=copy, small_mask=small_mask)
638
return make_mask(m1, copy=copy, small_mask=small_mask)
639
if m1 is m2 and is_mask(m1):
641
return make_mask(umath.logical_or(m1, m2), copy=copy, small_mask=small_mask)
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.
651
- `condition` (ndarray) : Masking condition.
652
- `x` (ndarray) : Array to mask.
653
- `copy` (boolean, *[False]*) : Returns a copy of `m` if true.
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)
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)
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)
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)
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)
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)
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.
691
return masked_where((x == value), x, copy=copy)
693
# c = umath.equal(d, value)
694
# m = mask_or(c, getmask(x))
695
# return array(d, mask=m, copy=copy)
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.
705
condition = (xf >= v1) & (xf <= v2)
706
return masked_where(condition, x, copy=copy)
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.
716
condition = (xf < v1) | (xf > v2)
717
return masked_where(condition, x, copy=copy)
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.
726
:parameter copy (Boolean, *[True]*): Returns a copy of `x` if true. """
728
condition = umath.equal(x._data, value)
731
condition = umath.equal(fromnumeric.asarray(x), value)
733
mask = mask_or(mask, make_mask(condition, small_mask=True))
734
return masked_array(x, mask=mask, copy=copy, fill_value=value)
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.
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.
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))
753
except AttributeError:
756
condition = umath.equal(xnew, value)
758
mask = mask_or(mask, make_mask(condition, small_mask=True))
759
return masked_array(xnew, mask=mask, copy=copy, fill_value=value)
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
772
"Displays the string to print for masked values."
775
def set_display (self, s):
776
"Sets the string to print for masked values."
780
"Is the use of the display value enabled?"
783
def enable(self, small_mask=1):
784
"Set the enabling small_mask to `small_mask`."
785
self._enabled = small_mask
788
return str(self._display)
792
#if you single index into a masked location you get this object.
793
masked_print_option = _MaskedPrintOption('--')
795
#####--------------------------------------------------------------------------
796
#---- --- MaskedArray class ---
797
#####--------------------------------------------------------------------------
798
##def _getoptions(a_out, a_in):
799
## "Copies standards options of a_in to a_out."
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.
806
# def __init__ (self, methodname, fill_self=0, fill_other=0, domain=None):
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.
814
# self.methodname = methodname
815
# self.fill_self = fill_self
816
# self.fill_other = fill_other
817
# self.domain = domain
819
# self.__doc__ = self.getdoc()
822
# "Returns the doc of the function (from the doc of the method)."
824
# return getattr(MaskedArray, self.methodname).__doc__
826
# return getattr(ndarray, self.methodname).__doc__
828
# def __get__(self, obj, objtype=None):
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)
844
# domain = self.domain(target, base)
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
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
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))
864
# except AttributeError:
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`.
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.
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).
883
def __init__(self, funcname, onmask=True):
884
self._name = funcname
885
self._onmask = onmask
887
self.__doc__ = self.getdoc()
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__
897
# return getattr(MaskedArray, self._name).__doc__
900
# return getattr(numpy, self._name).__doc__
902
# return getattr(ndarray, self._name).__doc
904
def __get__(self, obj, objtype=None):
908
def __call__(self, *args, **params):
909
methodname = self._name
910
data = self.obj._data
911
mask = self.obj._mask
913
result = getattr(data, methodname)(*args, **params).view(cls)
914
result._smallmask = self.obj._smallmask
918
elif mask is not nomask:
919
result.__setmask__(getattr(mask, methodname)(*args, **params))
921
#..........................................................
923
class flatiter(object):
924
"Defines an interator."
925
def __init__(self, ma):
927
self.ma_iter = numpy.asarray(ma).flat
929
if ma._mask is nomask:
932
self.maskiter = ma._mask.flat
937
### This won't work is ravel makes a copy
938
def __setitem__(self, index, value):
943
d = self.ma_iter.next()
944
if self.maskiter is not None and self.maskiter.next():
949
class MaskedArray(numeric.ndarray):
950
"""Arrays with possibly masked values.
951
Masked values of True exclude the corresponding element from any computation.
954
x = array(data, dtype=None, copy=True, order=False,
955
mask = nomask, fill_value=None, small_mask=True)
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.
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)
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.
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.
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)
985
If `data` is already a ndarray, its dtype becomes the default value of dtype.
988
warnings.warn("The flag 'flag' is now called 'small_mask'!",
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)
998
_data = data.view(cls)
1000
_data = _data.view(type(data))
1001
# Backwards compat .......
1002
if hasattr(data,'_mask') and not isinstance(data, ndarray):
1003
_data._mask = data._mask
1005
# Process mask ...........
1008
_data._mask = nomask
1010
_data._mask = _data._mask.copy()
1012
mask = numeric.array(mask, dtype=MaskType, copy=copy)
1013
if mask.shape != _data.shape:
1014
(nd, nm) = (_data.size, mask.size)
1016
mask = numeric.resize(mask, _data.shape)
1018
mask = fromnumeric.reshape(mask, _data.shape)
1020
msg = "Mask and data not compatible: data size is %i, "+\
1022
raise MAError, msg % (nd, nm)
1023
if _data._mask is nomask:
1025
_data._sharedmask = True
1027
# Make a copy of the mask to avoid propagation
1028
_data._sharedmask = False
1032
_data._mask = umath.logical_or(mask, _data._mask)
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
1045
#........................
1046
def __array_finalize__(self,obj):
1047
"""Finalizes the masked array.
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)
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.
1069
#TODO : Should we check for type result
1070
result = obj.view(type(self))
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])
1077
domain = ufunc_domain.get(func, None)
1078
if domain is not None:
1080
d = reduce(domain, args)
1088
if not m.ndim and m:
1090
if result.shape == ():
1092
result._mask = numeric.ones(result.shape, bool_)
1097
result._fill_value = self._fill_value
1098
result._hardmask = self._hardmask
1099
result._smallmask = self._smallmask
1100
result._baseclass = self._baseclass
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.
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)
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
1119
# use _set_mask to take care of the shape
1120
dout.__setmask__(m[indx])
1121
elif m is not nomask and m[indx]:
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.
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
1138
m = make_mask_none(self.shape)
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:
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)
1161
mindx = mask_or(self._mask[indx], valmask, copy=True)
1162
dindx = self._data[indx]
1164
dindx[~mindx] = dval
1165
elif mindx is nomask:
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)
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:
1200
self._mask.flat = newmask
1201
if self._mask.shape:
1202
self._mask = numeric.reshape(self._mask, self.shape)
1203
_set_mask = __setmask__
1205
def _get_mask(self):
1206
"""Returns the current mask."""
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
1215
def soften_mask(self):
1216
"Forces the mask to soft."
1217
self._hardmask = False
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
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.
1234
return flatiter(self)
1236
def _set_flat (self, value):
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
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."""
1253
value = default_fill_value(self)
1254
self._fill_value = value
1256
fill_value = property(fget=get_fill_value, fset=set_fill_value,
1257
doc="Filling value")
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.
1264
If `fill_value` is None, uses self.fill_value.
1267
if m is nomask or not m.any():
1270
if fill_value is None:
1271
fill_value = self.fill_value
1273
if self is masked_singleton:
1274
result = numeric.asanyarray(fill_value)
1276
result = self._data.copy()
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))
1285
if self._data.shape:
1288
result = numeric.array(fill_value, dtype=self.dtype)
1293
def compressed(self):
1294
"A 1-D array of all the non-masked data."
1296
if self._mask is nomask:
1298
elif not self._smallmask and not self._mask.any():
1301
return d[numeric.logical_not(d._mask)]
1302
#............................................
1304
"""x.__str__() <==> str(x)
1305
Calculates the string representation, using masked for fill if it is enabled.
1306
Otherwise, fills with fill value.
1308
if masked_print_option.enabled():
1309
f = masked_print_option
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")
1328
res = self.filled(self.fill_value)
1332
"""x.__repr__() <==> repr(x)
1333
Calculates the repr representation, using masked for fill if it is enabled.
1334
Otherwise fill with fill value.
1337
masked_%(name)s(data =
1341
fill_value=%(fill)s)
1344
masked_%(name)s(data = %(data)s,
1346
fill_value=%(fill)s)
1349
name = repr(self._data).split('(')[0]
1351
return with_mask1 % {
1354
'mask': str(self._mask),
1355
'fill': str(self.fill_value),
1357
return with_mask % {
1360
'mask': str(self._mask),
1361
'fill': str(self.fill_value),
1363
#............................................
1364
def __iadd__(self, other):
1365
"Adds other to self in place."
1366
ndarray.__iadd__(self._data,other)
1368
if self._mask is nomask:
1370
elif m is not nomask:
1374
def __isub__(self, other):
1375
"Subtracts other from self in place."
1376
ndarray.__isub__(self._data,other)
1378
if self._mask is nomask:
1380
elif m is not nomask:
1384
def __imul__(self, other):
1385
"Multiplies self by other in place."
1386
ndarray.__imul__(self._data,other)
1388
if self._mask is nomask:
1390
elif m is not nomask:
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)
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.")
1408
#raise MAError, 'Cannot convert masked element to a Python float.'
1409
return float(self.item())
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."""
1436
return numeric.ones(t) * n
1437
n1 = fromnumeric.size(m, axis)
1438
n2 = m.astype(int_).sum(axis)
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
1455
repeat = _arraymethod('repeat')
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.
1463
self._data.resize(newshape, refcheck, order)
1464
if self.mask is not nomask:
1465
self._mask.resize(newshape, refcheck, order)
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.")
1472
flatten = _arraymethod('flatten')
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,
1479
If `values` has some masked values, the initial mask is updated in consequence,
1480
else the corresponding values are unmasked.
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]
1492
self._data.put(indices, values, mode=mode)
1498
if getmask(values) is nomask:
1499
m.put(indices, False, mode=mode)
1501
m.put(indices, values._mask, mode=mode)
1502
m = make_mask(m, copy=False, small_mask=True)
1504
#............................................
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...
1516
d = self.filled(True).all(axis=axis, out=out).view(type(self))
1518
d.__setmask__(self._mask.all(axis))
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...
1528
d = self.filled(False).any(axis=axis, out=out).view(type(self))
1530
d.__setmask__(self._mask.all(axis))
1534
"""a.nonzero() returns a tuple of arrays
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
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`.
1552
# TODO: What are we doing with `out`?
1555
result = super(MaskedArray, self).trace(offset=offset, axis1=axis1,
1556
axis2=axis2, out=out)
1557
return result.astype(dtype)
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.
1568
if self._mask is nomask:
1571
mask = self._mask.all(axis)
1572
if (not mask.ndim) and mask:
1574
result = self.filled(0).sum(axis, dtype=dtype).view(type(self))
1576
result.__setmask__(mask)
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.
1585
result = self.filled(0).cumsum(axis=axis, dtype=dtype).view(type(self))
1586
result.__setmask__(self.mask)
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.
1595
if self._mask is nomask:
1598
mask = self._mask.all(axis)
1599
if (not mask.ndim) and mask:
1601
result = self.filled(1).prod(axis=axis, dtype=dtype).view(type(self))
1603
result.__setmask__(mask)
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.
1613
result = self.filled(1).cumprod(axis=axis, dtype=dtype).view(type(self))
1614
result.__setmask__(self.mask)
1617
def mean(self, axis=None, dtype=None):
1618
"""a.mean(axis=None, dtype=None)
1620
Averages the array over the given axis. If the axis is None,
1621
averages over all dimensions of the array. Equivalent to
1623
a.sum(axis, dtype) / size(a, axis).
1625
The optional dtype argument is the data type for intermediate
1626
calculations in the sum.
1628
Returns a masked array, of the same class as a.
1630
if self._mask is nomask:
1631
return super(MaskedArray, self).mean(axis=axis, dtype=dtype)
1633
dsum = self.sum(axis=axis, dtype=dtype)
1634
cnt = self.count(axis=axis)
1637
def anom(self, axis=None, dtype=None):
1638
"""a.anom(axis=None, dtype=None)
1639
Returns the anomalies, or deviation from the average.
1641
m = self.mean(axis, dtype)
1645
return (self - expand_dims(m,axis))
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.
1651
The variance is the average of the squared deviations from the mean,
1652
i.e. var = mean((x - x.mean())**2).
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)
1658
cnt = self.count(axis=axis)
1659
danom = self.anom(axis=axis, dtype=dtype)
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))
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.
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)).
1673
dvar = self.var(axis,dtype)
1674
if axis is not None or dvar is not masked:
1677
#............................................
1678
def argsort(self, axis=None, fill_value=None, kind='quicksort',
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.
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'
1692
Returns: array of indices that sort 'a' along the specified axis.
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.
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:
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
|------------------------------------------------------|
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.
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
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.
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.
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
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.
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.
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)
1756
def sort(self, axis=-1, kind='quicksort', order=None,
1757
endwith=True, fill_value=None):
1759
Sort a along the given axis.
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
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).
1776
This method sorts 'a' in place along the given axis using the algorithm
1777
specified by the kind keyword.
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:
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
|------------------------------------------------------|
1794
if self._mask is nomask:
1795
ndarray.sort(self,axis=axis, kind=kind, order=order)
1797
if fill_value is None:
1799
filler = minimum_fill_value(self)
1801
filler = maximum_fill_value(self)
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
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."""
1819
# Check all/nothing case ......
1821
return super(MaskedArray, self).min(axis=axis)
1822
elif (not mask.ndim) and mask:
1824
# Get the mask ................
1826
mask = umath.logical_and.reduce(mask.flat)
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))
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."""
1844
# Check all/nothing case ......
1846
return super(MaskedArray, self).max(axis=axis)
1847
elif (not mask.ndim) and mask:
1849
# Check the mask ..............
1851
mask = umath.logical_and.reduce(mask.flat)
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))
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)
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
1888
Masked values are converted to `fill_value`. If `fill_value` is None, the
1889
corresponding entries in the output list will be None.
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:
1898
elif self.ndim == 1:
1899
maskedidx = self._mask.nonzero()[0].tolist()
1900
[operator.setitem(result,i,None) for i in maskedidx]
1902
for idx in zip(*[i.tolist() for i in self._mask.nonzero()]):
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.
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
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
1923
"C" -- C order (row major)
1924
"Fortran" -- Fortran order (column major)
1925
"Any" -- Current order of array.
1926
None -- Same as "Any"
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...
1935
"""Returns the `_data` part of the MaskedArray."""
1938
"""Returns the `_data` part of the MaskedArray.
1939
You should really use `data` instead..."""
1941
#--------------------------------------------
1943
def __getstate__(self):
1944
"Returns the internal state of the masked array, for pickling purposes."
1949
self._data.tostring(),
1950
getmaskarray(self).tostring(),
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:
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.
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
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__())
1977
def _mareconstruct(subtype, baseclass, baseshape, basetype,):
1978
"""Internal function that builds a new MaskedArray from the information stored
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
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
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...
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)
2014
"""Returns whether x has some masked values."""
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."
2031
return self.reduce(a)
2032
return where(self.compare(a, b), a, b)
2034
def reduce(self, target, axis=None):
2035
"""Reduces target along the given axis."""
2037
if axis is not None:
2038
kargs = { 'axis' : axis }
2041
target = target.ravel()
2042
if not (m is nomask):
2045
t = self.ufunc.reduce(target, **kargs)
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'):
2056
def outer (self, a, b):
2057
"Returns the function applied to the outer product of a and b."
2060
if ma is nomask and mb is nomask:
2063
ma = getmaskarray(a)
2064
mb = getmaskarray(b)
2065
m = logical_or.outer(ma, mb)
2066
result = self.ufunc.outer(filled(a), filled(b))
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.
2076
self.ufunc = umath.minimum
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.
2087
self.ufunc = umath.maximum
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."""
2096
raise TypeError("Output arrays Unsupported for masked arrays")
2098
return minimum(array)
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."""
2106
raise TypeError("Output arrays Unsupported for masked arrays")
2110
return maximum.reduce(obj, axis)
2111
#.............................
2112
def ptp(obj, axis=None):
2113
"""a.ptp(axis=None) = a.max(axis)-a.min(axis)"""
2115
return obj.max(axis)-obj.min(axis)
2116
except AttributeError:
2117
return max(obj, axis=axis) - min(obj, axis=axis)
2120
#####---------------------------------------------------------------------------
2121
#---- --- Definition of functions from the corresponding methods ---
2122
#####---------------------------------------------------------------------------
2124
"""Defines functions from existing MaskedArray methods.
2125
:ivar _methodname (String): Name of the method to transform.
2127
def __init__(self, methodname):
2128
self._methodname = methodname
2129
self.__doc__ = self.getdoc()
2131
"Returns the doc of the function (from the doc of the method)."
2133
return getattr(MaskedArray, self._methodname).__doc__
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)
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)
2147
return method(*args, **params)
2149
return getattr(numpy,self._methodname).__call__(a, *args, **params)
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')
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."
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)
2187
return masked_array(umath.power(fa, fb))
2190
return masked_array(umath.power(fa, fb), m)
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.
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'
2206
Returns: array of indices that sort 'a' along the specified axis.
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.
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:
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
|------------------------------------------------------|
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.
2229
if fill_value is None:
2230
fill_value = default_fill_value(a)
2231
d = filled(a, fill_value)
2233
return d.argsort(kind=kind, order=order)
2234
return d.argsort(axis, kind=kind, order=order)
2236
def argmin(a, axis=None, fill_value=None):
2237
"""Returns the array of indices for the minimum values of `a` along the
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.
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.
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)
2254
def argmax(a, axis=None, fill_value=None):
2255
"""Returns the array of indices for the maximum values of `a` along the
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.
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.
2267
if fill_value is None:
2268
fill_value = default_fill_value(a)
2270
fill_value = - fill_value
2273
d = filled(a, fill_value)
2274
return d.argmax(axis=axis)
2276
def sort(a, axis=-1, kind='quicksort', order=None, endwith=True, fill_value=None):
2278
Sort a along the given axis.
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
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).
2295
This method sorts 'a' in place along the given axis using the algorithm
2296
specified by the kind keyword.
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:
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
|------------------------------------------------------|
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.
2317
a = numeric.asanyarray(a)
2318
if fill_value is None:
2320
filler = minimum_fill_value(a)
2322
filler = maximum_fill_value(a)
2326
indx = numpy.indices(a.shape).tolist()
2327
indx[axis] = filled(a,filler).argsort(axis=axis,kind=kind,order=order)
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:
2336
return x.compressed()
2338
def count(a, axis = None):
2339
"Count of the non-masked elements in a, or along a certain axis."
2341
return a.count(axis)
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)
2349
if getmask(x) is not nomask:
2353
dm = numeric.concatenate([getmaskarray(a) for a in arrays], axis)
2354
dm = make_mask(dm, copy=False, small_mask=True)
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
2364
result.shape = new_shape
2365
if result._mask is not nomask:
2366
result._mask.shape = new_shape
2369
#......................................
2370
def left_shift (a, n):
2374
d = umath.left_shift(filled(a), n)
2375
return masked_array(d)
2377
d = umath.left_shift(filled(a, 0), n)
2378
return masked_array(d, mask=m)
2380
def right_shift (a, n):
2381
"Right shift n bits"
2384
d = umath.right_shift(filled(a), n)
2385
return masked_array(d)
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
2395
return a.put(indices, values, mode=mode)
2396
except AttributeError:
2397
return fromnumeric.asarray(a).put(indices, values, mode=mode)
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
2405
return a.putmask(values, mask)
2406
except AttributeError:
2407
return fromnumeric.asarray(a).putmask(values, mask)
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.
2413
#We can't use 'frommethod', as 'transpose' doesn't take keywords
2415
return a.transpose(axes)
2416
except AttributeError:
2417
return fromnumeric.asarray(a).transpose(axes)
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.
2423
return a.reshape(new_shape)
2424
except AttributeError:
2425
return fromnumeric.asarray(a).reshape(new_shape)
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.
2433
# We can't use _frommethods here, as N.resize is notoriously whiny.
2436
m = fromnumeric.resize(m, new_shape)
2437
result = fromnumeric.resize(x, new_shape).view(get_masked_subclass(x))
2443
#................................................
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))
2450
"""Returns the shape of `a` (as a function call which also works on nested sequences).
2452
return fromnumeric.shape(filled(obj))
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.
2458
return fromnumeric.size(filled(obj), axis)
2459
#................................................
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
2471
fc = filled(not_equal(condition, 0), 0)
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)
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.
2486
"Returns the filled array, or True if ``masked``."
2491
"Returns the mask, True if ``masked``, False if ``nomask``."
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)
2506
def round_(a, decimals=0, out=None):
2507
"""Returns reference to result. Copies a and rounds to 'decimals' places.
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).
2514
Reference to out, where None specifies a copy of the original array a.
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
2526
result = result.view(MaskedArray)
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.
2533
return array(numeric.arange(start, stop, step, dtype),mask=nomask)
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.
2543
if len(fa.shape) == 0:
2545
if len(fb.shape) == 0:
2547
return masked_array(numeric.inner(fa, fb))
2548
innerproduct = inner
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)
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
2565
def allequal (a, b, fill_value=True):
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.
2570
m = mask_or(getmask(a), getmask(b))
2574
d = umath.equal(x, y)
2579
d = umath.equal(x, y)
2580
dm = array(d, mask=m, copy=False)
2581
return dm.filled(True).all(None)
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.
2593
m = mask_or(getmask(a), getmask(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))
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.
2608
return masked_array(a, dtype=dtype, copy=False, keep_mask=True)
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)
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)
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)
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)
2633
#####--------------------------------------------------------------------------
2634
#---- --- Pickling ---
2635
#####--------------------------------------------------------------------------
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.
2640
if not hasattr(F,'readline'):
2642
return cPickle.dump(a,F)
2645
"""Returns a string corresponding to the pickling of the MaskedArray."""
2646
return cPickle.dumps(a)
2649
"""Wrapper around ``cPickle.load`` which accepts either a file-like object or
2651
if not hasattr(F, 'readline'):
2653
return cPickle.load(F)
2656
"Loads a pickle from the current string."""
2657
return cPickle.loads(strg)
2660
################################################################################
2662
if __name__ == '__main__':
2663
from testutils import assert_equal, assert_almost_equal
2666
assert(x.ctypes.data == x.filled().ctypes.data)
2668
a = array([1,2,3,4],mask=[0,0,0,0],small_mask=True)
2671
assert(a.ravel()._mask, [0,0,0,0])
2672
assert(a.compressed(), a)
2674
assert(a.compressed()._mask, [0,0,0])
2676
x = array(0, mask=0)
2678
J = x.filled().ctypes.data
2680
x = array([0,0], mask=0)
2681
(I,J) = (x.ctypes.data, x.filled().ctypes.data)
2684
x = array(numpy.arange(12))
2687
assert(xlist[1] is None)
2688
assert(xlist[-2] is None)
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])
b'\\ No newline at end of file'