CoolFace
Apppublic

Aluode/PerceptionLabPortable

sourceHugging Faceupdated 9mo agoView on Hugging Face
0likes
_mask.py182 linesDownload Raw Back to utils
1# Authors: The scikit-learn developers
2# SPDX-License-Identifier: BSD-3-Clause
3
4from contextlib import suppress
5
6import numpy as np
7from scipy import sparse as sp
8
9from ._missing import is_scalar_nan
10from ._param_validation import validate_params
11from .fixes import _object_dtype_isnan
12
13
14def _get_dense_mask(X, value_to_mask):
15    with suppress(ImportError, AttributeError):
16        # We also suppress `AttributeError` because older versions of pandas do
17        # not have `NA`.
18        import pandas
19
20        if value_to_mask is pandas.NA:
21            return pandas.isna(X)
22
23    if is_scalar_nan(value_to_mask):
24        if X.dtype.kind == "f":
25            Xt = np.isnan(X)
26        elif X.dtype.kind in ("i", "u"):
27            # can't have NaNs in integer array.
28            Xt = np.zeros(X.shape, dtype=bool)
29        else:
30            # np.isnan does not work on object dtypes.
31            Xt = _object_dtype_isnan(X)
32    else:
33        Xt = X == value_to_mask
34
35    return Xt
36
37
38def _get_mask(X, value_to_mask):
39    """Compute the boolean mask X == value_to_mask.
40
41    Parameters
42    ----------
43    X : {ndarray, sparse matrix} of shape (n_samples, n_features)
44        Input data, where ``n_samples`` is the number of samples and
45        ``n_features`` is the number of features.
46
47    value_to_mask : {int, float}
48        The value which is to be masked in X.
49
50    Returns
51    -------
52    X_mask : {ndarray, sparse matrix} of shape (n_samples, n_features)
53        Missing mask.
54    """
55    if not sp.issparse(X):
56        # For all cases apart of a sparse input where we need to reconstruct
57        # a sparse output
58        return _get_dense_mask(X, value_to_mask)
59
60    Xt = _get_dense_mask(X.data, value_to_mask)
61
62    sparse_constructor = sp.csr_matrix if X.format == "csr" else sp.csc_matrix
63    Xt_sparse = sparse_constructor(
64        (Xt, X.indices.copy(), X.indptr.copy()), shape=X.shape, dtype=bool
65    )
66
67    return Xt_sparse
68
69
70@validate_params(
71    {
72        "X": ["array-like", "sparse matrix"],
73        "mask": ["array-like"],
74    },
75    prefer_skip_nested_validation=True,
76)
77def safe_mask(X, mask):
78    """Return a mask which is safe to use on X.
79
80    Parameters
81    ----------
82    X : {array-like, sparse matrix}
83        Data on which to apply mask.
84
85    mask : array-like
86        Mask to be used on X.
87
88    Returns
89    -------
90    mask : ndarray
91        Array that is safe to use on X.
92
93    Examples
94    --------
95    >>> from sklearn.utils import safe_mask
96    >>> from scipy.sparse import csr_matrix
97    >>> data = csr_matrix([[1], [2], [3], [4], [5]])
98    >>> condition = [False, True, True, False, True]
99    >>> mask = safe_mask(data, condition)
100    >>> data[mask].toarray()
101    array([[2],
102           [3],
103           [5]])
104    """
105    mask = np.asarray(mask)
106    if np.issubdtype(mask.dtype, np.signedinteger):
107        return mask
108
109    if hasattr(X, "toarray"):
110        ind = np.arange(mask.shape[0])
111        mask = ind[mask]
112    return mask
113
114
115def axis0_safe_slice(X, mask, len_mask):
116    """Return a mask which is safer to use on X than safe_mask.
117
118    This mask is safer than safe_mask since it returns an
119    empty array, when a sparse matrix is sliced with a boolean mask
120    with all False, instead of raising an unhelpful error in older
121    versions of SciPy.
122
123    See: https://github.com/scipy/scipy/issues/5361
124
125    Also note that we can avoid doing the dot product by checking if
126    the len_mask is not zero in _huber_loss_and_gradient but this
127    is not going to be the bottleneck, since the number of outliers
128    and non_outliers are typically non-zero and it makes the code
129    tougher to follow.
130
131    Parameters
132    ----------
133    X : {array-like, sparse matrix}
134        Data on which to apply mask.
135
136    mask : ndarray
137        Mask to be used on X.
138
139    len_mask : int
140        The length of the mask.
141
142    Returns
143    -------
144    mask : ndarray
145        Array that is safe to use on X.
146    """
147    if len_mask != 0:
148        return X[safe_mask(X, mask), :]
149    return np.zeros(shape=(0, X.shape[1]))
150
151
152def indices_to_mask(indices, mask_length):
153    """Convert list of indices to boolean mask.
154
155    Parameters
156    ----------
157    indices : list-like
158        List of integers treated as indices.
159    mask_length : int
160        Length of boolean mask to be generated.
161        This parameter must be greater than max(indices).
162
163    Returns
164    -------
165    mask : 1d boolean nd-array
166        Boolean array that is True where indices are present, else False.
167
168    Examples
169    --------
170    >>> from sklearn.utils._mask import indices_to_mask
171    >>> indices = [1, 2 , 3, 4]
172    >>> indices_to_mask(indices, 5)
173    array([False,  True,  True,  True,  True])
174    """
175    if mask_length <= np.max(indices):
176        raise ValueError("mask_length must be greater than max(indices)")
177
178    mask = np.zeros(mask_length, dtype=bool)
179    mask[indices] = True
180
181    return mask
182