Team Ai
Datasetpublic

codekingpro/portable-devtools

sourceHugging Faceupdated 5mo agoView on Hugging Face
1likes15kdownloads
core.py8930 linesDownload Raw Back to ma
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

Showing the first 1,200 of 8930 lines. Download the file for the rest.

codekingpro/portable-devtools · Team Ai