SciPy arrays and NumPy interop
How SciPy relates to NumPy, what it adds on top of the array type, and how to keep a large problem sparse instead of dense.
SciPy is NumPy plus algorithms
NumPy gives you the array and the arithmetic; SciPy gives you the algorithms that run on it. Every SciPy routine accepts NumPy arrays and returns NumPy arrays, so the two libraries are one toolkit sharing one data type.
import numpy as np
import scipy.linalg as la
a = np.array([[3.0, 1.0], [1.0, 2.0]])
b = np.array([9.0, 8.0])
x = la.solve(a, b) # SciPy, not numpy.linalg
print(x, x.dtype, x.shape) # [3. 2.] float64 (2,)
print(la.det(a), la.eigvals(a))
print(np.allclose(a @ x, b))- Import the subpackage you need (
scipy.linalg,scipy.optimize) instead of the top-levelscipynamespace. - The NumPy aliases that once lived in
scipy—scipy.zeros,scipy.pi,scipy.randn— were deprecated in SciPy 1.8 and have been removed from recent releases. Take them from NumPy. - Because the return value is a NumPy array,
dtype,shape, broadcasting and fancy indexing all behave exactly as you already expect. - SciPy functions do not modify their inputs unless you opt in with an argument such as
overwrite_a=True.
Sparse matrices: the array you never materialise
import numpy as np
from scipy import sparse
n = 10_000
main = 2.0 * np.ones(n)
off = -1.0 * np.ones(n - 1)
A = sparse.diags([off, main, off], offsets=[-1, 0, 1], format="csc")
print(A.shape, A.nnz) # (10000, 10000) 29998
y = sparse.linalg.spsolve(A, np.ones(n))
print(y[:3]) # a dense vector, because the solution is dense| Concern | Dense (numpy / scipy.linalg) | Sparse (scipy.sparse) |
|---|---|---|
| Storage | Every entry, 8 bytes each | Non-zeros only: data + indices |
| Typical use | Small and medium dense matrices | Grids, graphs, text counts, finite elements |
| Solve | la.solve (LU factorisation) | sparse.linalg.spsolve, cg, gmres |
| Best format | n/a | csc for factorisation, csr for row slicing |
| Failure mode | Memory exhaustion on very large n | A dense intermediate appears silently |
⚠️
Never call
.toarray() on a large sparse matrix just to look at it — that allocates the full dense array and discards the only reason you chose sparse storage. Print .shape and .nnz instead, and keep the whole pipeline sparse end to end. Format conversions (csc to csr) also copy the entire matrix, so pick one format per stage.FAQ
Should I use numpy.linalg or scipy.linalg?
Prefer
scipy.linalg. It wraps LAPACK directly, is generally faster, and exposes decompositions NumPy omits such as lu_factor, schur and expm. NumPy's version exists partly as a fallback that avoids the SciPy dependency.How do I decide between dense and sparse?
Count the non-zeros. If they are a small fraction of the entries and the matrix is more than a few thousand on a side, sparse storage and a sparse solver will be dramatically faster and use far less memory.
Related
NumPy arrays Reshaping, stacking and splitting
Last refreshed 2026-09-18.