Skip to content

Slicing and Indexing

Accessing elements and submatrices in sparse matrices.

Mental Model

Slicing a sparse matrix is format-dependent: CSR makes row slicing fast because rows are stored contiguously, while CSC makes column slicing fast for the same reason. Single-element access is slow in any format because it requires a search through compressed indices. If you need to read many individual elements, convert to dense first or restructure your algorithm to use row/column slices.

Element Access

1. Single Element

```python from scipy import sparse

def main(): A = sparse.csr_matrix([[1, 0, 2], [0, 3, 0], [4, 0, 5]])

print(f"A[0, 0] = {A[0, 0]}")
print(f"A[0, 2] = {A[0, 2]}")
print(f"A[1, 0] = {A[1, 0]}")  # Zero element

if name == "main": main() ```

Row and Column Slicing

1. Row Slicing (CSR efficient)

```python from scipy import sparse

def main(): A = sparse.csr_matrix([[1, 0, 2], [0, 3, 0], [4, 0, 5]])

row_0 = A[0, :]
rows_0_1 = A[0:2, :]

print("Row 0:")
print(row_0.toarray())
print()
print("Rows 0-1:")
print(rows_0_1.toarray())

if name == "main": main() ```

2. Column Slicing (CSC efficient)

```python from scipy import sparse

def main(): A = sparse.csc_matrix([[1, 0, 2], [0, 3, 0], [4, 0, 5]])

col_0 = A[:, 0]
cols_0_1 = A[:, 0:2]

print("Column 0:")
print(col_0.toarray())

if name == "main": main() ```

Fancy Indexing

1. Row Selection

```python from scipy import sparse import numpy as np

def main(): A = sparse.csr_matrix([[1, 2, 3], [4, 5, 6], [7, 8, 9], [10, 11, 12]])

rows = [0, 2, 3]
B = A[rows, :]

print("Selected rows [0, 2, 3]:")
print(B.toarray())

if name == "main": main() ```

2. Boolean Indexing

```python from scipy import sparse import numpy as np

def main(): A = sparse.csr_matrix([[1, 2], [3, 4], [5, 6]])

mask = np.array([True, False, True])
B = A[mask, :]

print("Boolean selected rows:")
print(B.toarray())

if name == "main": main() ```

Modifying Elements

1. Set Single Element

```python from scipy import sparse

def main(): A = sparse.lil_matrix((3, 3))

A[0, 0] = 1
A[1, 1] = 2
A[2, 2] = 3

print(A.toarray())

if name == "main": main() ```

2. LIL for Modification

```python from scipy import sparse

def main(): # CSR is inefficient for element-by-element modification # Use LIL, then convert

lil = sparse.lil_matrix((5, 5))
for i in range(5):
    lil[i, i] = i + 1

csr = lil.tocsr()
print(csr.toarray())

if name == "main": main() ```

Performance

1. Format Matters

Operation CSR CSC COO
Row slice Fast Slow Slow
Col slice Slow Fast Slow
Element access Medium Medium Slow
Modification Slow Slow Fast

Summary

  • Use CSR for row operations
  • Use CSC for column operations
  • Use LIL for building/modifying
  • Avoid element-by-element access on CSR/CSC

Exercises

Exercise 1. Create a \(10 \times 10\) sparse CSR matrix from the dense matrix where entry \((i, j) = i \cdot 10 + j\) if \(|i - j| \leq 1\), and 0 otherwise. Extract rows 3 through 6 using slicing and print the result as a dense array. Also extract the single element at position \((5, 4)\).

Solution to Exercise 1
import numpy as np
from scipy import sparse

n = 10
dense = np.zeros((n, n))
for i in range(n):
    for j in range(max(0, i - 1), min(n, i + 2)):
        dense[i, j] = i * 10 + j

A = sparse.csr_matrix(dense)

rows_3_6 = A[3:7, :]
print("Rows 3-6:")
print(rows_3_6.toarray())

element = A[5, 4]
print(f"\nA[5, 4] = {element}")

Exercise 2. Build a \(100 \times 100\) sparse random matrix in CSR format (density 0.1, np.random.seed(7)). Use fancy indexing to select rows [0, 10, 20, 50, 99] and print the resulting submatrix shape and number of nonzeros. Then use boolean indexing to select all rows whose row sum exceeds 5.

Solution to Exercise 2
import numpy as np
from scipy import sparse

np.random.seed(7)
A = sparse.random(100, 100, density=0.1, format='csr')

# Fancy indexing
selected_rows = A[[0, 10, 20, 50, 99], :]
print(f"Selected rows shape: {selected_rows.shape}")
print(f"Selected rows nnz: {selected_rows.nnz}")

# Boolean indexing
row_sums = np.array(A.sum(axis=1)).flatten()
mask = row_sums > 5
filtered = A[mask, :]
print(f"\nRows with sum > 5: {mask.sum()}")
print(f"Filtered shape: {filtered.shape}")

Exercise 3. Create a \(50 \times 50\) sparse matrix using LIL format. Set the diagonal entries to their row index (0 through 49), then set entries \((i, i+5)\) to -1 for valid indices. Convert to CSR, extract column 10 using CSC format (convert first), and print the nonzero entries of that column.

Solution to Exercise 3
import numpy as np
from scipy import sparse

n = 50
lil = sparse.lil_matrix((n, n))
for i in range(n):
    lil[i, i] = i
    if i + 5 < n:
        lil[i, i + 5] = -1

csr = lil.tocsr()
csc = csr.tocsc()

col_10 = csc[:, 10]
nz_rows = col_10.nonzero()[0]
print("Column 10 nonzero entries:")
for r in nz_rows:
    print(f"  Row {r}: {col_10[r, 0]}")