Coverage for /pythoncovmergedfiles/medio/medio/usr/local/lib/python3.9/dist-packages/scipy/sparse/_data.py: 25%
190 statements
« prev ^ index » next coverage.py v7.3.1, created at 2023-09-23 06:43 +0000
« prev ^ index » next coverage.py v7.3.1, created at 2023-09-23 06:43 +0000
1"""Base class for sparse matrice with a .data attribute
3 subclasses must provide a _with_data() method that
4 creates a new matrix with the same sparsity pattern
5 as self but with a different data array
7"""
9import numpy as np
11from ._base import _spbase, _ufuncs_with_fixed_point_at_zero
12from ._sputils import isscalarlike, validateaxis
14__all__ = []
17# TODO implement all relevant operations
18# use .data.__methods__() instead of /=, *=, etc.
19class _data_matrix(_spbase):
20 def __init__(self):
21 _spbase.__init__(self)
23 @property
24 def dtype(self):
25 return self.data.dtype
27 @dtype.setter
28 def dtype(self, newtype):
29 self.data.dtype = newtype
31 def _deduped_data(self):
32 if hasattr(self, 'sum_duplicates'):
33 self.sum_duplicates()
34 return self.data
36 def __abs__(self):
37 return self._with_data(abs(self._deduped_data()))
39 def __round__(self, ndigits=0):
40 return self._with_data(np.around(self._deduped_data(), decimals=ndigits))
42 def _real(self):
43 return self._with_data(self.data.real)
45 def _imag(self):
46 return self._with_data(self.data.imag)
48 def __neg__(self):
49 if self.dtype.kind == 'b':
50 raise NotImplementedError('negating a boolean sparse array is not '
51 'supported')
52 return self._with_data(-self.data)
54 def __imul__(self, other): # self *= other
55 if isscalarlike(other):
56 self.data *= other
57 return self
58 else:
59 return NotImplemented
61 def __itruediv__(self, other): # self /= other
62 if isscalarlike(other):
63 recip = 1.0 / other
64 self.data *= recip
65 return self
66 else:
67 return NotImplemented
69 def astype(self, dtype, casting='unsafe', copy=True):
70 dtype = np.dtype(dtype)
71 if self.dtype != dtype:
72 matrix = self._with_data(
73 self.data.astype(dtype, casting=casting, copy=True),
74 copy=True
75 )
76 return matrix._with_data(matrix._deduped_data(), copy=False)
77 elif copy:
78 return self.copy()
79 else:
80 return self
82 astype.__doc__ = _spbase.astype.__doc__
84 def conjugate(self, copy=True):
85 if np.issubdtype(self.dtype, np.complexfloating):
86 return self._with_data(self.data.conjugate(), copy=copy)
87 elif copy:
88 return self.copy()
89 else:
90 return self
92 conjugate.__doc__ = _spbase.conjugate.__doc__
94 def copy(self):
95 return self._with_data(self.data.copy(), copy=True)
97 copy.__doc__ = _spbase.copy.__doc__
99 def count_nonzero(self):
100 return np.count_nonzero(self._deduped_data())
102 count_nonzero.__doc__ = _spbase.count_nonzero.__doc__
104 def power(self, n, dtype=None):
105 """
106 This function performs element-wise power.
108 Parameters
109 ----------
110 n : n is a scalar
112 dtype : If dtype is not specified, the current dtype will be preserved.
113 """
114 if not isscalarlike(n):
115 raise NotImplementedError("input is not scalar")
117 data = self._deduped_data()
118 if dtype is not None:
119 data = data.astype(dtype)
120 return self._with_data(data ** n)
122 ###########################
123 # Multiplication handlers #
124 ###########################
126 def _mul_scalar(self, other):
127 return self._with_data(self.data * other)
130# Add the numpy unary ufuncs for which func(0) = 0 to _data_matrix.
131for npfunc in _ufuncs_with_fixed_point_at_zero:
132 name = npfunc.__name__
134 def _create_method(op):
135 def method(self):
136 result = op(self._deduped_data())
137 return self._with_data(result, copy=True)
139 method.__doc__ = ("Element-wise {}.\n\n"
140 "See `numpy.{}` for more information.".format(name, name))
141 method.__name__ = name
143 return method
145 setattr(_data_matrix, name, _create_method(npfunc))
148def _find_missing_index(ind, n):
149 for k, a in enumerate(ind):
150 if k != a:
151 return k
153 k += 1
154 if k < n:
155 return k
156 else:
157 return -1
160class _minmax_mixin:
161 """Mixin for min and max methods.
163 These are not implemented for dia_matrix, hence the separate class.
164 """
166 def _min_or_max_axis(self, axis, min_or_max):
167 N = self.shape[axis]
168 if N == 0:
169 raise ValueError("zero-size array to reduction operation")
170 M = self.shape[1 - axis]
171 idx_dtype = self._get_index_dtype(maxval=M)
173 mat = self.tocsc() if axis == 0 else self.tocsr()
174 mat.sum_duplicates()
176 major_index, value = mat._minor_reduce(min_or_max)
177 not_full = np.diff(mat.indptr)[major_index] < N
178 value[not_full] = min_or_max(value[not_full], 0)
180 mask = value != 0
181 major_index = np.compress(mask, major_index)
182 value = np.compress(mask, value)
184 if axis == 0:
185 return self._coo_container(
186 (value, (np.zeros(len(value), dtype=idx_dtype), major_index)),
187 dtype=self.dtype, shape=(1, M)
188 )
189 else:
190 return self._coo_container(
191 (value, (major_index, np.zeros(len(value), dtype=idx_dtype))),
192 dtype=self.dtype, shape=(M, 1)
193 )
195 def _min_or_max(self, axis, out, min_or_max):
196 if out is not None:
197 raise ValueError("Sparse matrices do not support "
198 "an 'out' parameter.")
200 validateaxis(axis)
202 if axis is None:
203 if 0 in self.shape:
204 raise ValueError("zero-size array to reduction operation")
206 zero = self.dtype.type(0)
207 if self.nnz == 0:
208 return zero
209 m = min_or_max.reduce(self._deduped_data().ravel())
210 if self.nnz != np.prod(self.shape):
211 m = min_or_max(zero, m)
212 return m
214 if axis < 0:
215 axis += 2
217 if (axis == 0) or (axis == 1):
218 return self._min_or_max_axis(axis, min_or_max)
219 else:
220 raise ValueError("axis out of range")
222 def _arg_min_or_max_axis(self, axis, argmin_or_argmax, compare):
223 if self.shape[axis] == 0:
224 raise ValueError("Can't apply the operation along a zero-sized "
225 "dimension.")
227 if axis < 0:
228 axis += 2
230 zero = self.dtype.type(0)
232 mat = self.tocsc() if axis == 0 else self.tocsr()
233 mat.sum_duplicates()
235 ret_size, line_size = mat._swap(mat.shape)
236 ret = np.zeros(ret_size, dtype=int)
238 nz_lines, = np.nonzero(np.diff(mat.indptr))
239 for i in nz_lines:
240 p, q = mat.indptr[i:i + 2]
241 data = mat.data[p:q]
242 indices = mat.indices[p:q]
243 extreme_index = argmin_or_argmax(data)
244 extreme_value = data[extreme_index]
245 if compare(extreme_value, zero) or q - p == line_size:
246 ret[i] = indices[extreme_index]
247 else:
248 zero_ind = _find_missing_index(indices, line_size)
249 if extreme_value == zero:
250 ret[i] = min(extreme_index, zero_ind)
251 else:
252 ret[i] = zero_ind
254 if axis == 1:
255 ret = ret.reshape(-1, 1)
257 return self._ascontainer(ret)
259 def _arg_min_or_max(self, axis, out, argmin_or_argmax, compare):
260 if out is not None:
261 raise ValueError("Sparse types do not support an 'out' parameter.")
263 validateaxis(axis)
265 if axis is not None:
266 return self._arg_min_or_max_axis(axis, argmin_or_argmax, compare)
268 if 0 in self.shape:
269 raise ValueError("Can't apply the operation to an empty matrix.")
271 if self.nnz == 0:
272 return 0
274 zero = self.dtype.type(0)
275 mat = self.tocoo()
276 # Convert to canonical form: no duplicates, sorted indices.
277 mat.sum_duplicates()
278 extreme_index = argmin_or_argmax(mat.data)
279 extreme_value = mat.data[extreme_index]
280 num_row, num_col = mat.shape
282 # If the min value is less than zero, or max is greater than zero,
283 # then we don't need to worry about implicit zeros.
284 if compare(extreme_value, zero):
285 # cast to Python int to avoid overflow and RuntimeError
286 return (int(mat.row[extreme_index]) * num_col +
287 int(mat.col[extreme_index]))
289 # Cheap test for the rare case where we have no implicit zeros.
290 size = num_row * num_col
291 if size == mat.nnz:
292 return (int(mat.row[extreme_index]) * num_col +
293 int(mat.col[extreme_index]))
295 # At this stage, any implicit zero could be the min or max value.
296 # After sum_duplicates(), the `row` and `col` arrays are guaranteed to
297 # be sorted in C-order, which means the linearized indices are sorted.
298 linear_indices = mat.row * num_col + mat.col
299 first_implicit_zero_index = _find_missing_index(linear_indices, size)
300 if extreme_value == zero:
301 return min(first_implicit_zero_index, extreme_index)
302 return first_implicit_zero_index
304 def max(self, axis=None, out=None):
305 """
306 Return the maximum of the array/matrix or maximum along an axis.
307 This takes all elements into account, not just the non-zero ones.
309 Parameters
310 ----------
311 axis : {-2, -1, 0, 1, None} optional
312 Axis along which the sum is computed. The default is to
313 compute the maximum over all elements, returning
314 a scalar (i.e., `axis` = `None`).
316 out : None, optional
317 This argument is in the signature *solely* for NumPy
318 compatibility reasons. Do not pass in anything except
319 for the default value, as this argument is not used.
321 Returns
322 -------
323 amax : coo_matrix or scalar
324 Maximum of `a`. If `axis` is None, the result is a scalar value.
325 If `axis` is given, the result is a sparse.coo_matrix of dimension
326 ``a.ndim - 1``.
328 See Also
329 --------
330 min : The minimum value of a sparse array/matrix along a given axis.
331 numpy.matrix.max : NumPy's implementation of 'max' for matrices
333 """
334 return self._min_or_max(axis, out, np.maximum)
336 def min(self, axis=None, out=None):
337 """
338 Return the minimum of the array/matrix or maximum along an axis.
339 This takes all elements into account, not just the non-zero ones.
341 Parameters
342 ----------
343 axis : {-2, -1, 0, 1, None} optional
344 Axis along which the sum is computed. The default is to
345 compute the minimum over all elements, returning
346 a scalar (i.e., `axis` = `None`).
348 out : None, optional
349 This argument is in the signature *solely* for NumPy
350 compatibility reasons. Do not pass in anything except for
351 the default value, as this argument is not used.
353 Returns
354 -------
355 amin : coo_matrix or scalar
356 Minimum of `a`. If `axis` is None, the result is a scalar value.
357 If `axis` is given, the result is a sparse.coo_matrix of dimension
358 ``a.ndim - 1``.
360 See Also
361 --------
362 max : The maximum value of a sparse array/matrix along a given axis.
363 numpy.matrix.min : NumPy's implementation of 'min' for matrices
365 """
366 return self._min_or_max(axis, out, np.minimum)
368 def nanmax(self, axis=None, out=None):
369 """
370 Return the maximum of the array/matrix or maximum along an axis, ignoring any
371 NaNs. This takes all elements into account, not just the non-zero
372 ones.
374 .. versionadded:: 1.11.0
376 Parameters
377 ----------
378 axis : {-2, -1, 0, 1, None} optional
379 Axis along which the maximum is computed. The default is to
380 compute the maximum over all elements, returning
381 a scalar (i.e., `axis` = `None`).
383 out : None, optional
384 This argument is in the signature *solely* for NumPy
385 compatibility reasons. Do not pass in anything except
386 for the default value, as this argument is not used.
388 Returns
389 -------
390 amax : coo_matrix or scalar
391 Maximum of `a`. If `axis` is None, the result is a scalar value.
392 If `axis` is given, the result is a sparse.coo_matrix of dimension
393 ``a.ndim - 1``.
395 See Also
396 --------
397 nanmin : The minimum value of a sparse array/matrix along a given axis,
398 ignoring NaNs.
399 max : The maximum value of a sparse array/matrix along a given axis,
400 propagating NaNs.
401 numpy.nanmax : NumPy's implementation of 'nanmax'.
403 """
404 return self._min_or_max(axis, out, np.fmax)
406 def nanmin(self, axis=None, out=None):
407 """
408 Return the minimum of the array/matrix or minimum along an axis, ignoring any
409 NaNs. This takes all elements into account, not just the non-zero
410 ones.
412 .. versionadded:: 1.11.0
414 Parameters
415 ----------
416 axis : {-2, -1, 0, 1, None} optional
417 Axis along which the minimum is computed. The default is to
418 compute the minimum over all elements, returning
419 a scalar (i.e., `axis` = `None`).
421 out : None, optional
422 This argument is in the signature *solely* for NumPy
423 compatibility reasons. Do not pass in anything except for
424 the default value, as this argument is not used.
426 Returns
427 -------
428 amin : coo_matrix or scalar
429 Minimum of `a`. If `axis` is None, the result is a scalar value.
430 If `axis` is given, the result is a sparse.coo_matrix of dimension
431 ``a.ndim - 1``.
433 See Also
434 --------
435 nanmax : The maximum value of a sparse array/matrix along a given axis,
436 ignoring NaNs.
437 min : The minimum value of a sparse array/matrix along a given axis,
438 propagating NaNs.
439 numpy.nanmin : NumPy's implementation of 'nanmin'.
441 """
442 return self._min_or_max(axis, out, np.fmin)
444 def argmax(self, axis=None, out=None):
445 """Return indices of maximum elements along an axis.
447 Implicit zero elements are also taken into account. If there are
448 several maximum values, the index of the first occurrence is returned.
450 Parameters
451 ----------
452 axis : {-2, -1, 0, 1, None}, optional
453 Axis along which the argmax is computed. If None (default), index
454 of the maximum element in the flatten data is returned.
455 out : None, optional
456 This argument is in the signature *solely* for NumPy
457 compatibility reasons. Do not pass in anything except for
458 the default value, as this argument is not used.
460 Returns
461 -------
462 ind : numpy.matrix or int
463 Indices of maximum elements. If matrix, its size along `axis` is 1.
464 """
465 return self._arg_min_or_max(axis, out, np.argmax, np.greater)
467 def argmin(self, axis=None, out=None):
468 """Return indices of minimum elements along an axis.
470 Implicit zero elements are also taken into account. If there are
471 several minimum values, the index of the first occurrence is returned.
473 Parameters
474 ----------
475 axis : {-2, -1, 0, 1, None}, optional
476 Axis along which the argmin is computed. If None (default), index
477 of the minimum element in the flatten data is returned.
478 out : None, optional
479 This argument is in the signature *solely* for NumPy
480 compatibility reasons. Do not pass in anything except for
481 the default value, as this argument is not used.
483 Returns
484 -------
485 ind : numpy.matrix or int
486 Indices of minimum elements. If matrix, its size along `axis` is 1.
487 """
488 return self._arg_min_or_max(axis, out, np.argmin, np.less)