Coverage for /pythoncovmergedfiles/medio/medio/usr/local/lib/python3.9/dist-packages/scipy/sparse/_coo.py: 15%
382 statements
« prev ^ index » next coverage.py v7.4.4, created at 2024-03-22 06:44 +0000
« prev ^ index » next coverage.py v7.4.4, created at 2024-03-22 06:44 +0000
1""" A sparse matrix in COOrdinate or 'triplet' format"""
3__docformat__ = "restructuredtext en"
5__all__ = ['coo_array', 'coo_matrix', 'isspmatrix_coo']
7import math
8from warnings import warn
10import numpy as np
12from .._lib._util import copy_if_needed
13from ._matrix import spmatrix
14from ._sparsetools import coo_tocsr, coo_todense, coo_matvec
15from ._base import issparse, SparseEfficiencyWarning, _spbase, sparray
16from ._data import _data_matrix, _minmax_mixin
17from ._sputils import (upcast_char, to_native, isshape, getdtype,
18 getdata, downcast_intp_index, get_index_dtype,
19 check_shape, check_reshape_kwargs)
21import operator
24class _coo_base(_data_matrix, _minmax_mixin):
25 _format = 'coo'
27 def __init__(self, arg1, shape=None, dtype=None, copy=False):
28 _data_matrix.__init__(self)
29 is_array = isinstance(self, sparray)
30 if not copy:
31 copy = copy_if_needed
33 if isinstance(arg1, tuple):
34 if isshape(arg1, allow_1d=is_array):
35 self._shape = check_shape(arg1, allow_1d=is_array)
36 idx_dtype = self._get_index_dtype(maxval=max(self._shape))
37 data_dtype = getdtype(dtype, default=float)
38 self.coords = tuple(np.array([], dtype=idx_dtype)
39 for _ in range(len(self._shape)))
40 self.data = np.array([], dtype=data_dtype)
41 self.has_canonical_format = True
42 else:
43 try:
44 obj, coords = arg1
45 except (TypeError, ValueError) as e:
46 raise TypeError('invalid input format') from e
48 if shape is None:
49 if any(len(idx) == 0 for idx in coords):
50 raise ValueError('cannot infer dimensions from zero '
51 'sized index arrays')
52 shape = tuple(operator.index(np.max(idx)) + 1
53 for idx in coords)
54 self._shape = check_shape(shape, allow_1d=is_array)
56 idx_dtype = self._get_index_dtype(coords,
57 maxval=max(self.shape),
58 check_contents=True)
59 self.coords = tuple(np.array(idx, copy=copy, dtype=idx_dtype)
60 for idx in coords)
61 self.data = getdata(obj, copy=copy, dtype=dtype)
62 self.has_canonical_format = False
63 else:
64 if issparse(arg1):
65 if arg1.format == self.format and copy:
66 self.coords = tuple(idx.copy() for idx in arg1.coords)
67 self.data = arg1.data.copy()
68 self._shape = check_shape(arg1.shape, allow_1d=is_array)
69 self.has_canonical_format = arg1.has_canonical_format
70 else:
71 coo = arg1.tocoo()
72 self.coords = tuple(coo.coords)
73 self.data = coo.data
74 self._shape = check_shape(coo.shape, allow_1d=is_array)
75 self.has_canonical_format = False
76 else:
77 # dense argument
78 M = np.asarray(arg1)
79 if not is_array:
80 M = np.atleast_2d(M)
81 if M.ndim != 2:
82 raise TypeError('expected dimension <= 2 array or matrix')
84 self._shape = check_shape(M.shape, allow_1d=is_array)
85 if shape is not None:
86 if check_shape(shape, allow_1d=is_array) != self._shape:
87 message = f'inconsistent shapes: {shape} != {self._shape}'
88 raise ValueError(message)
89 index_dtype = self._get_index_dtype(maxval=max(self._shape))
90 coords = M.nonzero()
91 self.coords = tuple(idx.astype(index_dtype, copy=False)
92 for idx in coords)
93 self.data = M[coords]
94 self.has_canonical_format = True
96 if dtype is not None:
97 self.data = self.data.astype(dtype, copy=False)
99 self._check()
101 @property
102 def row(self):
103 if self.ndim > 1:
104 return self.coords[-2]
105 result = np.zeros_like(self.col)
106 result.setflags(write=False)
107 return result
110 @row.setter
111 def row(self, new_row):
112 if self.ndim < 2:
113 raise ValueError('cannot set row attribute of a 1-dimensional sparse array')
114 new_row = np.asarray(new_row, dtype=self.coords[-2].dtype)
115 self.coords = self.coords[:-2] + (new_row,) + self.coords[-1:]
117 @property
118 def col(self):
119 return self.coords[-1]
121 @col.setter
122 def col(self, new_col):
123 new_col = np.asarray(new_col, dtype=self.coords[-1].dtype)
124 self.coords = self.coords[:-1] + (new_col,)
126 def reshape(self, *args, **kwargs):
127 is_array = isinstance(self, sparray)
128 shape = check_shape(args, self.shape, allow_1d=is_array)
129 order, copy = check_reshape_kwargs(kwargs)
131 # Return early if reshape is not required
132 if shape == self.shape:
133 if copy:
134 return self.copy()
135 else:
136 return self
138 # When reducing the number of dimensions, we need to be careful about
139 # index overflow. This is why we can't simply call
140 # `np.ravel_multi_index()` followed by `np.unravel_index()` here.
141 flat_coords = _ravel_coords(self.coords, self.shape, order=order)
142 if len(shape) == 2:
143 if order == 'C':
144 new_coords = divmod(flat_coords, shape[1])
145 else:
146 new_coords = divmod(flat_coords, shape[0])[::-1]
147 else:
148 new_coords = np.unravel_index(flat_coords, shape, order=order)
150 # Handle copy here rather than passing on to the constructor so that no
151 # copy will be made of `new_coords` regardless.
152 if copy:
153 new_data = self.data.copy()
154 else:
155 new_data = self.data
157 return self.__class__((new_data, new_coords), shape=shape, copy=False)
159 reshape.__doc__ = _spbase.reshape.__doc__
161 def _getnnz(self, axis=None):
162 if axis is None or (axis == 0 and self.ndim == 1):
163 nnz = len(self.data)
164 if any(len(idx) != nnz for idx in self.coords):
165 raise ValueError('all index and data arrays must have the '
166 'same length')
168 if self.data.ndim != 1 or any(idx.ndim != 1 for idx in self.coords):
169 raise ValueError('row, column, and data arrays must be 1-D')
171 return int(nnz)
173 if axis < 0:
174 axis += self.ndim
175 if axis >= self.ndim:
176 raise ValueError('axis out of bounds')
177 if self.ndim > 2:
178 raise NotImplementedError('per-axis nnz for COO arrays with >2 '
179 'dimensions is not supported')
180 return np.bincount(downcast_intp_index(self.coords[1 - axis]),
181 minlength=self.shape[1 - axis])
183 _getnnz.__doc__ = _spbase._getnnz.__doc__
185 def _check(self):
186 """ Checks data structure for consistency """
187 if self.ndim != len(self.coords):
188 raise ValueError('mismatching number of index arrays for shape; '
189 f'got {len(self.coords)}, expected {self.ndim}')
191 # index arrays should have integer data types
192 for i, idx in enumerate(self.coords):
193 if idx.dtype.kind != 'i':
194 warn(f'index array {i} has non-integer dtype ({idx.dtype.name})',
195 stacklevel=3)
197 idx_dtype = self._get_index_dtype(self.coords, maxval=max(self.shape))
198 self.coords = tuple(np.asarray(idx, dtype=idx_dtype)
199 for idx in self.coords)
200 self.data = to_native(self.data)
202 if self.nnz > 0:
203 for i, idx in enumerate(self.coords):
204 if idx.max() >= self.shape[i]:
205 raise ValueError(f'axis {i} index {idx.max()} exceeds '
206 f'matrix dimension {self.shape[i]}')
207 if idx.min() < 0:
208 raise ValueError(f'negative axis {i} index: {idx.min()}')
210 def transpose(self, axes=None, copy=False):
211 if axes is None:
212 axes = range(self.ndim)[::-1]
213 elif isinstance(self, sparray):
214 if len(axes) != self.ndim:
215 raise ValueError("axes don't match matrix dimensions")
216 if len(set(axes)) != self.ndim:
217 raise ValueError("repeated axis in transpose")
218 elif axes != (1, 0):
219 raise ValueError("Sparse matrices do not support an 'axes' "
220 "parameter because swapping dimensions is the "
221 "only logical permutation.")
223 permuted_shape = tuple(self._shape[i] for i in axes)
224 permuted_coords = tuple(self.coords[i] for i in axes)
225 return self.__class__((self.data, permuted_coords),
226 shape=permuted_shape, copy=copy)
228 transpose.__doc__ = _spbase.transpose.__doc__
230 def resize(self, *shape) -> None:
231 is_array = isinstance(self, sparray)
232 shape = check_shape(shape, allow_1d=is_array)
234 # Check for added dimensions.
235 if len(shape) > self.ndim:
236 flat_coords = _ravel_coords(self.coords, self.shape)
237 max_size = math.prod(shape)
238 self.coords = np.unravel_index(flat_coords[:max_size], shape)
239 self.data = self.data[:max_size]
240 self._shape = shape
241 return
243 # Check for removed dimensions.
244 if len(shape) < self.ndim:
245 tmp_shape = (
246 self._shape[:len(shape) - 1] # Original shape without last axis
247 + (-1,) # Last axis is used to flatten the array
248 + (1,) * (self.ndim - len(shape)) # Pad with ones
249 )
250 tmp = self.reshape(tmp_shape)
251 self.coords = tmp.coords[:len(shape)]
252 self._shape = tmp.shape[:len(shape)]
254 # Handle truncation of existing dimensions.
255 is_truncating = any(old > new for old, new in zip(self.shape, shape))
256 if is_truncating:
257 mask = np.logical_and.reduce([
258 idx < size for idx, size in zip(self.coords, shape)
259 ])
260 if not mask.all():
261 self.coords = tuple(idx[mask] for idx in self.coords)
262 self.data = self.data[mask]
264 self._shape = shape
266 resize.__doc__ = _spbase.resize.__doc__
268 def toarray(self, order=None, out=None):
269 B = self._process_toarray_args(order, out)
270 fortran = int(B.flags.f_contiguous)
271 if not fortran and not B.flags.c_contiguous:
272 raise ValueError("Output array must be C or F contiguous")
273 if self.ndim > 2:
274 raise ValueError("Cannot densify higher-rank sparse array")
275 # This handles both 0D and 1D cases correctly regardless of the
276 # original shape.
277 M, N = self._shape_as_2d
278 coo_todense(M, N, self.nnz, self.row, self.col, self.data,
279 B.ravel('A'), fortran)
280 # Note: reshape() doesn't copy here, but does return a new array (view).
281 return B.reshape(self.shape)
283 toarray.__doc__ = _spbase.toarray.__doc__
285 def tocsc(self, copy=False):
286 """Convert this array/matrix to Compressed Sparse Column format
288 Duplicate entries will be summed together.
290 Examples
291 --------
292 >>> from numpy import array
293 >>> from scipy.sparse import coo_array
294 >>> row = array([0, 0, 1, 3, 1, 0, 0])
295 >>> col = array([0, 2, 1, 3, 1, 0, 0])
296 >>> data = array([1, 1, 1, 1, 1, 1, 1])
297 >>> A = coo_array((data, (row, col)), shape=(4, 4)).tocsc()
298 >>> A.toarray()
299 array([[3, 0, 1, 0],
300 [0, 2, 0, 0],
301 [0, 0, 0, 0],
302 [0, 0, 0, 1]])
304 """
305 if self.ndim != 2:
306 raise ValueError("Cannot convert a 1d sparse array to csc format")
307 if self.nnz == 0:
308 return self._csc_container(self.shape, dtype=self.dtype)
309 else:
310 from ._csc import csc_array
311 indptr, indices, data, shape = self._coo_to_compressed(csc_array._swap)
313 x = self._csc_container((data, indices, indptr), shape=shape)
314 if not self.has_canonical_format:
315 x.sum_duplicates()
316 return x
318 def tocsr(self, copy=False):
319 """Convert this array/matrix to Compressed Sparse Row format
321 Duplicate entries will be summed together.
323 Examples
324 --------
325 >>> from numpy import array
326 >>> from scipy.sparse import coo_array
327 >>> row = array([0, 0, 1, 3, 1, 0, 0])
328 >>> col = array([0, 2, 1, 3, 1, 0, 0])
329 >>> data = array([1, 1, 1, 1, 1, 1, 1])
330 >>> A = coo_array((data, (row, col)), shape=(4, 4)).tocsr()
331 >>> A.toarray()
332 array([[3, 0, 1, 0],
333 [0, 2, 0, 0],
334 [0, 0, 0, 0],
335 [0, 0, 0, 1]])
337 """
338 if self.ndim != 2:
339 raise ValueError("Cannot convert a 1d sparse array to csr format")
340 if self.nnz == 0:
341 return self._csr_container(self.shape, dtype=self.dtype)
342 else:
343 from ._csr import csr_array
344 indptr, indices, data, shape = self._coo_to_compressed(csr_array._swap)
346 x = self._csr_container((data, indices, indptr), shape=self.shape)
347 if not self.has_canonical_format:
348 x.sum_duplicates()
349 return x
351 def _coo_to_compressed(self, swap):
352 """convert (shape, coords, data) to (indptr, indices, data, shape)"""
353 M, N = swap(self.shape)
354 major, minor = swap(self.coords)
355 nnz = len(major)
356 # convert idx_dtype intc to int32 for pythran.
357 # tested in scipy/optimize/tests/test__numdiff.py::test_group_columns
358 idx_dtype = self._get_index_dtype(self.coords, maxval=max(self.nnz, N))
359 major = major.astype(idx_dtype, copy=False)
360 minor = minor.astype(idx_dtype, copy=False)
362 indptr = np.empty(M + 1, dtype=idx_dtype)
363 indices = np.empty_like(minor, dtype=idx_dtype)
364 data = np.empty_like(self.data, dtype=self.dtype)
366 coo_tocsr(M, N, nnz, major, minor, self.data, indptr, indices, data)
367 return indptr, indices, data, self.shape
369 def tocoo(self, copy=False):
370 if copy:
371 return self.copy()
372 else:
373 return self
375 tocoo.__doc__ = _spbase.tocoo.__doc__
377 def todia(self, copy=False):
378 if self.ndim != 2:
379 raise ValueError("Cannot convert a 1d sparse array to dia format")
380 self.sum_duplicates()
381 ks = self.col - self.row # the diagonal for each nonzero
382 diags, diag_idx = np.unique(ks, return_inverse=True)
384 if len(diags) > 100:
385 # probably undesired, should todia() have a maxdiags parameter?
386 warn("Constructing a DIA matrix with %d diagonals "
387 "is inefficient" % len(diags),
388 SparseEfficiencyWarning, stacklevel=2)
390 #initialize and fill in data array
391 if self.data.size == 0:
392 data = np.zeros((0, 0), dtype=self.dtype)
393 else:
394 data = np.zeros((len(diags), self.col.max()+1), dtype=self.dtype)
395 data[diag_idx, self.col] = self.data
397 return self._dia_container((data, diags), shape=self.shape)
399 todia.__doc__ = _spbase.todia.__doc__
401 def todok(self, copy=False):
402 self.sum_duplicates()
403 dok = self._dok_container(self.shape, dtype=self.dtype)
404 # ensure that 1d coordinates are not tuples
405 if self.ndim == 1:
406 coords = self.coords[0]
407 else:
408 coords = zip(*self.coords)
410 dok._dict = dict(zip(coords, self.data))
411 return dok
413 todok.__doc__ = _spbase.todok.__doc__
415 def diagonal(self, k=0):
416 if self.ndim != 2:
417 raise ValueError("diagonal requires two dimensions")
418 rows, cols = self.shape
419 if k <= -rows or k >= cols:
420 return np.empty(0, dtype=self.data.dtype)
421 diag = np.zeros(min(rows + min(k, 0), cols - max(k, 0)),
422 dtype=self.dtype)
423 diag_mask = (self.row + k) == self.col
425 if self.has_canonical_format:
426 row = self.row[diag_mask]
427 data = self.data[diag_mask]
428 else:
429 inds = tuple(idx[diag_mask] for idx in self.coords)
430 (row, _), data = self._sum_duplicates(inds, self.data[diag_mask])
431 diag[row + min(k, 0)] = data
433 return diag
435 diagonal.__doc__ = _data_matrix.diagonal.__doc__
437 def _setdiag(self, values, k):
438 if self.ndim != 2:
439 raise ValueError("setting a diagonal requires two dimensions")
440 M, N = self.shape
441 if values.ndim and not len(values):
442 return
443 idx_dtype = self.row.dtype
445 # Determine which triples to keep and where to put the new ones.
446 full_keep = self.col - self.row != k
447 if k < 0:
448 max_index = min(M+k, N)
449 if values.ndim:
450 max_index = min(max_index, len(values))
451 keep = np.logical_or(full_keep, self.col >= max_index)
452 new_row = np.arange(-k, -k + max_index, dtype=idx_dtype)
453 new_col = np.arange(max_index, dtype=idx_dtype)
454 else:
455 max_index = min(M, N-k)
456 if values.ndim:
457 max_index = min(max_index, len(values))
458 keep = np.logical_or(full_keep, self.row >= max_index)
459 new_row = np.arange(max_index, dtype=idx_dtype)
460 new_col = np.arange(k, k + max_index, dtype=idx_dtype)
462 # Define the array of data consisting of the entries to be added.
463 if values.ndim:
464 new_data = values[:max_index]
465 else:
466 new_data = np.empty(max_index, dtype=self.dtype)
467 new_data[:] = values
469 # Update the internal structure.
470 self.coords = (np.concatenate((self.row[keep], new_row)),
471 np.concatenate((self.col[keep], new_col)))
472 self.data = np.concatenate((self.data[keep], new_data))
473 self.has_canonical_format = False
475 # needed by _data_matrix
476 def _with_data(self, data, copy=True):
477 """Returns a matrix with the same sparsity structure as self,
478 but with different data. By default the index arrays are copied.
479 """
480 if copy:
481 coords = tuple(idx.copy() for idx in self.coords)
482 else:
483 coords = self.coords
484 return self.__class__((data, coords), shape=self.shape, dtype=data.dtype)
486 def sum_duplicates(self) -> None:
487 """Eliminate duplicate entries by adding them together
489 This is an *in place* operation
490 """
491 if self.has_canonical_format:
492 return
493 summed = self._sum_duplicates(self.coords, self.data)
494 self.coords, self.data = summed
495 self.has_canonical_format = True
497 def _sum_duplicates(self, coords, data):
498 # Assumes coords not in canonical format.
499 if len(data) == 0:
500 return coords, data
501 # Sort coords w.r.t. rows, then cols. This corresponds to C-order,
502 # which we rely on for argmin/argmax to return the first index in the
503 # same way that numpy does (in the case of ties).
504 order = np.lexsort(coords[::-1])
505 coords = tuple(idx[order] for idx in coords)
506 data = data[order]
507 unique_mask = np.logical_or.reduce([
508 idx[1:] != idx[:-1] for idx in coords
509 ])
510 unique_mask = np.append(True, unique_mask)
511 coords = tuple(idx[unique_mask] for idx in coords)
512 unique_inds, = np.nonzero(unique_mask)
513 data = np.add.reduceat(data, unique_inds, dtype=self.dtype)
514 return coords, data
516 def eliminate_zeros(self):
517 """Remove zero entries from the array/matrix
519 This is an *in place* operation
520 """
521 mask = self.data != 0
522 self.data = self.data[mask]
523 self.coords = tuple(idx[mask] for idx in self.coords)
525 #######################
526 # Arithmetic handlers #
527 #######################
529 def _add_dense(self, other):
530 if other.shape != self.shape:
531 raise ValueError(f'Incompatible shapes ({self.shape} and {other.shape})')
532 dtype = upcast_char(self.dtype.char, other.dtype.char)
533 result = np.array(other, dtype=dtype, copy=True)
534 fortran = int(result.flags.f_contiguous)
535 M, N = self._shape_as_2d
536 coo_todense(M, N, self.nnz, self.row, self.col, self.data,
537 result.ravel('A'), fortran)
538 return self._container(result, copy=False)
540 def _matmul_vector(self, other):
541 result_shape = self.shape[0] if self.ndim > 1 else 1
542 result = np.zeros(result_shape,
543 dtype=upcast_char(self.dtype.char, other.dtype.char))
545 if self.ndim == 2:
546 col = self.col
547 row = self.row
548 elif self.ndim == 1:
549 col = self.coords[0]
550 row = np.zeros_like(col)
551 else:
552 raise NotImplementedError(
553 f"coo_matvec not implemented for ndim={self.ndim}")
555 coo_matvec(self.nnz, row, col, self.data, other, result)
556 # Array semantics return a scalar here, not a single-element array.
557 if isinstance(self, sparray) and result_shape == 1:
558 return result[0]
559 return result
561 def _matmul_multivector(self, other):
562 result_dtype = upcast_char(self.dtype.char, other.dtype.char)
563 if self.ndim == 2:
564 result_shape = (other.shape[1], self.shape[0])
565 col = self.col
566 row = self.row
567 elif self.ndim == 1:
568 result_shape = (other.shape[1],)
569 col = self.coords[0]
570 row = np.zeros_like(col)
571 else:
572 raise NotImplementedError(
573 f"coo_matvec not implemented for ndim={self.ndim}")
575 result = np.zeros(result_shape, dtype=result_dtype)
576 for i, other_col in enumerate(other.T):
577 coo_matvec(self.nnz, row, col, self.data, other_col, result[i:i + 1])
578 return result.T.view(type=type(other))
581def _ravel_coords(coords, shape, order='C'):
582 """Like np.ravel_multi_index, but avoids some overflow issues."""
583 if len(coords) == 1:
584 return coords[0]
585 # Handle overflow as in https://github.com/scipy/scipy/pull/9132
586 if len(coords) == 2:
587 nrows, ncols = shape
588 row, col = coords
589 if order == 'C':
590 maxval = (ncols * max(0, nrows - 1) + max(0, ncols - 1))
591 idx_dtype = get_index_dtype(maxval=maxval)
592 return np.multiply(ncols, row, dtype=idx_dtype) + col
593 elif order == 'F':
594 maxval = (nrows * max(0, ncols - 1) + max(0, nrows - 1))
595 idx_dtype = get_index_dtype(maxval=maxval)
596 return np.multiply(nrows, col, dtype=idx_dtype) + row
597 else:
598 raise ValueError("'order' must be 'C' or 'F'")
599 return np.ravel_multi_index(coords, shape, order=order)
602def isspmatrix_coo(x):
603 """Is `x` of coo_matrix type?
605 Parameters
606 ----------
607 x
608 object to check for being a coo matrix
610 Returns
611 -------
612 bool
613 True if `x` is a coo matrix, False otherwise
615 Examples
616 --------
617 >>> from scipy.sparse import coo_array, coo_matrix, csr_matrix, isspmatrix_coo
618 >>> isspmatrix_coo(coo_matrix([[5]]))
619 True
620 >>> isspmatrix_coo(coo_array([[5]]))
621 False
622 >>> isspmatrix_coo(csr_matrix([[5]]))
623 False
624 """
625 return isinstance(x, coo_matrix)
628# This namespace class separates array from matrix with isinstance
629class coo_array(_coo_base, sparray):
630 """
631 A sparse array in COOrdinate format.
633 Also known as the 'ijv' or 'triplet' format.
635 This can be instantiated in several ways:
636 coo_array(D)
637 where D is an ndarray
639 coo_array(S)
640 with another sparse array or matrix S (equivalent to S.tocoo())
642 coo_array(shape, [dtype])
643 to construct an empty sparse array with shape `shape`
644 dtype is optional, defaulting to dtype='d'.
646 coo_array((data, coords), [shape])
647 to construct from existing data and index arrays:
648 1. data[:] the entries of the sparse array, in any order
649 2. coords[i][:] the axis-i coordinates of the data entries
651 Where ``A[coords] = data``, and coords is a tuple of index arrays.
652 When shape is not specified, it is inferred from the index arrays.
654 Attributes
655 ----------
656 dtype : dtype
657 Data type of the sparse array
658 shape : tuple of integers
659 Shape of the sparse array
660 ndim : int
661 Number of dimensions of the sparse array
662 nnz
663 size
664 data
665 COO format data array of the sparse array
666 coords
667 COO format tuple of index arrays
668 has_canonical_format : bool
669 Whether the matrix has sorted coordinates and no duplicates
670 format
671 T
673 Notes
674 -----
676 Sparse arrays can be used in arithmetic operations: they support
677 addition, subtraction, multiplication, division, and matrix power.
679 Advantages of the COO format
680 - facilitates fast conversion among sparse formats
681 - permits duplicate entries (see example)
682 - very fast conversion to and from CSR/CSC formats
684 Disadvantages of the COO format
685 - does not directly support:
686 + arithmetic operations
687 + slicing
689 Intended Usage
690 - COO is a fast format for constructing sparse arrays
691 - Once a COO array has been constructed, convert to CSR or
692 CSC format for fast arithmetic and matrix vector operations
693 - By default when converting to CSR or CSC format, duplicate (i,j)
694 entries will be summed together. This facilitates efficient
695 construction of finite element matrices and the like. (see example)
697 Canonical format
698 - Entries and coordinates sorted by row, then column.
699 - There are no duplicate entries (i.e. duplicate (i,j) locations)
700 - Data arrays MAY have explicit zeros.
702 Examples
703 --------
705 >>> # Constructing an empty sparse array
706 >>> import numpy as np
707 >>> from scipy.sparse import coo_array
708 >>> coo_array((3, 4), dtype=np.int8).toarray()
709 array([[0, 0, 0, 0],
710 [0, 0, 0, 0],
711 [0, 0, 0, 0]], dtype=int8)
713 >>> # Constructing a sparse array using ijv format
714 >>> row = np.array([0, 3, 1, 0])
715 >>> col = np.array([0, 3, 1, 2])
716 >>> data = np.array([4, 5, 7, 9])
717 >>> coo_array((data, (row, col)), shape=(4, 4)).toarray()
718 array([[4, 0, 9, 0],
719 [0, 7, 0, 0],
720 [0, 0, 0, 0],
721 [0, 0, 0, 5]])
723 >>> # Constructing a sparse array with duplicate coordinates
724 >>> row = np.array([0, 0, 1, 3, 1, 0, 0])
725 >>> col = np.array([0, 2, 1, 3, 1, 0, 0])
726 >>> data = np.array([1, 1, 1, 1, 1, 1, 1])
727 >>> coo = coo_array((data, (row, col)), shape=(4, 4))
728 >>> # Duplicate coordinates are maintained until implicitly or explicitly summed
729 >>> np.max(coo.data)
730 1
731 >>> coo.toarray()
732 array([[3, 0, 1, 0],
733 [0, 2, 0, 0],
734 [0, 0, 0, 0],
735 [0, 0, 0, 1]])
737 """
740class coo_matrix(spmatrix, _coo_base):
741 """
742 A sparse matrix in COOrdinate format.
744 Also known as the 'ijv' or 'triplet' format.
746 This can be instantiated in several ways:
747 coo_matrix(D)
748 where D is a 2-D ndarray
750 coo_matrix(S)
751 with another sparse array or matrix S (equivalent to S.tocoo())
753 coo_matrix((M, N), [dtype])
754 to construct an empty matrix with shape (M, N)
755 dtype is optional, defaulting to dtype='d'.
757 coo_matrix((data, (i, j)), [shape=(M, N)])
758 to construct from three arrays:
759 1. data[:] the entries of the matrix, in any order
760 2. i[:] the row indices of the matrix entries
761 3. j[:] the column indices of the matrix entries
763 Where ``A[i[k], j[k]] = data[k]``. When shape is not
764 specified, it is inferred from the index arrays
766 Attributes
767 ----------
768 dtype : dtype
769 Data type of the matrix
770 shape : 2-tuple
771 Shape of the matrix
772 ndim : int
773 Number of dimensions (this is always 2)
774 nnz
775 size
776 data
777 COO format data array of the matrix
778 row
779 COO format row index array of the matrix
780 col
781 COO format column index array of the matrix
782 has_canonical_format : bool
783 Whether the matrix has sorted indices and no duplicates
784 format
785 T
787 Notes
788 -----
790 Sparse matrices can be used in arithmetic operations: they support
791 addition, subtraction, multiplication, division, and matrix power.
793 Advantages of the COO format
794 - facilitates fast conversion among sparse formats
795 - permits duplicate entries (see example)
796 - very fast conversion to and from CSR/CSC formats
798 Disadvantages of the COO format
799 - does not directly support:
800 + arithmetic operations
801 + slicing
803 Intended Usage
804 - COO is a fast format for constructing sparse matrices
805 - Once a COO matrix has been constructed, convert to CSR or
806 CSC format for fast arithmetic and matrix vector operations
807 - By default when converting to CSR or CSC format, duplicate (i,j)
808 entries will be summed together. This facilitates efficient
809 construction of finite element matrices and the like. (see example)
811 Canonical format
812 - Entries and coordinates sorted by row, then column.
813 - There are no duplicate entries (i.e. duplicate (i,j) locations)
814 - Data arrays MAY have explicit zeros.
816 Examples
817 --------
819 >>> # Constructing an empty matrix
820 >>> import numpy as np
821 >>> from scipy.sparse import coo_matrix
822 >>> coo_matrix((3, 4), dtype=np.int8).toarray()
823 array([[0, 0, 0, 0],
824 [0, 0, 0, 0],
825 [0, 0, 0, 0]], dtype=int8)
827 >>> # Constructing a matrix using ijv format
828 >>> row = np.array([0, 3, 1, 0])
829 >>> col = np.array([0, 3, 1, 2])
830 >>> data = np.array([4, 5, 7, 9])
831 >>> coo_matrix((data, (row, col)), shape=(4, 4)).toarray()
832 array([[4, 0, 9, 0],
833 [0, 7, 0, 0],
834 [0, 0, 0, 0],
835 [0, 0, 0, 5]])
837 >>> # Constructing a matrix with duplicate coordinates
838 >>> row = np.array([0, 0, 1, 3, 1, 0, 0])
839 >>> col = np.array([0, 2, 1, 3, 1, 0, 0])
840 >>> data = np.array([1, 1, 1, 1, 1, 1, 1])
841 >>> coo = coo_matrix((data, (row, col)), shape=(4, 4))
842 >>> # Duplicate coordinates are maintained until implicitly or explicitly summed
843 >>> np.max(coo.data)
844 1
845 >>> coo.toarray()
846 array([[3, 0, 1, 0],
847 [0, 2, 0, 0],
848 [0, 0, 0, 0],
849 [0, 0, 0, 1]])
851 """
853 def __setstate__(self, state):
854 if 'coords' not in state:
855 # For retro-compatibility with the previous attributes
856 # storing nnz coordinates for 2D COO matrix.
857 state['coords'] = (state.pop('row'), state.pop('col'))
858 self.__dict__.update(state)