codekingpro/portable-devtools
115k
1"""
2numpy.ma : a package to handle missing or invalid values.
3
4This package was initially written for numarray by Paul F. Dubois
5at Lawrence Livermore National Laboratory.
6In 2006, the package was completely rewritten by Pierre Gerard-Marchant
7(University of Georgia) to make the MaskedArray class a subclass of ndarray,
8and to improve support of structured arrays.
9
10
11Copyright 1999, 2000, 2001 Regents of the University of California.
12Released for unlimited redistribution.
13
14* Adapted for numpy_core 2005 by Travis Oliphant and (mainly) Paul Dubois.
15* Subclassing of the base `ndarray` 2006 by Pierre Gerard-Marchant
16 (pgmdevlist_AT_gmail_DOT_com)
17* Improvements suggested by Reggie Dugard (reggie_AT_merfinllc_DOT_com)
18
19.. moduleauthor:: Pierre Gerard-Marchant
20
21"""
22import builtins
23import functools
24import inspect
25import operator
26import re
27import textwrap
28import warnings
29
30import numpy as np
31import numpy._core.numerictypes as ntypes
32import numpy._core.umath as umath
33from numpy import (
34 _NoValue,
35 amax,
36 amin,
37 angle,
38 array as narray, # noqa: F401
39 bool_,
40 expand_dims,
41 finfo, # noqa: F401
42 iinfo, # noqa: F401
43 iscomplexobj,
44 ndarray,
45)
46from numpy._core import multiarray as mu
47from numpy._core.numeric import normalize_axis_tuple
48from numpy._utils import set_module
49
50__all__ = [
51 'MAError', 'MaskError', 'MaskType', 'MaskedArray', 'abs', 'absolute',
52 'add', 'all', 'allclose', 'allequal', 'alltrue', 'amax', 'amin',
53 'angle', 'anom', 'anomalies', 'any', 'append', 'arange', 'arccos',
54 'arccosh', 'arcsin', 'arcsinh', 'arctan', 'arctan2', 'arctanh',
55 'argmax', 'argmin', 'argsort', 'around', 'array', 'asanyarray',
56 'asarray', 'bitwise_and', 'bitwise_or', 'bitwise_xor', 'bool_', 'ceil',
57 'choose', 'clip', 'common_fill_value', 'compress', 'compressed',
58 'concatenate', 'conjugate', 'convolve', 'copy', 'correlate', 'cos', 'cosh',
59 'count', 'cumprod', 'cumsum', 'default_fill_value', 'diag', 'diagonal',
60 'diff', 'divide', 'empty', 'empty_like', 'equal', 'exp',
61 'expand_dims', 'fabs', 'filled', 'fix_invalid', 'flatten_mask',
62 'flatten_structured_array', 'floor', 'floor_divide', 'fmod',
63 'frombuffer', 'fromflex', 'fromfunction', 'getdata', 'getmask',
64 'getmaskarray', 'greater', 'greater_equal', 'harden_mask', 'hypot',
65 'identity', 'ids', 'indices', 'inner', 'innerproduct', 'isMA',
66 'isMaskedArray', 'is_mask', 'is_masked', 'isarray', 'left_shift',
67 'less', 'less_equal', 'log', 'log10', 'log2',
68 'logical_and', 'logical_not', 'logical_or', 'logical_xor', 'make_mask',
69 'make_mask_descr', 'make_mask_none', 'mask_or', 'masked',
70 'masked_array', 'masked_equal', 'masked_greater',
71 'masked_greater_equal', 'masked_inside', 'masked_invalid',
72 'masked_less', 'masked_less_equal', 'masked_not_equal',
73 'masked_object', 'masked_outside', 'masked_print_option',
74 'masked_singleton', 'masked_values', 'masked_where', 'max', 'maximum',
75 'maximum_fill_value', 'mean', 'min', 'minimum', 'minimum_fill_value',
76 'mod', 'multiply', 'mvoid', 'ndim', 'negative', 'nomask', 'nonzero',
77 'not_equal', 'ones', 'ones_like', 'outer', 'outerproduct', 'power', 'prod',
78 'product', 'ptp', 'put', 'putmask', 'ravel', 'remainder',
79 'repeat', 'reshape', 'resize', 'right_shift', 'round', 'round_',
80 'set_fill_value', 'shape', 'sin', 'sinh', 'size', 'soften_mask',
81 'sometrue', 'sort', 'sqrt', 'squeeze', 'std', 'subtract', 'sum',
82 'swapaxes', 'take', 'tan', 'tanh', 'trace', 'transpose', 'true_divide',
83 'var', 'where', 'zeros', 'zeros_like',
84 ]
85
86MaskType = np.bool
87nomask = MaskType(0)
88
89class MaskedArrayFutureWarning(FutureWarning):
90 pass
91
92def _deprecate_argsort_axis(arr):
93 """
94 Adjust the axis passed to argsort, warning if necessary
95
96 Parameters
97 ----------
98 arr
99 The array which argsort was called on
100
101 np.ma.argsort has a long-term bug where the default of the axis argument
102 is wrong (gh-8701), which now must be kept for backwards compatibility.
103 Thankfully, this only makes a difference when arrays are 2- or more-
104 dimensional, so we only need a warning then.
105 """
106 if arr.ndim <= 1:
107 # no warning needed - but switch to -1 anyway, to avoid surprising
108 # subclasses, which are more likely to implement scalar axes.
109 return -1
110 else:
111 # 2017-04-11, Numpy 1.13.0, gh-8701: warn on axis default
112 warnings.warn(
113 "In the future the default for argsort will be axis=-1, not the "
114 "current None, to match its documentation and np.argsort. "
115 "Explicitly pass -1 or None to silence this warning.",
116 MaskedArrayFutureWarning, stacklevel=3)
117 return None
118
119
120def doc_note(initialdoc, note):
121 """
122 Adds a Notes section to an existing docstring.
123
124 """
125 if initialdoc is None:
126 return
127 if note is None:
128 return initialdoc
129
130 notesplit = re.split(r'\n\s*?Notes\n\s*?-----', inspect.cleandoc(initialdoc))
131 notedoc = f"\n\nNotes\n-----\n{inspect.cleandoc(note)}\n"
132
133 return ''.join(notesplit[:1] + [notedoc] + notesplit[1:])
134
135
136###############################################################################
137# Exceptions #
138###############################################################################
139
140
141class MAError(Exception):
142 """
143 Class for masked array related errors.
144
145 """
146 pass
147
148
149class MaskError(MAError):
150 """
151 Class for mask related errors.
152
153 """
154 pass
155
156
157###############################################################################
158# Filling options #
159###############################################################################
160
161
162# b: boolean - c: complex - f: floats - i: integer - O: object - S: string
163default_filler = {'b': True,
164 'c': 1.e20 + 0.0j,
165 'f': 1.e20,
166 'i': 999999,
167 'O': '?',
168 'S': b'N/A',
169 'u': 999999,
170 'V': b'???',
171 'U': 'N/A',
172 'T': 'N/A'
173 }
174
175# Add datetime64 and timedelta64 types
176for v in ["Y", "M", "W", "D", "h", "m", "s", "ms", "us", "ns", "ps",
177 "fs", "as"]:
178 default_filler["M8[" + v + "]"] = np.datetime64("NaT", v)
179 default_filler["m8[" + v + "]"] = np.timedelta64("NaT", v)
180
181float_types_list = [np.half, np.single, np.double, np.longdouble,
182 np.csingle, np.cdouble, np.clongdouble]
183
184_minvals: dict[type, int] = {}
185_maxvals: dict[type, int] = {}
186
187for sctype in ntypes.sctypeDict.values():
188 scalar_dtype = np.dtype(sctype)
189
190 if scalar_dtype.kind in "Mm":
191 info = np.iinfo(np.int64)
192 min_val, max_val = info.min + 1, info.max
193 elif np.issubdtype(scalar_dtype, np.integer):
194 info = np.iinfo(sctype)
195 min_val, max_val = info.min, info.max
196 elif np.issubdtype(scalar_dtype, np.floating):
197 info = np.finfo(sctype)
198 min_val, max_val = info.min, info.max
199 elif scalar_dtype.kind == "b":
200 min_val, max_val = 0, 1
201 else:
202 min_val, max_val = None, None
203
204 _minvals[sctype] = min_val
205 _maxvals[sctype] = max_val
206
207max_filler = _minvals
208max_filler.update([(k, -np.inf) for k in float_types_list[:4]])
209max_filler.update([(k, complex(-np.inf, -np.inf)) for k in float_types_list[-3:]])
210
211min_filler = _maxvals
212min_filler.update([(k, +np.inf) for k in float_types_list[:4]])
213min_filler.update([(k, complex(+np.inf, +np.inf)) for k in float_types_list[-3:]])
214
215del float_types_list
216
217def _recursive_fill_value(dtype, f):
218 """
219 Recursively produce a fill value for `dtype`, calling f on scalar dtypes
220 """
221 if dtype.names is not None:
222 # We wrap into `array` here, which ensures we use NumPy cast rules
223 # for integer casts, this allows the use of 99999 as a fill value
224 # for int8.
225 # TODO: This is probably a mess, but should best preserve behavior?
226 vals = tuple(
227 np.array(_recursive_fill_value(dtype[name], f))
228 for name in dtype.names)
229 return np.array(vals, dtype=dtype)[()] # decay to void scalar from 0d
230 elif dtype.subdtype:
231 subtype, shape = dtype.subdtype
232 subval = _recursive_fill_value(subtype, f)
233 return np.full(shape, subval)
234 else:
235 return f(dtype)
236
237
238def _get_dtype_of(obj):
239 """ Convert the argument for *_fill_value into a dtype """
240 if isinstance(obj, np.dtype):
241 return obj
242 elif hasattr(obj, 'dtype'):
243 return obj.dtype
244 else:
245 return np.asanyarray(obj).dtype
246
247
248def default_fill_value(obj):
249 """
250 Return the default fill value for the argument object.
251
252 The default filling value depends on the datatype of the input
253 array or the type of the input scalar:
254
255 =========== ========
256 datatype default
257 =========== ========
258 bool True
259 int 999999
260 float 1.e20
261 complex 1.e20+0j
262 object '?'
263 string 'N/A'
264 StringDType 'N/A'
265 =========== ========
266
267 For structured types, a structured scalar is returned, with each field the
268 default fill value for its type.
269
270 For subarray types, the fill value is an array of the same size containing
271 the default scalar fill value.
272
273 Parameters
274 ----------
275 obj : ndarray, dtype or scalar
276 The array data-type or scalar for which the default fill value
277 is returned.
278
279 Returns
280 -------
281 fill_value : scalar
282 The default fill value.
283
284 Examples
285 --------
286 >>> import numpy as np
287 >>> np.ma.default_fill_value(1)
288 999999
289 >>> np.ma.default_fill_value(np.array([1.1, 2., np.pi]))
290 1e+20
291 >>> np.ma.default_fill_value(np.dtype(complex))
292 (1e+20+0j)
293
294 """
295 def _scalar_fill_value(dtype):
296 if dtype.kind in 'Mm':
297 return default_filler.get(dtype.str[1:], '?')
298 else:
299 return default_filler.get(dtype.kind, '?')
300
301 dtype = _get_dtype_of(obj)
302 return _recursive_fill_value(dtype, _scalar_fill_value)
303
304
305def _extremum_fill_value(obj, extremum, extremum_name):
306
307 def _scalar_fill_value(dtype):
308 try:
309 return extremum[dtype.type]
310 except KeyError as e:
311 raise TypeError(
312 f"Unsuitable type {dtype} for calculating {extremum_name}."
313 ) from None
314
315 dtype = _get_dtype_of(obj)
316 return _recursive_fill_value(dtype, _scalar_fill_value)
317
318
319def minimum_fill_value(obj):
320 """
321 Return the maximum value that can be represented by the dtype of an object.
322
323 This function is useful for calculating a fill value suitable for
324 taking the minimum of an array with a given dtype.
325
326 Parameters
327 ----------
328 obj : ndarray, dtype or scalar
329 An object that can be queried for it's numeric type.
330
331 Returns
332 -------
333 val : scalar
334 The maximum representable value.
335
336 Raises
337 ------
338 TypeError
339 If `obj` isn't a suitable numeric type.
340
341 See Also
342 --------
343 maximum_fill_value : The inverse function.
344 set_fill_value : Set the filling value of a masked array.
345 MaskedArray.fill_value : Return current fill value.
346
347 Examples
348 --------
349 >>> import numpy as np
350 >>> import numpy.ma as ma
351 >>> a = np.int8()
352 >>> ma.minimum_fill_value(a)
353 127
354 >>> a = np.int32()
355 >>> ma.minimum_fill_value(a)
356 2147483647
357
358 An array of numeric data can also be passed.
359
360 >>> a = np.array([1, 2, 3], dtype=np.int8)
361 >>> ma.minimum_fill_value(a)
362 127
363 >>> a = np.array([1, 2, 3], dtype=np.float32)
364 >>> ma.minimum_fill_value(a)
365 inf
366
367 """
368 return _extremum_fill_value(obj, min_filler, "minimum")
369
370
371def maximum_fill_value(obj):
372 """
373 Return the minimum value that can be represented by the dtype of an object.
374
375 This function is useful for calculating a fill value suitable for
376 taking the maximum of an array with a given dtype.
377
378 Parameters
379 ----------
380 obj : ndarray, dtype or scalar
381 An object that can be queried for it's numeric type.
382
383 Returns
384 -------
385 val : scalar
386 The minimum representable value.
387
388 Raises
389 ------
390 TypeError
391 If `obj` isn't a suitable numeric type.
392
393 See Also
394 --------
395 minimum_fill_value : The inverse function.
396 set_fill_value : Set the filling value of a masked array.
397 MaskedArray.fill_value : Return current fill value.
398
399 Examples
400 --------
401 >>> import numpy as np
402 >>> import numpy.ma as ma
403 >>> a = np.int8()
404 >>> ma.maximum_fill_value(a)
405 -128
406 >>> a = np.int32()
407 >>> ma.maximum_fill_value(a)
408 -2147483648
409
410 An array of numeric data can also be passed.
411
412 >>> a = np.array([1, 2, 3], dtype=np.int8)
413 >>> ma.maximum_fill_value(a)
414 -128
415 >>> a = np.array([1, 2, 3], dtype=np.float32)
416 >>> ma.maximum_fill_value(a)
417 -inf
418
419 """
420 return _extremum_fill_value(obj, max_filler, "maximum")
421
422
423def _recursive_set_fill_value(fillvalue, dt):
424 """
425 Create a fill value for a structured dtype.
426
427 Parameters
428 ----------
429 fillvalue : scalar or array_like
430 Scalar or array representing the fill value. If it is of shorter
431 length than the number of fields in dt, it will be resized.
432 dt : dtype
433 The structured dtype for which to create the fill value.
434
435 Returns
436 -------
437 val : tuple
438 A tuple of values corresponding to the structured fill value.
439
440 """
441 fillvalue = np.resize(fillvalue, len(dt.names))
442 output_value = []
443 for (fval, name) in zip(fillvalue, dt.names):
444 cdtype = dt[name]
445 if cdtype.subdtype:
446 cdtype = cdtype.subdtype[0]
447
448 if cdtype.names is not None:
449 output_value.append(tuple(_recursive_set_fill_value(fval, cdtype)))
450 else:
451 output_value.append(np.array(fval, dtype=cdtype).item())
452 return tuple(output_value)
453
454
455def _check_fill_value(fill_value, ndtype):
456 """
457 Private function validating the given `fill_value` for the given dtype.
458
459 If fill_value is None, it is set to the default corresponding to the dtype.
460
461 If fill_value is not None, its value is forced to the given dtype.
462
463 The result is always a 0d array.
464
465 """
466 ndtype = np.dtype(ndtype)
467 if fill_value is None:
468 fill_value = default_fill_value(ndtype)
469 # TODO: It seems better to always store a valid fill_value, the oddity
470 # about is that `_fill_value = None` would behave even more
471 # different then.
472 # (e.g. this allows arr_uint8.astype(int64) to have the default
473 # fill value again...)
474 # The one thing that changed in 2.0/2.1 around cast safety is that the
475 # default `int(99...)` is not a same-kind cast anymore, so if we
476 # have a uint, use the default uint.
477 if ndtype.kind == "u":
478 fill_value = np.uint(fill_value)
479 elif ndtype.names is not None:
480 if isinstance(fill_value, (ndarray, np.void)):
481 try:
482 fill_value = np.asarray(fill_value, dtype=ndtype)
483 except ValueError as e:
484 err_msg = "Unable to transform %s to dtype %s"
485 raise ValueError(err_msg % (fill_value, ndtype)) from e
486 else:
487 fill_value = np.asarray(fill_value, dtype=object)
488 fill_value = np.array(_recursive_set_fill_value(fill_value, ndtype),
489 dtype=ndtype)
490 elif isinstance(fill_value, str) and (ndtype.char not in 'OSTVU'):
491 # Note this check doesn't work if fill_value is not a scalar
492 err_msg = "Cannot set fill value of string with array of dtype %s"
493 raise TypeError(err_msg % ndtype)
494 else:
495 # In case we want to convert 1e20 to int.
496 # Also in case of converting string arrays.
497 try:
498 fill_value = np.asarray(fill_value, dtype=ndtype)
499 except (OverflowError, ValueError) as e:
500 # Raise TypeError instead of OverflowError or ValueError.
501 # OverflowError is seldom used, and the real problem here is
502 # that the passed fill_value is not compatible with the ndtype.
503 err_msg = "Cannot convert fill_value %s to dtype %s"
504 raise TypeError(err_msg % (fill_value, ndtype)) from e
505 return np.array(fill_value)
506
507
508def set_fill_value(a, fill_value):
509 """
510 Set the filling value of a, if a is a masked array.
511
512 This function changes the fill value of the masked array `a` in place.
513 If `a` is not a masked array, the function returns silently, without
514 doing anything.
515
516 Parameters
517 ----------
518 a : array_like
519 Input array.
520 fill_value : dtype
521 Filling value. A consistency test is performed to make sure
522 the value is compatible with the dtype of `a`.
523
524 Returns
525 -------
526 None
527 Nothing returned by this function.
528
529 See Also
530 --------
531 maximum_fill_value : Return the default fill value for a dtype.
532 MaskedArray.fill_value : Return current fill value.
533 MaskedArray.set_fill_value : Equivalent method.
534
535 Examples
536 --------
537 >>> import numpy as np
538 >>> import numpy.ma as ma
539 >>> a = np.arange(5)
540 >>> a
541 array([0, 1, 2, 3, 4])
542 >>> a = ma.masked_where(a < 3, a)
543 >>> a
544 masked_array(data=[--, --, --, 3, 4],
545 mask=[ True, True, True, False, False],
546 fill_value=999999)
547 >>> ma.set_fill_value(a, -999)
548 >>> a
549 masked_array(data=[--, --, --, 3, 4],
550 mask=[ True, True, True, False, False],
551 fill_value=-999)
552
553 Nothing happens if `a` is not a masked array.
554
555 >>> a = list(range(5))
556 >>> a
557 [0, 1, 2, 3, 4]
558 >>> ma.set_fill_value(a, 100)
559 >>> a
560 [0, 1, 2, 3, 4]
561 >>> a = np.arange(5)
562 >>> a
563 array([0, 1, 2, 3, 4])
564 >>> ma.set_fill_value(a, 100)
565 >>> a
566 array([0, 1, 2, 3, 4])
567
568 """
569 if isinstance(a, MaskedArray):
570 a.set_fill_value(fill_value)
571
572
573def get_fill_value(a):
574 """
575 Return the filling value of a, if any. Otherwise, returns the
576 default filling value for that type.
577
578 """
579 if isinstance(a, MaskedArray):
580 result = a.fill_value
581 else:
582 result = default_fill_value(a)
583 return result
584
585
586def common_fill_value(a, b):
587 """
588 Return the common filling value of two masked arrays, if any.
589
590 If ``a.fill_value == b.fill_value``, return the fill value,
591 otherwise return None.
592
593 Parameters
594 ----------
595 a, b : MaskedArray
596 The masked arrays for which to compare fill values.
597
598 Returns
599 -------
600 fill_value : scalar or None
601 The common fill value, or None.
602
603 Examples
604 --------
605 >>> import numpy as np
606 >>> x = np.ma.array([0, 1.], fill_value=3)
607 >>> y = np.ma.array([0, 1.], fill_value=3)
608 >>> np.ma.common_fill_value(x, y)
609 3.0
610
611 """
612 t1 = get_fill_value(a)
613 t2 = get_fill_value(b)
614 if t1 == t2:
615 return t1
616 return None
617
618
619def filled(a, fill_value=None):
620 """
621 Return input as an `~numpy.ndarray`, with masked values replaced by
622 `fill_value`.
623
624 If `a` is not a `MaskedArray`, `a` itself is returned.
625 If `a` is a `MaskedArray` with no masked values, then ``a.data`` is
626 returned.
627 If `a` is a `MaskedArray` and `fill_value` is None, `fill_value` is set to
628 ``a.fill_value``.
629
630 Parameters
631 ----------
632 a : MaskedArray or array_like
633 An input object.
634 fill_value : array_like, optional.
635 Can be scalar or non-scalar. If non-scalar, the
636 resulting filled array should be broadcastable
637 over input array. Default is None.
638
639 Returns
640 -------
641 a : ndarray
642 The filled array.
643
644 See Also
645 --------
646 compressed
647
648 Examples
649 --------
650 >>> import numpy as np
651 >>> import numpy.ma as ma
652 >>> x = ma.array(np.arange(9).reshape(3, 3), mask=[[1, 0, 0],
653 ... [1, 0, 0],
654 ... [0, 0, 0]])
655 >>> x.filled()
656 array([[999999, 1, 2],
657 [999999, 4, 5],
658 [ 6, 7, 8]])
659 >>> x.filled(fill_value=333)
660 array([[333, 1, 2],
661 [333, 4, 5],
662 [ 6, 7, 8]])
663 >>> x.filled(fill_value=np.arange(3))
664 array([[0, 1, 2],
665 [0, 4, 5],
666 [6, 7, 8]])
667
668 """
669 if hasattr(a, 'filled'):
670 return a.filled(fill_value)
671
672 elif isinstance(a, ndarray):
673 # Should we check for contiguity ? and a.flags['CONTIGUOUS']:
674 return a
675 elif isinstance(a, dict):
676 return np.array(a, 'O')
677 else:
678 return np.array(a)
679
680
681def get_masked_subclass(*arrays):
682 """
683 Return the youngest subclass of MaskedArray from a list of (masked) arrays.
684
685 In case of siblings, the first listed takes over.
686
687 """
688 if len(arrays) == 1:
689 arr = arrays[0]
690 if isinstance(arr, MaskedArray):
691 rcls = type(arr)
692 else:
693 rcls = MaskedArray
694 else:
695 arrcls = [type(a) for a in arrays]
696 rcls = arrcls[0]
697 if not issubclass(rcls, MaskedArray):
698 rcls = MaskedArray
699 for cls in arrcls[1:]:
700 if issubclass(cls, rcls):
701 rcls = cls
702 # Don't return MaskedConstant as result: revert to MaskedArray
703 if rcls.__name__ == 'MaskedConstant':
704 return MaskedArray
705 return rcls
706
707
708def getdata(a, subok=True):
709 """
710 Return the data of a masked array as an ndarray.
711
712 Return the data of `a` (if any) as an ndarray if `a` is a ``MaskedArray``,
713 else return `a` as a ndarray or subclass (depending on `subok`) if not.
714
715 Parameters
716 ----------
717 a : array_like
718 Input ``MaskedArray``, alternatively a ndarray or a subclass thereof.
719 subok : bool
720 Whether to force the output to be a `pure` ndarray (False) or to
721 return a subclass of ndarray if appropriate (True, default).
722
723 See Also
724 --------
725 getmask : Return the mask of a masked array, or nomask.
726 getmaskarray : Return the mask of a masked array, or full array of False.
727
728 Examples
729 --------
730 >>> import numpy as np
731 >>> import numpy.ma as ma
732 >>> a = ma.masked_equal([[1,2],[3,4]], 2)
733 >>> a
734 masked_array(
735 data=[[1, --],
736 [3, 4]],
737 mask=[[False, True],
738 [False, False]],
739 fill_value=2)
740 >>> ma.getdata(a)
741 array([[1, 2],
742 [3, 4]])
743
744 Equivalently use the ``MaskedArray`` `data` attribute.
745
746 >>> a.data
747 array([[1, 2],
748 [3, 4]])
749
750 """
751 try:
752 data = a._data
753 except AttributeError:
754 data = np.array(a, copy=None, subok=subok)
755 if not subok:
756 return data.view(ndarray)
757 return data
758
759
760get_data = getdata
761
762
763def fix_invalid(a, mask=nomask, copy=True, fill_value=None):
764 """
765 Return input with invalid data masked and replaced by a fill value.
766
767 Invalid data means values of `nan`, `inf`, etc.
768
769 Parameters
770 ----------
771 a : array_like
772 Input array, a (subclass of) ndarray.
773 mask : sequence, optional
774 Mask. Must be convertible to an array of booleans with the same
775 shape as `data`. True indicates a masked (i.e. invalid) data.
776 copy : bool, optional
777 Whether to use a copy of `a` (True) or to fix `a` in place (False).
778 Default is True.
779 fill_value : scalar, optional
780 Value used for fixing invalid data. Default is None, in which case
781 the ``a.fill_value`` is used.
782
783 Returns
784 -------
785 b : MaskedArray
786 The input array with invalid entries fixed.
787
788 Notes
789 -----
790 A copy is performed by default.
791
792 Examples
793 --------
794 >>> import numpy as np
795 >>> x = np.ma.array([1., -1, np.nan, np.inf], mask=[1] + [0]*3)
796 >>> x
797 masked_array(data=[--, -1.0, nan, inf],
798 mask=[ True, False, False, False],
799 fill_value=1e+20)
800 >>> np.ma.fix_invalid(x)
801 masked_array(data=[--, -1.0, --, --],
802 mask=[ True, False, True, True],
803 fill_value=1e+20)
804
805 >>> fixed = np.ma.fix_invalid(x)
806 >>> fixed.data
807 array([ 1.e+00, -1.e+00, 1.e+20, 1.e+20])
808 >>> x.data
809 array([ 1., -1., nan, inf])
810
811 """
812 a = masked_array(a, copy=copy, mask=mask, subok=True)
813 invalid = np.logical_not(np.isfinite(a._data))
814 if not invalid.any():
815 return a
816 a._mask |= invalid
817 if fill_value is None:
818 fill_value = a.fill_value
819 a._data[invalid] = fill_value
820 return a
821
822def is_string_or_list_of_strings(val):
823 return (isinstance(val, str) or
824 (isinstance(val, list) and val and
825 builtins.all(isinstance(s, str) for s in val)))
826
827###############################################################################
828# Ufuncs #
829###############################################################################
830
831
832ufunc_domain = {}
833ufunc_fills = {}
834
835
836class _DomainCheckInterval:
837 """
838 Define a valid interval, so that :
839
840 ``domain_check_interval(a,b)(x) == True`` where
841 ``x < a`` or ``x > b``.
842
843 """
844
845 def __init__(self, a, b):
846 "domain_check_interval(a,b)(x) = true where x < a or y > b"
847 if a > b:
848 (a, b) = (b, a)
849 self.a = a
850 self.b = b
851
852 def __call__(self, x):
853 "Execute the call behavior."
854 # nans at masked positions cause RuntimeWarnings, even though
855 # they are masked. To avoid this we suppress warnings.
856 with np.errstate(invalid='ignore'):
857 return umath.logical_or(umath.greater(x, self.b),
858 umath.less(x, self.a))
859
860
861class _DomainTan:
862 """
863 Define a valid interval for the `tan` function, so that:
864
865 ``domain_tan(eps) = True`` where ``abs(cos(x)) < eps``
866
867 """
868
869 def __init__(self, eps):
870 "domain_tan(eps) = true where abs(cos(x)) < eps)"
871 self.eps = eps
872
873 def __call__(self, x):
874 "Executes the call behavior."
875 with np.errstate(invalid='ignore'):
876 return umath.less(umath.absolute(umath.cos(x)), self.eps)
877
878
879class _DomainSafeDivide:
880 """
881 Define a domain for safe division.
882
883 """
884
885 def __init__(self, tolerance=None):
886 self.tolerance = tolerance
887
888 def __call__(self, a, b):
889 # Delay the selection of the tolerance to here in order to reduce numpy
890 # import times. The calculation of these parameters is a substantial
891 # component of numpy's import time.
892 if self.tolerance is None:
893 self.tolerance = np.finfo(float).tiny
894 # don't call ma ufuncs from __array_wrap__ which would fail for scalars
895 a, b = np.asarray(a), np.asarray(b)
896 with np.errstate(all='ignore'):
897 return umath.absolute(a) * self.tolerance >= umath.absolute(b)
898
899
900class _DomainGreater:
901 """
902 DomainGreater(v)(x) is True where x <= v.
903
904 """
905
906 def __init__(self, critical_value):
907 "DomainGreater(v)(x) = true where x <= v"
908 self.critical_value = critical_value
909
910 def __call__(self, x):
911 "Executes the call behavior."
912 with np.errstate(invalid='ignore'):
913 return umath.less_equal(x, self.critical_value)
914
915
916class _DomainGreaterEqual:
917 """
918 DomainGreaterEqual(v)(x) is True where x < v.
919
920 """
921
922 def __init__(self, critical_value):
923 "DomainGreaterEqual(v)(x) = true where x < v"
924 self.critical_value = critical_value
925
926 def __call__(self, x):
927 "Executes the call behavior."
928 with np.errstate(invalid='ignore'):
929 return umath.less(x, self.critical_value)
930
931
932class _MaskedUFunc:
933 def __init__(self, ufunc):
934 self.f = ufunc
935 self.__doc__ = ufunc.__doc__
936 self.__name__ = ufunc.__name__
937 self.__qualname__ = ufunc.__qualname__
938
939 def __str__(self):
940 return f"Masked version of {self.f}"
941
942
943class _MaskedUnaryOperation(_MaskedUFunc):
944 """
945 Defines masked version of unary operations, where invalid values are
946 pre-masked.
947
948 Parameters
949 ----------
950 mufunc : callable
951 The function for which to define a masked version. Made available
952 as ``_MaskedUnaryOperation.f``.
953 fill : scalar, optional
954 Filling value, default is 0.
955 domain : class instance
956 Domain for the function. Should be one of the ``_Domain*``
957 classes. Default is None.
958
959 """
960
961 def __init__(self, mufunc, fill=0, domain=None):
962 super().__init__(mufunc)
963 self.fill = fill
964 self.domain = domain
965 ufunc_domain[mufunc] = domain
966 ufunc_fills[mufunc] = fill
967
968 def __call__(self, a, *args, **kwargs):
969 """
970 Execute the call behavior.
971
972 """
973 d = getdata(a)
974 # Deal with domain
975 if self.domain is not None:
976 # Case 1.1. : Domained function
977 # nans at masked positions cause RuntimeWarnings, even though
978 # they are masked. To avoid this we suppress warnings.
979 with np.errstate(divide='ignore', invalid='ignore'):
980 result = self.f(d, *args, **kwargs)
981 # Make a mask
982 m = ~umath.isfinite(result)
983 m |= self.domain(d)
984 m |= getmask(a)
985 else:
986 # Case 1.2. : Function without a domain
987 # Get the result and the mask
988 with np.errstate(divide='ignore', invalid='ignore'):
989 result = self.f(d, *args, **kwargs)
990 m = getmask(a)
991
992 if not result.ndim:
993 # Case 2.1. : The result is scalarscalar
994 if m:
995 return masked
996 return result
997
998 if m is not nomask:
999 # Case 2.2. The result is an array
1000 # We need to fill the invalid data back w/ the input Now,
1001 # that's plain silly: in C, we would just skip the element and
1002 # keep the original, but we do have to do it that way in Python
1003
1004 # In case result has a lower dtype than the inputs (as in
1005 # equal)
1006 try:
1007 np.copyto(result, d, where=m)
1008 except TypeError:
1009 pass
1010 # Transform to
1011 masked_result = result.view(get_masked_subclass(a))
1012 masked_result._mask = m
1013 masked_result._update_from(a)
1014 return masked_result
1015
1016
1017class _MaskedBinaryOperation(_MaskedUFunc):
1018 """
1019 Define masked version of binary operations, where invalid
1020 values are pre-masked.
1021
1022 Parameters
1023 ----------
1024 mbfunc : function
1025 The function for which to define a masked version. Made available
1026 as ``_MaskedBinaryOperation.f``.
1027 domain : class instance
1028 Default domain for the function. Should be one of the ``_Domain*``
1029 classes. Default is None.
1030 fillx : scalar, optional
1031 Filling value for the first argument, default is 0.
1032 filly : scalar, optional
1033 Filling value for the second argument, default is 0.
1034
1035 """
1036
1037 def __init__(self, mbfunc, fillx=0, filly=0):
1038 """
1039 abfunc(fillx, filly) must be defined.
1040
1041 abfunc(x, filly) = x for all x to enable reduce.
1042
1043 """
1044 super().__init__(mbfunc)
1045 self.fillx = fillx
1046 self.filly = filly
1047 ufunc_domain[mbfunc] = None
1048 ufunc_fills[mbfunc] = (fillx, filly)
1049
1050 def __call__(self, a, b, *args, **kwargs):
1051 """
1052 Execute the call behavior.
1053
1054 """
1055 # Get the data, as ndarray
1056 (da, db) = (getdata(a), getdata(b))
1057 # Get the result
1058 with np.errstate():
1059 np.seterr(divide='ignore', invalid='ignore')
1060 result = self.f(da, db, *args, **kwargs)
1061 # Get the mask for the result
1062 (ma, mb) = (getmask(a), getmask(b))
1063 if ma is nomask:
1064 if mb is nomask:
1065 m = nomask
1066 else:
1067 m = umath.logical_or(getmaskarray(a), mb)
1068 elif mb is nomask:
1069 m = umath.logical_or(ma, getmaskarray(b))
1070 else:
1071 m = umath.logical_or(ma, mb)
1072
1073 # Case 1. : scalar
1074 if not result.ndim:
1075 if m:
1076 return masked
1077 return result
1078
1079 # Case 2. : array
1080 # Revert result to da where masked
1081 if m is not nomask and m.any():
1082 # any errors, just abort; impossible to guarantee masked values
1083 try:
1084 np.copyto(result, da, casting='unsafe', where=m)
1085 except Exception:
1086 pass
1087
1088 # Transforms to a (subclass of) MaskedArray
1089 masked_result = result.view(get_masked_subclass(a, b))
1090 masked_result._mask = m
1091 if isinstance(a, MaskedArray):
1092 masked_result._update_from(a)
1093 elif isinstance(b, MaskedArray):
1094 masked_result._update_from(b)
1095 return masked_result
1096
1097 def reduce(self, target, axis=0, dtype=None):
1098 """
1099 Reduce `target` along the given `axis`.
1100
1101 """
1102 tclass = get_masked_subclass(target)
1103 m = getmask(target)
1104 t = filled(target, self.filly)
1105 if t.shape == ():
1106 t = t.reshape(1)
1107 if m is not nomask:
1108 m = make_mask(m, copy=True)
1109 m.shape = (1,)
1110
1111 if m is nomask:
1112 tr = self.f.reduce(t, axis)
1113 mr = nomask
1114 else:
1115 tr = self.f.reduce(t, axis, dtype=dtype)
1116 mr = umath.logical_and.reduce(m, axis)
1117
1118 if not tr.shape:
1119 if mr:
1120 return masked
1121 else:
1122 return tr
1123 masked_tr = tr.view(tclass)
1124 masked_tr._mask = mr
1125 return masked_tr
1126
1127 def outer(self, a, b):
1128 """
1129 Return the function applied to the outer product of a and b.
1130
1131 """
1132 (da, db) = (getdata(a), getdata(b))
1133 d = self.f.outer(da, db)
1134 ma = getmask(a)
1135 mb = getmask(b)
1136 if ma is nomask and mb is nomask:
1137 m = nomask
1138 else:
1139 ma = getmaskarray(a)
1140 mb = getmaskarray(b)
1141 m = umath.logical_or.outer(ma, mb)
1142 if (not m.ndim) and m:
1143 return masked
1144 if m is not nomask:
1145 np.copyto(d, da, where=m)
1146 if not d.shape:
1147 return d
1148 masked_d = d.view(get_masked_subclass(a, b))
1149 masked_d._mask = m
1150 return masked_d
1151
1152 def accumulate(self, target, axis=0):
1153 """Accumulate `target` along `axis` after filling with y fill
1154 value.
1155
1156 """
1157 tclass = get_masked_subclass(target)
1158 t = filled(target, self.filly)
1159 result = self.f.accumulate(t, axis)
1160 masked_result = result.view(tclass)
1161 return masked_result
1162
1163
1164class _DomainedBinaryOperation(_MaskedUFunc):
1165 """
1166 Define binary operations that have a domain, like divide.
1167
1168 They have no reduce, outer or accumulate.
1169
1170 Parameters
1171 ----------
1172 mbfunc : function
1173 The function for which to define a masked version. Made available
1174 as ``_DomainedBinaryOperation.f``.
1175 domain : class instance
1176 Default domain for the function. Should be one of the ``_Domain*``
1177 classes.
1178 fillx : scalar, optional
1179 Filling value for the first argument, default is 0.
1180 filly : scalar, optional
1181 Filling value for the second argument, default is 0.
1182
1183 """
1184
1185 def __init__(self, dbfunc, domain, fillx=0, filly=0):
1186 """abfunc(fillx, filly) must be defined.
1187 abfunc(x, filly) = x for all x to enable reduce.
1188 """
1189 super().__init__(dbfunc)
1190 self.domain = domain
1191 self.fillx = fillx
1192 self.filly = filly
1193 ufunc_domain[dbfunc] = domain
1194 ufunc_fills[dbfunc] = (fillx, filly)
1195
1196 def __call__(self, a, b, *args, **kwargs):
1197 "Execute the call behavior."
1198 # Get the data
1199 (da, db) = (getdata(a), getdata(b))
1200 # Get the result
