Coverage for scipy\sparse\_index.py: 0%

# Coverage for scipy\sparse\_index.py : 0%

## 217 statements 0 run 217 missing 0 excluded

Hot-keys on this page

r m x p toggle line displays

j k next/prev highlighted chunk

0 (zero) top of page

1 (one) first highlighted chunk

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 | """Indexing mixin for sparse matrix classes. """ from __future__ import division, print_function, absolute_import import numpy as np from .sputils import isintlike try: INT_TYPES = (int, long, np.integer) except NameError: # long is not defined in Python3 INT_TYPES = (int, np.integer) class IndexMixin(object): """ This class provides common dispatching and validation logic for indexing. """ def __getitem__(self, key): row, col = self._validate_indices(key) # Dispatch to specialized methods. if isinstance(row, INT_TYPES): if isinstance(col, INT_TYPES): return self._get_intXint(row, col) elif isinstance(col, slice): return self._get_intXslice(row, col) elif col.ndim == 1: return self._get_intXarray(row, col) raise IndexError('index results in >2 dimensions') elif isinstance(row, slice): if isinstance(col, INT_TYPES): return self._get_sliceXint(row, col) elif isinstance(col, slice): if row == slice(None) and row == col: return self.copy() return self._get_sliceXslice(row, col) elif col.ndim == 1: return self._get_sliceXarray(row, col) raise IndexError('index results in >2 dimensions') elif row.ndim == 1: if isinstance(col, INT_TYPES): return self._get_arrayXint(row, col) elif isinstance(col, slice): return self._get_arrayXslice(row, col) else: # row.ndim == 2 if isinstance(col, INT_TYPES): return self._get_arrayXint(row, col) elif isinstance(col, slice): raise IndexError('index results in >2 dimensions') elif row.shape[1] == 1 and col.ndim == 1: # special case for outer indexing return self._get_columnXarray(row[:,0], col) # The only remaining case is inner (fancy) indexing row, col = np.broadcast_arrays(row, col) if row.shape != col.shape: raise IndexError('number of row and column indices differ') if row.size == 0: return self.__class__(np.atleast_2d(row).shape, dtype=self.dtype) return self._get_arrayXarray(row, col) def __setitem__(self, key, x): row, col = self._validate_indices(key) if isinstance(row, INT_TYPES) and isinstance(col, INT_TYPES): x = np.asarray(x, dtype=self.dtype) if x.size != 1: raise ValueError('Trying to assign a sequence to an item') self._set_intXint(row, col, x.flat[0]) return if isinstance(row, slice): row = np.arange(*row.indices(self.shape[0]))[:, None] else: row = np.atleast_1d(row) if isinstance(col, slice): col = np.arange(*col.indices(self.shape[1]))[None, :] if row.ndim == 1: row = row[:, None] else: col = np.atleast_1d(col) i, j = np.broadcast_arrays(row, col) if i.shape != j.shape: raise IndexError('number of row and column indices differ') from .base import isspmatrix if isspmatrix(x): if i.ndim == 1: # Inner indexing, so treat them like row vectors. i = i[None] j = j[None] broadcast_row = x.shape[0] == 1 and i.shape[0] != 1 broadcast_col = x.shape[1] == 1 and i.shape[1] != 1 if not ((broadcast_row or x.shape[0] == i.shape[0]) and (broadcast_col or x.shape[1] == i.shape[1])): raise ValueError('shape mismatch in assignment') if x.size == 0: return x = x.tocoo(copy=True) x.sum_duplicates() self._set_arrayXarray_sparse(i, j, x) else: # Make x and i into the same shape x = np.asarray(x, dtype=self.dtype) x, _ = np.broadcast_arrays(x, i) if x.shape != i.shape: raise ValueError("shape mismatch in assignment") if x.size == 0: return self._set_arrayXarray(i, j, x) def _validate_indices(self, key): M, N = self.shape row, col = _unpack_index(key) if isintlike(row): row = int(row) if row < -M or row >= M: raise IndexError('row index (%d) out of range' % row) if row < 0: row += M elif not isinstance(row, slice): row = self._asindices(row, M) if isintlike(col): col = int(col) if col < -N or col >= N: raise IndexError('column index (%d) out of range' % col) if col < 0: col += N elif not isinstance(col, slice): col = self._asindices(col, N) return row, col def _asindices(self, idx, length): """Convert `idx` to a valid index for an axis with a given length. Subclasses that need special validation can override this method. """ try: x = np.asarray(idx) except (ValueError, TypeError, MemoryError): raise IndexError('invalid index') if x.ndim not in (1, 2): raise IndexError('Index dimension must be <= 2') if x.size == 0: return x # Check bounds max_indx = x.max() if max_indx >= length: raise IndexError('index (%d) out of range' % max_indx) min_indx = x.min() if min_indx < 0: if min_indx < -length: raise IndexError('index (%d) out of range' % min_indx) if x is idx or not x.flags.owndata: x = x.copy() x[x < 0] += length return x def getrow(self, i): """Return a copy of row i of the matrix, as a (1 x n) row vector. """ M, N = self.shape i = int(i) if i < -M or i >= M: raise IndexError('index (%d) out of range' % i) if i < 0: i += M return self._get_intXslice(i, slice(None)) def getcol(self, i): """Return a copy of column i of the matrix, as a (m x 1) column vector. """ M, N = self.shape i = int(i) if i < -N or i >= N: raise IndexError('index (%d) out of range' % i) if i < 0: i += N return self._get_sliceXint(slice(None), i) def _get_intXint(self, row, col): raise NotImplementedError() def _get_intXarray(self, row, col): raise NotImplementedError() def _get_intXslice(self, row, col): raise NotImplementedError() def _get_sliceXint(self, row, col): raise NotImplementedError() def _get_sliceXslice(self, row, col): raise NotImplementedError() def _get_sliceXarray(self, row, col): raise NotImplementedError() def _get_arrayXint(self, row, col): raise NotImplementedError() def _get_arrayXslice(self, row, col): raise NotImplementedError() def _get_columnXarray(self, row, col): raise NotImplementedError() def _get_arrayXarray(self, row, col): raise NotImplementedError() def _set_intXint(self, row, col, x): raise NotImplementedError() def _set_arrayXarray(self, row, col, x): raise NotImplementedError() def _set_arrayXarray_sparse(self, row, col, x): # Fall back to densifying x x = np.asarray(x.toarray(), dtype=self.dtype) x, _ = np.broadcast_arrays(x, row) self._set_arrayXarray(row, col, x) def _unpack_index(index): """ Parse index. Always return a tuple of the form (row, col). Valid type for row/col is integer, slice, or array of integers. """ # First, check if indexing with single boolean matrix. from .base import spmatrix, isspmatrix if (isinstance(index, (spmatrix, np.ndarray)) and index.ndim == 2 and index.dtype.kind == 'b'): return index.nonzero() # Parse any ellipses. index = _check_ellipsis(index) # Next, parse the tuple or object if isinstance(index, tuple): if len(index) == 2: row, col = index elif len(index) == 1: row, col = index[0], slice(None) else: raise IndexError('invalid number of indices') else: row, col = index, slice(None) # Next, check for validity and transform the index as needed. if isspmatrix(row) or isspmatrix(col): # Supporting sparse boolean indexing with both row and col does # not work because spmatrix.ndim is always 2. raise IndexError( 'Indexing with sparse matrices is not supported ' 'except boolean indexing where matrix and index ' 'are equal shapes.') if isinstance(row, np.ndarray) and row.dtype.kind == 'b': row = _boolean_index_to_array(row) if isinstance(col, np.ndarray) and col.dtype.kind == 'b': col = _boolean_index_to_array(col) return row, col def _check_ellipsis(index): """Process indices with Ellipsis. Returns modified index.""" if index is Ellipsis: return (slice(None), slice(None)) if not isinstance(index, tuple): return index # TODO: Deprecate this multiple-ellipsis handling, # as numpy no longer supports it. # Find first ellipsis. for j, v in enumerate(index): if v is Ellipsis: first_ellipsis = j break else: return index # Try to expand it using shortcuts for common cases if len(index) == 1: return (slice(None), slice(None)) if len(index) == 2: if first_ellipsis == 0: if index[1] is Ellipsis: return (slice(None), slice(None)) return (slice(None), index[1]) return (index[0], slice(None)) # Expand it using a general-purpose algorithm tail = [] for v in index[first_ellipsis+1:]: if v is not Ellipsis: tail.append(v) nd = first_ellipsis + len(tail) nslice = max(0, 2 - nd) return index[:first_ellipsis] + (slice(None),)*nslice + tuple(tail) def _boolean_index_to_array(idx): if idx.ndim > 1: raise IndexError('invalid index shape') return idx.nonzero()[0]