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