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-level scipy namespace.
  • The NumPy aliases that once lived in scipyscipy.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
ConcernDense (numpy / scipy.linalg)Sparse (scipy.sparse)
StorageEvery entry, 8 bytes eachNon-zeros only: data + indices
Typical useSmall and medium dense matricesGrids, graphs, text counts, finite elements
Solvela.solve (LU factorisation)sparse.linalg.spsolve, cg, gmres
Best formatn/acsc for factorisation, csr for row slicing
Failure modeMemory exhaustion on very large nA 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.

NumPy arrays Reshaping, stacking and splitting

Last refreshed 2026-09-18.