Coverage for /usr/lib/python3/dist-packages/scipy/sparse/_base.py: 29%
534 statements
« prev ^ index » next coverage.py v7.4.4, created at 2025-06-14 15:25 +0200
« prev ^ index » next coverage.py v7.4.4, created at 2025-06-14 15:25 +0200
1"""Base class for sparse matrices"""
2from warnings import warn
4import numpy as np
6from ._sputils import (asmatrix, check_reshape_kwargs, check_shape,
7 get_sum_dtype, isdense, isscalarlike,
8 matrix, validateaxis,)
10from ._matrix import spmatrix
12__all__ = ['isspmatrix', 'issparse', 'sparray',
13 'SparseWarning', 'SparseEfficiencyWarning']
16class SparseWarning(Warning):
17 pass
20class SparseFormatWarning(SparseWarning):
21 pass
24class SparseEfficiencyWarning(SparseWarning):
25 pass
28# The formats that we might potentially understand.
29_formats = {'csc': [0, "Compressed Sparse Column"],
30 'csr': [1, "Compressed Sparse Row"],
31 'dok': [2, "Dictionary Of Keys"],
32 'lil': [3, "List of Lists"],
33 'dod': [4, "Dictionary of Dictionaries"],
34 'sss': [5, "Symmetric Sparse Skyline"],
35 'coo': [6, "COOrdinate"],
36 'lba': [7, "Linpack BAnded"],
37 'egd': [8, "Ellpack-itpack Generalized Diagonal"],
38 'dia': [9, "DIAgonal"],
39 'bsr': [10, "Block Sparse Row"],
40 'msr': [11, "Modified compressed Sparse Row"],
41 'bsc': [12, "Block Sparse Column"],
42 'msc': [13, "Modified compressed Sparse Column"],
43 'ssk': [14, "Symmetric SKyline"],
44 'nsk': [15, "Nonsymmetric SKyline"],
45 'jad': [16, "JAgged Diagonal"],
46 'uss': [17, "Unsymmetric Sparse Skyline"],
47 'vbr': [18, "Variable Block Row"],
48 'und': [19, "Undefined"]
49 }
52# These univariate ufuncs preserve zeros.
53_ufuncs_with_fixed_point_at_zero = frozenset([
54 np.sin, np.tan, np.arcsin, np.arctan, np.sinh, np.tanh, np.arcsinh,
55 np.arctanh, np.rint, np.sign, np.expm1, np.log1p, np.deg2rad,
56 np.rad2deg, np.floor, np.ceil, np.trunc, np.sqrt])
59MAXPRINT = 50
62class _spbase:
63 """ This class provides a base class for all sparse arrays. It
64 cannot be instantiated. Most of the work is provided by subclasses.
65 """
67 __array_priority__ = 10.1
68 _format = 'und' # undefined
69 ndim = 2
71 @property
72 def _bsr_container(self):
73 from ._bsr import bsr_array
74 return bsr_array
76 @property
77 def _coo_container(self):
78 from ._coo import coo_array
79 return coo_array
81 @property
82 def _csc_container(self):
83 from ._csc import csc_array
84 return csc_array
86 @property
87 def _csr_container(self):
88 from ._csr import csr_array
89 return csr_array
91 @property
92 def _dia_container(self):
93 from ._dia import dia_array
94 return dia_array
96 @property
97 def _dok_container(self):
98 from ._dok import dok_array
99 return dok_array
101 @property
102 def _lil_container(self):
103 from ._lil import lil_array
104 return lil_array
106 _is_array = True
108 def __init__(self, maxprint=MAXPRINT):
109 self._shape = None
110 if self.__class__.__name__ == '_spbase':
111 raise ValueError("This class is not intended"
112 " to be instantiated directly.")
113 self.maxprint = maxprint
115 # Use this in 1.13.0 and later:
116 #
117 # @property
118 # def shape(self):
119 # return self._shape
121 def reshape(self, *args, **kwargs):
122 """reshape(self, shape, order='C', copy=False)
124 Gives a new shape to a sparse array without changing its data.
126 Parameters
127 ----------
128 shape : length-2 tuple of ints
129 The new shape should be compatible with the original shape.
130 order : {'C', 'F'}, optional
131 Read the elements using this index order. 'C' means to read and
132 write the elements using C-like index order; e.g., read entire first
133 row, then second row, etc. 'F' means to read and write the elements
134 using Fortran-like index order; e.g., read entire first column, then
135 second column, etc.
136 copy : bool, optional
137 Indicates whether or not attributes of self should be copied
138 whenever possible. The degree to which attributes are copied varies
139 depending on the type of sparse array being used.
141 Returns
142 -------
143 reshaped : sparse array
144 A sparse array with the given `shape`, not necessarily of the same
145 format as the current object.
147 See Also
148 --------
149 numpy.reshape : NumPy's implementation of 'reshape' for ndarrays
150 """
151 # If the shape already matches, don't bother doing an actual reshape
152 # Otherwise, the default is to convert to COO and use its reshape
153 shape = check_shape(args, self.shape)
154 order, copy = check_reshape_kwargs(kwargs)
155 if shape == self.shape:
156 if copy:
157 return self.copy()
158 else:
159 return self
161 return self.tocoo(copy=copy).reshape(shape, order=order, copy=False)
163 def resize(self, shape):
164 """Resize the array in-place to dimensions given by ``shape``
166 Any elements that lie within the new shape will remain at the same
167 indices, while non-zero elements lying outside the new shape are
168 removed.
170 Parameters
171 ----------
172 shape : (int, int)
173 number of rows and columns in the new array
175 Notes
176 -----
177 The semantics are not identical to `numpy.ndarray.resize` or
178 `numpy.resize`. Here, the same data will be maintained at each index
179 before and after reshape, if that index is within the new bounds. In
180 numpy, resizing maintains contiguity of the array, moving elements
181 around in the logical array but not within a flattened representation.
183 We give no guarantees about whether the underlying data attributes
184 (arrays, etc.) will be modified in place or replaced with new objects.
185 """
186 # As an inplace operation, this requires implementation in each format.
187 raise NotImplementedError(
188 f'{type(self).__name__}.resize is not implemented')
190 def astype(self, dtype, casting='unsafe', copy=True):
191 """Cast the array elements to a specified type.
193 Parameters
194 ----------
195 dtype : string or numpy dtype
196 Typecode or data-type to which to cast the data.
197 casting : {'no', 'equiv', 'safe', 'same_kind', 'unsafe'}, optional
198 Controls what kind of data casting may occur.
199 Defaults to 'unsafe' for backwards compatibility.
200 'no' means the data types should not be cast at all.
201 'equiv' means only byte-order changes are allowed.
202 'safe' means only casts which can preserve values are allowed.
203 'same_kind' means only safe casts or casts within a kind,
204 like float64 to float32, are allowed.
205 'unsafe' means any data conversions may be done.
206 copy : bool, optional
207 If `copy` is `False`, the result might share some memory with this
208 array. If `copy` is `True`, it is guaranteed that the result and
209 this array do not share any memory.
210 """
212 dtype = np.dtype(dtype)
213 if self.dtype != dtype:
214 return self.tocsr().astype(
215 dtype, casting=casting, copy=copy).asformat(self.format)
216 elif copy:
217 return self.copy()
218 else:
219 return self
221 @classmethod
222 def _ascontainer(cls, X, **kwargs):
223 if cls._is_array:
224 return np.asarray(X, **kwargs)
225 else:
226 return asmatrix(X, **kwargs)
228 @classmethod
229 def _container(cls, X, **kwargs):
230 if cls._is_array:
231 return np.array(X, **kwargs)
232 else:
233 return matrix(X, **kwargs)
235 def _asfptype(self):
236 """Upcast array to a floating point format (if necessary)"""
238 fp_types = ['f', 'd', 'F', 'D']
240 if self.dtype.char in fp_types:
241 return self
242 else:
243 for fp_type in fp_types:
244 if self.dtype <= np.dtype(fp_type):
245 return self.astype(fp_type)
247 raise TypeError('cannot upcast [%s] to a floating '
248 'point format' % self.dtype.name)
250 def __iter__(self):
251 for r in range(self.shape[0]):
252 yield self[r, :]
254 def _getmaxprint(self):
255 """Maximum number of elements to display when printed."""
256 return self.maxprint
258 def count_nonzero(self):
259 """Number of non-zero entries, equivalent to
261 np.count_nonzero(a.toarray())
263 Unlike the nnz property, which return the number of stored
264 entries (the length of the data attribute), this method counts the
265 actual number of non-zero entries in data.
266 """
267 raise NotImplementedError("count_nonzero not implemented for %s." %
268 self.__class__.__name__)
270 def _getnnz(self, axis=None):
271 """Number of stored values, including explicit zeros.
273 Parameters
274 ----------
275 axis : None, 0, or 1
276 Select between the number of values across the whole array, in
277 each column, or in each row.
279 See also
280 --------
281 count_nonzero : Number of non-zero entries
282 """
283 raise NotImplementedError("getnnz not implemented for %s." %
284 self.__class__.__name__)
286 @property
287 def nnz(self):
288 """Number of stored values, including explicit zeros.
290 See also
291 --------
292 count_nonzero : Number of non-zero entries
293 """
294 return self._getnnz()
296 @property
297 def format(self):
298 return self._format
300 def __repr__(self):
301 _, format_name = _formats[self.format]
302 sparse_cls = 'array' if self._is_array else 'matrix'
303 return f"<%dx%d sparse {sparse_cls} of type '%s'\n" \
304 "\twith %d stored elements in %s format>" % \
305 (self.shape + (self.dtype.type, self.nnz, format_name))
307 def __str__(self):
308 maxprint = self._getmaxprint()
310 A = self.tocoo()
312 # helper function, outputs "(i,j) v"
313 def tostr(row, col, data):
314 triples = zip(list(zip(row, col)), data)
315 return '\n'.join([(' %s\t%s' % t) for t in triples])
317 if self.nnz > maxprint:
318 half = maxprint // 2
319 out = tostr(A.row[:half], A.col[:half], A.data[:half])
320 out += "\n :\t:\n"
321 half = maxprint - maxprint//2
322 out += tostr(A.row[-half:], A.col[-half:], A.data[-half:])
323 else:
324 out = tostr(A.row, A.col, A.data)
326 return out
328 def __bool__(self): # Simple -- other ideas?
329 if self.shape == (1, 1):
330 return self.nnz != 0
331 else:
332 raise ValueError("The truth value of an array with more than one "
333 "element is ambiguous. Use a.any() or a.all().")
334 __nonzero__ = __bool__
336 # What should len(sparse) return? For consistency with dense matrices,
337 # perhaps it should be the number of rows? But for some uses the number of
338 # non-zeros is more important. For now, raise an exception!
339 def __len__(self):
340 raise TypeError("sparse array length is ambiguous; use getnnz()"
341 " or shape[0]")
343 def asformat(self, format, copy=False):
344 """Return this array in the passed format.
346 Parameters
347 ----------
348 format : {str, None}
349 The desired sparse format ("csr", "csc", "lil", "dok", "array", ...)
350 or None for no conversion.
351 copy : bool, optional
352 If True, the result is guaranteed to not share data with self.
354 Returns
355 -------
356 A : This array in the passed format.
357 """
358 if format is None or format == self.format:
359 if copy:
360 return self.copy()
361 else:
362 return self
363 else:
364 try:
365 convert_method = getattr(self, 'to' + format)
366 except AttributeError as e:
367 raise ValueError(f'Format {format} is unknown.') from e
369 # Forward the copy kwarg, if it's accepted.
370 try:
371 return convert_method(copy=copy)
372 except TypeError:
373 return convert_method()
375 ###################################################################
376 # NOTE: All arithmetic operations use csr_matrix by default.
377 # Therefore a new sparse array format just needs to define a
378 # .tocsr() method to provide arithmetic support. Any of these
379 # methods can be overridden for efficiency.
380 ####################################################################
382 def multiply(self, other):
383 """Point-wise multiplication by another array
384 """
385 return self.tocsr().multiply(other)
387 def maximum(self, other):
388 """Element-wise maximum between this and another array."""
389 return self.tocsr().maximum(other)
391 def minimum(self, other):
392 """Element-wise minimum between this and another array."""
393 return self.tocsr().minimum(other)
395 def dot(self, other):
396 """Ordinary dot product
398 Examples
399 --------
400 >>> import numpy as np
401 >>> from scipy.sparse import csr_array
402 >>> A = csr_array([[1, 2, 0], [0, 0, 3], [4, 0, 5]])
403 >>> v = np.array([1, 0, -1])
404 >>> A.dot(v)
405 array([ 1, -3, -1], dtype=int64)
407 """
408 if np.isscalar(other):
409 return self * other
410 else:
411 return self @ other
413 def power(self, n, dtype=None):
414 """Element-wise power."""
415 return self.tocsr().power(n, dtype=dtype)
417 def __eq__(self, other):
418 return self.tocsr().__eq__(other)
420 def __ne__(self, other):
421 return self.tocsr().__ne__(other)
423 def __lt__(self, other):
424 return self.tocsr().__lt__(other)
426 def __gt__(self, other):
427 return self.tocsr().__gt__(other)
429 def __le__(self, other):
430 return self.tocsr().__le__(other)
432 def __ge__(self, other):
433 return self.tocsr().__ge__(other)
435 def __abs__(self):
436 return abs(self.tocsr())
438 def __round__(self, ndigits=0):
439 return round(self.tocsr(), ndigits=ndigits)
441 def _add_sparse(self, other):
442 return self.tocsr()._add_sparse(other)
444 def _add_dense(self, other):
445 return self.tocoo()._add_dense(other)
447 def _sub_sparse(self, other):
448 return self.tocsr()._sub_sparse(other)
450 def _sub_dense(self, other):
451 return self.todense() - other
453 def _rsub_dense(self, other):
454 # note: this can't be replaced by other + (-self) for unsigned types
455 return other - self.todense()
457 def __add__(self, other): # self + other
458 if isscalarlike(other):
459 if other == 0:
460 return self.copy()
461 # Now we would add this scalar to every element.
462 raise NotImplementedError('adding a nonzero scalar to a '
463 'sparse array is not supported')
464 elif issparse(other):
465 if other.shape != self.shape:
466 raise ValueError("inconsistent shapes")
467 return self._add_sparse(other)
468 elif isdense(other):
469 other = np.broadcast_to(other, self.shape)
470 return self._add_dense(other)
471 else:
472 return NotImplemented
474 def __radd__(self,other): # other + self
475 return self.__add__(other)
477 def __sub__(self, other): # self - other
478 if isscalarlike(other):
479 if other == 0:
480 return self.copy()
481 raise NotImplementedError('subtracting a nonzero scalar from a '
482 'sparse array is not supported')
483 elif issparse(other):
484 if other.shape != self.shape:
485 raise ValueError("inconsistent shapes")
486 return self._sub_sparse(other)
487 elif isdense(other):
488 other = np.broadcast_to(other, self.shape)
489 return self._sub_dense(other)
490 else:
491 return NotImplemented
493 def __rsub__(self,other): # other - self
494 if isscalarlike(other):
495 if other == 0:
496 return -self.copy()
497 raise NotImplementedError('subtracting a sparse array from a '
498 'nonzero scalar is not supported')
499 elif isdense(other):
500 other = np.broadcast_to(other, self.shape)
501 return self._rsub_dense(other)
502 else:
503 return NotImplemented
505 def _mul_dispatch(self, other):
506 """`np.matrix`-compatible mul, i.e. `dot` or `NotImplemented`
508 interpret other and call one of the following
509 self._mul_scalar()
510 self._mul_vector()
511 self._mul_multivector()
512 self._mul_sparse_matrix()
513 """
514 # This method has to be different from `__matmul__` because it is also
515 # called by sparse matrix classes.
517 M, N = self.shape
519 if other.__class__ is np.ndarray:
520 # Fast path for the most common case
521 if other.shape == (N,):
522 return self._mul_vector(other)
523 elif other.shape == (N, 1):
524 return self._mul_vector(other.ravel()).reshape(M, 1)
525 elif other.ndim == 2 and other.shape[0] == N:
526 return self._mul_multivector(other)
528 if isscalarlike(other):
529 # scalar value
530 return self._mul_scalar(other)
532 if issparse(other):
533 if self.shape[1] != other.shape[0]:
534 raise ValueError('dimension mismatch')
535 return self._mul_sparse_matrix(other)
537 # If it's a list or whatever, treat it like an array
538 other_a = np.asanyarray(other)
540 if other_a.ndim == 0 and other_a.dtype == np.object_:
541 # Not interpretable as an array; return NotImplemented so that
542 # other's __rmul__ can kick in if that's implemented.
543 return NotImplemented
545 try:
546 other.shape
547 except AttributeError:
548 other = other_a
550 if other.ndim == 1 or other.ndim == 2 and other.shape[1] == 1:
551 # dense row or column vector
552 if other.shape != (N,) and other.shape != (N, 1):
553 raise ValueError('dimension mismatch')
555 result = self._mul_vector(np.ravel(other))
557 if isinstance(other, np.matrix):
558 result = self._ascontainer(result)
560 if other.ndim == 2 and other.shape[1] == 1:
561 # If 'other' was an (nx1) column vector, reshape the result
562 result = result.reshape(-1, 1)
564 return result
566 elif other.ndim == 2:
567 ##
568 # dense 2D array or matrix ("multivector")
570 if other.shape[0] != self.shape[1]:
571 raise ValueError('dimension mismatch')
573 result = self._mul_multivector(np.asarray(other))
575 if isinstance(other, np.matrix):
576 result = self._ascontainer(result)
578 return result
580 else:
581 raise ValueError('could not interpret dimensions')
583 def __mul__(self, *args, **kwargs):
584 return self.multiply(*args, **kwargs)
586 # by default, use CSR for __mul__ handlers
587 def _mul_scalar(self, other):
588 return self.tocsr()._mul_scalar(other)
590 def _mul_vector(self, other):
591 return self.tocsr()._mul_vector(other)
593 def _mul_multivector(self, other):
594 return self.tocsr()._mul_multivector(other)
596 def _mul_sparse_matrix(self, other):
597 return self.tocsr()._mul_sparse_matrix(other)
599 def _rmul_dispatch(self, other):
600 if isscalarlike(other):
601 return self._mul_scalar(other)
602 else:
603 # Don't use asarray unless we have to
604 try:
605 tr = other.transpose()
606 except AttributeError:
607 tr = np.asarray(other).transpose()
608 ret = self.transpose()._mul_dispatch(tr)
609 if ret is NotImplemented:
610 return NotImplemented
611 return ret.transpose()
613 def __rmul__(self, *args, **kwargs): # other * self
614 return self.multiply(*args, **kwargs)
616 #######################
617 # matmul (@) operator #
618 #######################
620 def __matmul__(self, other):
621 if isscalarlike(other):
622 raise ValueError("Scalar operands are not allowed, "
623 "use '*' instead")
624 return self._mul_dispatch(other)
626 def __rmatmul__(self, other):
627 if isscalarlike(other):
628 raise ValueError("Scalar operands are not allowed, "
629 "use '*' instead")
630 return self._rmul_dispatch(other)
632 ####################
633 # Other Arithmetic #
634 ####################
636 def _divide(self, other, true_divide=False, rdivide=False):
637 if isscalarlike(other):
638 if rdivide:
639 if true_divide:
640 return np.true_divide(other, self.todense())
641 else:
642 return np.divide(other, self.todense())
644 if true_divide and np.can_cast(self.dtype, np.float_):
645 return self.astype(np.float_)._mul_scalar(1./other)
646 else:
647 r = self._mul_scalar(1./other)
649 scalar_dtype = np.asarray(other).dtype
650 if (np.issubdtype(self.dtype, np.integer) and
651 np.issubdtype(scalar_dtype, np.integer)):
652 return r.astype(self.dtype)
653 else:
654 return r
656 elif isdense(other):
657 if not rdivide:
658 if true_divide:
659 recip = np.true_divide(1., other)
660 else:
661 recip = np.divide(1., other)
662 return self.multiply(recip)
663 else:
664 if true_divide:
665 return np.true_divide(other, self.todense())
666 else:
667 return np.divide(other, self.todense())
668 elif issparse(other):
669 if rdivide:
670 return other._divide(self, true_divide, rdivide=False)
672 self_csr = self.tocsr()
673 if true_divide and np.can_cast(self.dtype, np.float_):
674 return self_csr.astype(np.float_)._divide_sparse(other)
675 else:
676 return self_csr._divide_sparse(other)
677 else:
678 return NotImplemented
680 def __truediv__(self, other):
681 return self._divide(other, true_divide=True)
683 def __div__(self, other):
684 # Always do true division
685 return self._divide(other, true_divide=True)
687 def __rtruediv__(self, other):
688 # Implementing this as the inverse would be too magical -- bail out
689 return NotImplemented
691 def __rdiv__(self, other):
692 # Implementing this as the inverse would be too magical -- bail out
693 return NotImplemented
695 def __neg__(self):
696 return -self.tocsr()
698 def __iadd__(self, other):
699 return NotImplemented
701 def __isub__(self, other):
702 return NotImplemented
704 def __imul__(self, other):
705 return NotImplemented
707 def __idiv__(self, other):
708 return self.__itruediv__(other)
710 def __itruediv__(self, other):
711 return NotImplemented
713 def __pow__(self, *args, **kwargs):
714 return self.power(*args, **kwargs)
716 @property
717 def A(self) -> np.ndarray:
718 if self._is_array:
719 warn(np.VisibleDeprecationWarning(
720 "`.A` is deprecated and will be removed in v1.13.0. "
721 "Use `.toarray()` instead."
722 ))
723 return self.toarray()
725 @property
726 def T(self):
727 return self.transpose()
729 @property
730 def H(self):
731 if self._is_array:
732 warn(np.VisibleDeprecationWarning(
733 "`.H` is deprecated and will be removed in v1.13.0. "
734 "Please use `.T.conjugate()` instead."
735 ))
736 return self.T.conjugate()
738 @property
739 def real(self):
740 return self._real()
742 @property
743 def imag(self):
744 return self._imag()
746 @property
747 def size(self):
748 return self._getnnz()
750 def transpose(self, axes=None, copy=False):
751 """
752 Reverses the dimensions of the sparse array.
754 Parameters
755 ----------
756 axes : None, optional
757 This argument is in the signature *solely* for NumPy
758 compatibility reasons. Do not pass in anything except
759 for the default value.
760 copy : bool, optional
761 Indicates whether or not attributes of `self` should be
762 copied whenever possible. The degree to which attributes
763 are copied varies depending on the type of sparse array
764 being used.
766 Returns
767 -------
768 p : `self` with the dimensions reversed.
770 See Also
771 --------
772 numpy.transpose : NumPy's implementation of 'transpose' for ndarrays
773 """
774 return self.tocsr(copy=copy).transpose(axes=axes, copy=False)
776 def conjugate(self, copy=True):
777 """Element-wise complex conjugation.
779 If the array is of non-complex data type and `copy` is False,
780 this method does nothing and the data is not copied.
782 Parameters
783 ----------
784 copy : bool, optional
785 If True, the result is guaranteed to not share data with self.
787 Returns
788 -------
789 A : The element-wise complex conjugate.
791 """
792 if np.issubdtype(self.dtype, np.complexfloating):
793 return self.tocsr(copy=copy).conjugate(copy=False)
794 elif copy:
795 return self.copy()
796 else:
797 return self
799 def conj(self, copy=True):
800 return self.conjugate(copy=copy)
802 conj.__doc__ = conjugate.__doc__
804 def _real(self):
805 return self.tocsr()._real()
807 def _imag(self):
808 return self.tocsr()._imag()
810 def nonzero(self):
811 """nonzero indices
813 Returns a tuple of arrays (row,col) containing the indices
814 of the non-zero elements of the array.
816 Examples
817 --------
818 >>> from scipy.sparse import csr_array
819 >>> A = csr_array([[1,2,0],[0,0,3],[4,0,5]])
820 >>> A.nonzero()
821 (array([0, 0, 1, 2, 2]), array([0, 1, 2, 0, 2]))
823 """
825 # convert to COOrdinate format
826 A = self.tocoo()
827 nz_mask = A.data != 0
828 return (A.row[nz_mask], A.col[nz_mask])
830 def _getcol(self, j):
831 """Returns a copy of column j of the array, as an (m x 1) sparse
832 array (column vector).
833 """
834 # Subclasses should override this method for efficiency.
835 # Post-multiply by a (n x 1) column vector 'a' containing all zeros
836 # except for a_j = 1
837 n = self.shape[1]
838 if j < 0:
839 j += n
840 if j < 0 or j >= n:
841 raise IndexError("index out of bounds")
842 col_selector = self._csc_container(([1], [[j], [0]]),
843 shape=(n, 1), dtype=self.dtype)
844 return self @ col_selector
846 def _getrow(self, i):
847 """Returns a copy of row i of the array, as a (1 x n) sparse
848 array (row vector).
849 """
850 # Subclasses should override this method for efficiency.
851 # Pre-multiply by a (1 x m) row vector 'a' containing all zeros
852 # except for a_i = 1
853 m = self.shape[0]
854 if i < 0:
855 i += m
856 if i < 0 or i >= m:
857 raise IndexError("index out of bounds")
858 row_selector = self._csr_container(([1], [[0], [i]]),
859 shape=(1, m), dtype=self.dtype)
860 return row_selector @ self
862 # The following dunder methods cannot be implemented.
863 #
864 # def __array__(self):
865 # # Sparse matrices rely on NumPy wrapping them in object arrays under
866 # # the hood to make unary ufuncs work on them. So we cannot raise
867 # # TypeError here - which would be handy to not give users object
868 # # arrays they probably don't want (they're looking for `.toarray()`).
869 # #
870 # # Conversion with `toarray()` would also break things because of the
871 # # behavior discussed above, plus we want to avoid densification by
872 # # accident because that can too easily blow up memory.
873 #
874 # def __array_ufunc__(self):
875 # # We cannot implement __array_ufunc__ due to mismatching semantics.
876 # # See gh-7707 and gh-7349 for details.
877 #
878 # def __array_function__(self):
879 # # We cannot implement __array_function__ due to mismatching semantics.
880 # # See gh-10362 for details.
882 def todense(self, order=None, out=None):
883 """
884 Return a dense matrix representation of this sparse array.
886 Parameters
887 ----------
888 order : {'C', 'F'}, optional
889 Whether to store multi-dimensional data in C (row-major)
890 or Fortran (column-major) order in memory. The default
891 is 'None', which provides no ordering guarantees.
892 Cannot be specified in conjunction with the `out`
893 argument.
895 out : ndarray, 2-D, optional
896 If specified, uses this array (or `numpy.matrix`) as the
897 output buffer instead of allocating a new array to
898 return. The provided array must have the same shape and
899 dtype as the sparse array on which you are calling the
900 method.
902 Returns
903 -------
904 arr : numpy.matrix, 2-D
905 A NumPy matrix object with the same shape and containing
906 the same data represented by the sparse array, with the
907 requested memory order. If `out` was passed and was an
908 array (rather than a `numpy.matrix`), it will be filled
909 with the appropriate values and returned wrapped in a
910 `numpy.matrix` object that shares the same memory.
911 """
912 return self._ascontainer(self.toarray(order=order, out=out))
914 def toarray(self, order=None, out=None):
915 """
916 Return a dense ndarray representation of this sparse array.
918 Parameters
919 ----------
920 order : {'C', 'F'}, optional
921 Whether to store multidimensional data in C (row-major)
922 or Fortran (column-major) order in memory. The default
923 is 'None', which provides no ordering guarantees.
924 Cannot be specified in conjunction with the `out`
925 argument.
927 out : ndarray, 2-D, optional
928 If specified, uses this array as the output buffer
929 instead of allocating a new array to return. The provided
930 array must have the same shape and dtype as the sparse
931 array on which you are calling the method. For most
932 sparse types, `out` is required to be memory contiguous
933 (either C or Fortran ordered).
935 Returns
936 -------
937 arr : ndarray, 2-D
938 An array with the same shape and containing the same
939 data represented by the sparse array, with the requested
940 memory order. If `out` was passed, the same object is
941 returned after being modified in-place to contain the
942 appropriate values.
943 """
944 return self.tocoo(copy=False).toarray(order=order, out=out)
946 # Any sparse array format deriving from _spbase must define one of
947 # tocsr or tocoo. The other conversion methods may be implemented for
948 # efficiency, but are not required.
949 def tocsr(self, copy=False):
950 """Convert this array to Compressed Sparse Row format.
952 With copy=False, the data/indices may be shared between this array and
953 the resultant csr_array.
954 """
955 return self.tocoo(copy=copy).tocsr(copy=False)
957 def todok(self, copy=False):
958 """Convert this array to Dictionary Of Keys format.
960 With copy=False, the data/indices may be shared between this array and
961 the resultant dok_array.
962 """
963 return self.tocoo(copy=copy).todok(copy=False)
965 def tocoo(self, copy=False):
966 """Convert this array to COOrdinate format.
968 With copy=False, the data/indices may be shared between this array and
969 the resultant coo_array.
970 """
971 return self.tocsr(copy=False).tocoo(copy=copy)
973 def tolil(self, copy=False):
974 """Convert this array to List of Lists format.
976 With copy=False, the data/indices may be shared between this array and
977 the resultant lil_array.
978 """
979 return self.tocsr(copy=False).tolil(copy=copy)
981 def todia(self, copy=False):
982 """Convert this array to sparse DIAgonal format.
984 With copy=False, the data/indices may be shared between this array and
985 the resultant dia_array.
986 """
987 return self.tocoo(copy=copy).todia(copy=False)
989 def tobsr(self, blocksize=None, copy=False):
990 """Convert this array to Block Sparse Row format.
992 With copy=False, the data/indices may be shared between this array and
993 the resultant bsr_array.
995 When blocksize=(R, C) is provided, it will be used for construction of
996 the bsr_array.
997 """
998 return self.tocsr(copy=False).tobsr(blocksize=blocksize, copy=copy)
1000 def tocsc(self, copy=False):
1001 """Convert this array to Compressed Sparse Column format.
1003 With copy=False, the data/indices may be shared between this array and
1004 the resultant csc_array.
1005 """
1006 return self.tocsr(copy=copy).tocsc(copy=False)
1008 def copy(self):
1009 """Returns a copy of this array.
1011 No data/indices will be shared between the returned value and current
1012 array.
1013 """
1014 return self.__class__(self, copy=True)
1016 def sum(self, axis=None, dtype=None, out=None):
1017 """
1018 Sum the array elements over a given axis.
1020 Parameters
1021 ----------
1022 axis : {-2, -1, 0, 1, None} optional
1023 Axis along which the sum is computed. The default is to
1024 compute the sum of all the array elements, returning a scalar
1025 (i.e., `axis` = `None`).
1026 dtype : dtype, optional
1027 The type of the returned array and of the accumulator in which
1028 the elements are summed. The dtype of `a` is used by default
1029 unless `a` has an integer dtype of less precision than the default
1030 platform integer. In that case, if `a` is signed then the platform
1031 integer is used while if `a` is unsigned then an unsigned integer
1032 of the same precision as the platform integer is used.
1034 .. versionadded:: 0.18.0
1036 out : np.matrix, optional
1037 Alternative output matrix in which to place the result. It must
1038 have the same shape as the expected output, but the type of the
1039 output values will be cast if necessary.
1041 .. versionadded:: 0.18.0
1043 Returns
1044 -------
1045 sum_along_axis : np.matrix
1046 A matrix with the same shape as `self`, with the specified
1047 axis removed.
1049 See Also
1050 --------
1051 numpy.matrix.sum : NumPy's implementation of 'sum' for matrices
1053 """
1054 validateaxis(axis)
1056 # We use multiplication by a matrix of ones to achieve this.
1057 # For some sparse array formats more efficient methods are
1058 # possible -- these should override this function.
1059 m, n = self.shape
1061 # Mimic numpy's casting.
1062 res_dtype = get_sum_dtype(self.dtype)
1064 if axis is None:
1065 # sum over rows and columns
1066 return (
1067 self @ self._ascontainer(np.ones((n, 1), dtype=res_dtype))
1068 ).sum(dtype=dtype, out=out)
1070 if axis < 0:
1071 axis += 2
1073 # axis = 0 or 1 now
1074 if axis == 0:
1075 # sum over columns
1076 ret = self._ascontainer(
1077 np.ones((1, m), dtype=res_dtype)
1078 ) @ self
1079 else:
1080 # sum over rows
1081 ret = self @ self._ascontainer(
1082 np.ones((n, 1), dtype=res_dtype)
1083 )
1085 if out is not None and out.shape != ret.shape:
1086 raise ValueError("dimensions do not match")
1088 return ret.sum(axis=axis, dtype=dtype, out=out)
1090 def mean(self, axis=None, dtype=None, out=None):
1091 """
1092 Compute the arithmetic mean along the specified axis.
1094 Returns the average of the array elements. The average is taken
1095 over all elements in the array by default, otherwise over the
1096 specified axis. `float64` intermediate and return values are used
1097 for integer inputs.
1099 Parameters
1100 ----------
1101 axis : {-2, -1, 0, 1, None} optional
1102 Axis along which the mean is computed. The default is to compute
1103 the mean of all elements in the array (i.e., `axis` = `None`).
1104 dtype : data-type, optional
1105 Type to use in computing the mean. For integer inputs, the default
1106 is `float64`; for floating point inputs, it is the same as the
1107 input dtype.
1109 .. versionadded:: 0.18.0
1111 out : np.matrix, optional
1112 Alternative output matrix in which to place the result. It must
1113 have the same shape as the expected output, but the type of the
1114 output values will be cast if necessary.
1116 .. versionadded:: 0.18.0
1118 Returns
1119 -------
1120 m : np.matrix
1122 See Also
1123 --------
1124 numpy.matrix.mean : NumPy's implementation of 'mean' for matrices
1126 """
1127 def _is_integral(dtype):
1128 return (np.issubdtype(dtype, np.integer) or
1129 np.issubdtype(dtype, np.bool_))
1131 validateaxis(axis)
1133 res_dtype = self.dtype.type
1134 integral = _is_integral(self.dtype)
1136 # output dtype
1137 if dtype is None:
1138 if integral:
1139 res_dtype = np.float64
1140 else:
1141 res_dtype = np.dtype(dtype).type
1143 # intermediate dtype for summation
1144 inter_dtype = np.float64 if integral else res_dtype
1145 inter_self = self.astype(inter_dtype)
1147 if axis is None:
1148 return (inter_self / np.array(
1149 self.shape[0] * self.shape[1]))\
1150 .sum(dtype=res_dtype, out=out)
1152 if axis < 0:
1153 axis += 2
1155 # axis = 0 or 1 now
1156 if axis == 0:
1157 return (inter_self * (1.0 / self.shape[0])).sum(
1158 axis=0, dtype=res_dtype, out=out)
1159 else:
1160 return (inter_self * (1.0 / self.shape[1])).sum(
1161 axis=1, dtype=res_dtype, out=out)
1163 def diagonal(self, k=0):
1164 """Returns the kth diagonal of the array.
1166 Parameters
1167 ----------
1168 k : int, optional
1169 Which diagonal to get, corresponding to elements a[i, i+k].
1170 Default: 0 (the main diagonal).
1172 .. versionadded:: 1.0
1174 See also
1175 --------
1176 numpy.diagonal : Equivalent numpy function.
1178 Examples
1179 --------
1180 >>> from scipy.sparse import csr_array
1181 >>> A = csr_array([[1, 2, 0], [0, 0, 3], [4, 0, 5]])
1182 >>> A.diagonal()
1183 array([1, 0, 5])
1184 >>> A.diagonal(k=1)
1185 array([2, 3])
1186 """
1187 return self.tocsr().diagonal(k=k)
1189 def trace(self, offset=0):
1190 """Returns the sum along diagonals of the sparse array.
1192 Parameters
1193 ----------
1194 offset : int, optional
1195 Which diagonal to get, corresponding to elements a[i, i+offset].
1196 Default: 0 (the main diagonal).
1198 """
1199 return self.diagonal(k=offset).sum()
1201 def setdiag(self, values, k=0):
1202 """
1203 Set diagonal or off-diagonal elements of the array.
1205 Parameters
1206 ----------
1207 values : array_like
1208 New values of the diagonal elements.
1210 Values may have any length. If the diagonal is longer than values,
1211 then the remaining diagonal entries will not be set. If values are
1212 longer than the diagonal, then the remaining values are ignored.
1214 If a scalar value is given, all of the diagonal is set to it.
1216 k : int, optional
1217 Which off-diagonal to set, corresponding to elements a[i,i+k].
1218 Default: 0 (the main diagonal).
1220 """
1221 M, N = self.shape
1222 if (k > 0 and k >= N) or (k < 0 and -k >= M):
1223 raise ValueError("k exceeds array dimensions")
1224 self._setdiag(np.asarray(values), k)
1226 def _setdiag(self, values, k):
1227 """This part of the implementation gets overridden by the
1228 different formats.
1229 """
1230 M, N = self.shape
1231 if k < 0:
1232 if values.ndim == 0:
1233 # broadcast
1234 max_index = min(M+k, N)
1235 for i in range(max_index):
1236 self[i - k, i] = values
1237 else:
1238 max_index = min(M+k, N, len(values))
1239 if max_index <= 0:
1240 return
1241 for i, v in enumerate(values[:max_index]):
1242 self[i - k, i] = v
1243 else:
1244 if values.ndim == 0:
1245 # broadcast
1246 max_index = min(M, N-k)
1247 for i in range(max_index):
1248 self[i, i + k] = values
1249 else:
1250 max_index = min(M, N-k, len(values))
1251 if max_index <= 0:
1252 return
1253 for i, v in enumerate(values[:max_index]):
1254 self[i, i + k] = v
1256 def _process_toarray_args(self, order, out):
1257 if out is not None:
1258 if order is not None:
1259 raise ValueError('order cannot be specified if out '
1260 'is not None')
1261 if out.shape != self.shape or out.dtype != self.dtype:
1262 raise ValueError('out array must be same dtype and shape as '
1263 'sparse array')
1264 out[...] = 0.
1265 return out
1266 else:
1267 return np.zeros(self.shape, dtype=self.dtype, order=order)
1269 def _get_index_dtype(self, arrays=(), maxval=None, check_contents=False):
1270 """
1271 Determine index dtype for array.
1273 This wraps _sputils.get_index_dtype, providing compatibility for both
1274 array and matrix API sparse matrices. Matrix API sparse matrices would
1275 attempt to downcast the indices - which can be computationally
1276 expensive and undesirable for users. The array API changes this
1277 behaviour.
1279 See discussion: https://github.com/scipy/scipy/issues/16774
1281 The get_index_dtype import is due to implementation details of the test
1282 suite. It allows the decorator ``with_64bit_maxval_limit`` to mock a
1283 lower int32 max value for checks on the matrix API's downcasting
1284 behaviour.
1285 """
1286 from ._sputils import get_index_dtype
1288 # Don't check contents for array API
1289 return get_index_dtype(arrays, maxval, (check_contents and not self._is_array))
1292 ## All methods below are deprecated and should be removed in
1293 ## scipy 1.13.0
1294 ##
1295 ## Also uncomment the definition of shape above.
1297 def get_shape(self):
1298 """Get shape of a sparse array.
1300 .. deprecated:: 1.11.0
1301 This method will be removed in SciPy 1.13.0.
1302 Use `X.shape` instead.
1303 """
1304 msg = (
1305 "`get_shape` is deprecated and will be removed in v1.13.0; "
1306 "use `X.shape` instead."
1307 )
1308 warn(msg, DeprecationWarning, stacklevel=2)
1310 return self._shape
1312 def set_shape(self, shape):
1313 """See `reshape`.
1315 .. deprecated:: 1.11.0
1316 This method will be removed in SciPy 1.13.0.
1317 Use `X.reshape` instead.
1318 """
1319 msg = (
1320 "Shape assignment is deprecated and will be removed in v1.13.0; "
1321 "use `reshape` instead."
1322 )
1323 warn(msg, DeprecationWarning, stacklevel=2)
1325 # Make sure copy is False since this is in place
1326 # Make sure format is unchanged because we are doing a __dict__ swap
1327 new_self = self.reshape(shape, copy=False).asformat(self.format)
1328 self.__dict__ = new_self.__dict__
1330 shape = property(
1331 fget=lambda self: self._shape,
1332 fset=set_shape,
1333 doc="""The shape of the array.
1335Note that, starting in SciPy 1.13.0, this property will no longer be
1336settable. To change the array shape, use `X.reshape` instead.
1337"""
1338 ) # noqa: F811
1340 def asfptype(self):
1341 """Upcast array to a floating point format (if necessary)
1343 .. deprecated:: 1.11.0
1344 This method is for internal use only, and will be removed from the
1345 public API in SciPy 1.13.0.
1346 """
1347 msg = (
1348 "`asfptype` is an internal function, and is deprecated "
1349 "as part of the public API. It will be removed in v1.13.0."
1350 )
1351 warn(msg, DeprecationWarning, stacklevel=2)
1352 return self._asfptype()
1354 def getmaxprint(self):
1355 """Maximum number of elements to display when printed.
1357 .. deprecated:: 1.11.0
1358 This method is for internal use only, and will be removed from the
1359 public API in SciPy 1.13.0.
1360 """
1361 msg = (
1362 "`getmaxprint` is an internal function, and is deprecated "
1363 "as part of the public API. It will be removed in v1.13.0."
1364 )
1365 warn(msg, DeprecationWarning, stacklevel=2)
1366 return self._getmaxprint()
1368 def getformat(self):
1369 """Matrix storage format.
1371 .. deprecated:: 1.11.0
1372 This method will be removed in SciPy 1.13.0.
1373 Use `X.format` instead.
1374 """
1375 msg = (
1376 "`getformat` is deprecated and will be removed in v1.13.0; "
1377 "use `X.format` instead."
1378 )
1379 warn(msg, DeprecationWarning, stacklevel=2)
1380 return self.format
1382 def getnnz(self, axis=None):
1383 """Number of stored values, including explicit zeros.
1385 .. deprecated:: 1.11.0
1386 This method will be removed in SciPy 1.13.0. Use `X.nnz`
1387 instead. The `axis` argument will no longer be supported;
1388 please let us know if you still need this functionality.
1390 Parameters
1391 ----------
1392 axis : None, 0, or 1
1393 Select between the number of values across the whole array, in
1394 each column, or in each row.
1396 See also
1397 --------
1398 count_nonzero : Number of non-zero entries
1399 """
1400 msg = (
1401 "`getnnz` is deprecated and will be removed in v1.13.0; "
1402 "use `X.nnz` instead."
1403 )
1404 warn(msg, DeprecationWarning, stacklevel=2)
1405 return self._getnnz(axis=axis)
1407 def getH(self):
1408 """Return the Hermitian transpose of this array.
1410 .. deprecated:: 1.11.0
1411 This method will be removed in SciPy 1.13.0.
1412 Use `X.conj().T` instead.
1413 """
1414 msg = (
1415 "`getH` is deprecated and will be removed in v1.13.0; "
1416 "use `X.conj().T` instead."
1417 )
1418 warn(msg, DeprecationWarning, stacklevel=2)
1419 return self.conjugate().transpose()
1421 def getcol(self, j):
1422 """Returns a copy of column j of the array, as an (m x 1) sparse
1423 array (column vector).
1425 .. deprecated:: 1.11.0
1426 This method will be removed in SciPy 1.13.0.
1427 Use array indexing instead.
1428 """
1429 msg = (
1430 "`getcol` is deprecated and will be removed in v1.13.0; "
1431 f"use `X[:, [{j}]]` instead."
1432 )
1433 warn(msg, DeprecationWarning, stacklevel=2)
1434 return self._getcol(j)
1436 def getrow(self, i):
1437 """Returns a copy of row i of the array, as a (1 x n) sparse
1438 array (row vector).
1440 .. deprecated:: 1.11.0
1441 This method will be removed in SciPy 1.13.0.
1442 Use array indexing instead.
1443 """
1444 msg = (
1445 "`getrow` is deprecated and will be removed in v1.13.0; "
1446 f"use `X[[{i}]]` instead."
1447 )
1448 warn(msg, DeprecationWarning, stacklevel=2)
1449 return self._getrow(i)
1451 ## End 1.13.0 deprecated methods
1454class sparray:
1455 """A namespace class to separate sparray from spmatrix"""
1456 pass
1458sparray.__doc__ = _spbase.__doc__
1461def issparse(x):
1462 """Is `x` of a sparse array type?
1464 Parameters
1465 ----------
1466 x
1467 object to check for being a sparse array
1469 Returns
1470 -------
1471 bool
1472 True if `x` is a sparse array or a sparse matrix, False otherwise
1474 Examples
1475 --------
1476 >>> import numpy as np
1477 >>> from scipy.sparse import csr_array, csr_matrix, issparse
1478 >>> issparse(csr_matrix([[5]]))
1479 True
1480 >>> issparse(csr_array([[5]]))
1481 True
1482 >>> issparse(np.array([[5]]))
1483 False
1484 >>> issparse(5)
1485 False
1486 """
1487 return isinstance(x, _spbase)
1490def isspmatrix(x):
1491 """Is `x` of a sparse matrix type?
1493 Parameters
1494 ----------
1495 x
1496 object to check for being a sparse matrix
1498 Returns
1499 -------
1500 bool
1501 True if `x` is a sparse matrix, False otherwise
1503 Examples
1504 --------
1505 >>> import numpy as np
1506 >>> from scipy.sparse import csr_array, csr_matrix, isspmatrix
1507 >>> isspmatrix(csr_matrix([[5]]))
1508 True
1509 >>> isspmatrix(csr_array([[5]]))
1510 False
1511 >>> isspmatrix(np.array([[5]]))
1512 False
1513 >>> isspmatrix(5)
1514 False
1515 """
1516 return isinstance(x, spmatrix)