SciPy Cheatsheet

Special Functions

Use this SciPy reference while you build software engineering projects, review code for technical interview prep, or polish examples for a software engineer resume.

Core Import

from scipy import special
import numpy as np

# Common direct imports
from scipy.special import (
    # Gamma & related
    gamma, gammaln, gammasgn, loggamma,
    rgamma, multigammaln, gammainccinv,
    gammainc, gammaincc, gammaincinv,
    psi, digamma, polygamma, beta, betaln, betainc, betaincinv,
    # Error functions
    erf, erfc, erfinv, erfcinv, erfi,
    # Bessel functions
    jv, yv, iv, kv, jn, yn,
    j0, j1, y0, y1, i0, i1, k0, k1,
    jvp, yvp, ivp, kvp,
    jn_zeros, jnp_zeros,
    # Airy functions
    airy, airye, ai_zeros, bi_zeros,
    # Orthogonal polynomials
    eval_legendre, eval_chebyt, eval_chebyu,
    eval_hermite, eval_laguerre, eval_genlaguerre,
    # Hypergeometric
    hyp1f1, hyp2f1, hyp0f1,
    # Elliptic integrals
    ellipk, ellipe, ellipkm1, ellipj,
    # Combinatorics
    comb, perm, factorial, factorial2,
    # Information theory
    entr, rel_entr, xlogy, xlog1py,
    # Statistical
    ndtr, ndtri, chdtr, stdtr,
    # Misc
    expit, logit, log1p, expm1,
    sinc, sici, shichi,
    zeta, zetac,
    lambertw,
    # Spherical harmonics
    sph_harm_y,
)

Beta Function

from scipy.special import beta, betaln, betainc, betaincinv

beta(2, 3)         # B(2,3) = Γ(2)Γ(3)/Γ(5) = 1/12
betaln(2, 3)       # log(B(2,3)), more stable

# Regularized incomplete beta I_x(a,b)
betainc(2, 3, 0.5)         # (a, b, x)
betaincinv(2, 3, 0.6875)   # (a, b, p) — inverse

Error Functions

from scipy.special import erf, erfc, erfinv, erfcinv, erfi, erfcx

# erf(x) = (2/sqrt(pi)) * int_0^x exp(-t^2) dt
erf(1.0)      # ≈ 0.8427
erfc(1.0)     # 1 - erf(x) ≈ 0.1573 (more accurate for large x)
erfinv(0.8)   # inverse erf
erfcinv(0.2)  # inverse erfc

# Imaginary error function erf(ix)/i
erfi(1.0)

# Scaled complementary: erfcx(x) = exp(x^2) * erfc(x) (no overflow)
erfcx(10.0)   # avoids overflow where erfc underflows

Bessel Functions

Cylindrical Bessel (General Order)

from scipy.special import jv, yv, iv, kv, jvp, yvp, ivp, kvp

# J_v(z) — Bessel of the first kind; args are (v, z): order, then argument
jv(0, 1.0)
jv(1.5, np.linspace(0, 10, 100))

# Y_v(z) — Bessel of the second kind (Neumann)
yv(0, 2.0)

# I_v(z) — modified Bessel of the first kind
iv(0, 1.0)

# K_v(z) — modified Bessel of the second kind
kv(0, 1.0)

# Derivatives
jvp(0, 1.0, n=1)   # (v, z, n): d/dz J_0(z) at z=1
ivp(0, 1.0, n=2)   # second derivative of I_0

Integer-Order Fast Implementations

from scipy.special import j0, j1, y0, y1, i0, i1, i0e, i1e, k0, k1, k0e, k1e

j0(x), j1(x)     # J_0, J_1 (fast C implementations)
y0(x), y1(x)     # Y_0, Y_1
i0(x), i1(x)     # I_0, I_1
i0e(x), i1e(x)   # exp(-abs(x)) * I_0(x): exponentially scaled (avoids overflow)
k0(x), k1(x)     # K_0, K_1
k0e(x), k1e(x)   # exp(x) * K_0(x): exponentially scaled

Zeros of Bessel Functions

from scipy.special import jn_zeros, jnp_zeros, yn_zeros, ynp_zeros

# First nt zeros of J_n(x)
jn_zeros(0, 5)    # [2.4048, 5.5201, 8.6537, 11.7915, 14.9309]
jn_zeros(1, 3)

# First nt zeros of J_n'(x)
jnp_zeros(0, 5)

Spherical Bessel Functions

from scipy.special import spherical_jn, spherical_yn, spherical_in, spherical_kn

spherical_jn(0, 1.0)
spherical_jn(1, 1.0, derivative=True)  # derivative
spherical_yn(0, 1.0)

Airy Functions

from scipy.special import airy, airye, ai_zeros, bi_zeros

# Ai(z), Ai'(z), Bi(z), Bi'(z)
Ai, Aip, Bi, Bip = airy(1.0)

# Exponentially scaled (avoids overflow for large z)
Ai_e, Aip_e, Bi_e, Bip_e = airye(10.0)

# Zeros
a, ap, ai_ap, aip_a = ai_zeros(5)   # zeros of Ai, zeros of Ai', values Ai(a'), values Ai'(a)

Orthogonal Polynomials

from scipy.special import (
    eval_legendre, eval_chebyt, eval_chebyu,
    eval_hermite, eval_hermitenorm,
    eval_laguerre, eval_genlaguerre,
    eval_jacobi
)

# Legendre P_n(x), x in [-1, 1] — args are (n, x)
eval_legendre(3, 0.5)

# Chebyshev T_n(x) (first kind), U_n(x) (second kind)
eval_chebyt(4, 0.5)
eval_chebyu(4, 0.5)

# Hermite H_n(x) (physicists') and He_n(x) (probabilists')
eval_hermite(3, 1.0)
eval_hermitenorm(3, 1.0)

# Laguerre L_n(x), generalized L_n^alpha(x): (n, alpha, x)
eval_laguerre(3, 1.0)
eval_genlaguerre(3, 0.5, 1.0)

# Jacobi P_n^(alpha,beta)(x): (n, alpha, beta, x)
eval_jacobi(3, 0.5, 0.5, 0.5)

Polynomial Objects (Weights, Roots, etc.)

from scipy.special import roots_legendre, roots_chebyt, roots_hermite, roots_laguerre

# Gauss-Legendre quadrature nodes and weights
x, w = roots_legendre(10)   # on [-1, 1]

# Gauss-Chebyshev
x, w = roots_chebyt(10)

# Gauss-Hermite (physicists')
x, w = roots_hermite(10)

# Gauss-Laguerre
x, w = roots_laguerre(10)   # on [0, ∞)

Hypergeometric Functions

from scipy.special import hyp1f1, hyp2f1, hyp0f1

# Confluent hypergeometric: M(a, b, z) = 1F1(a; b; z) — args (a, b, z)
hyp1f1(1, 2, 1.0)

# Gauss hypergeometric: 2F1(a, b; c; z), |z| < 1 — args (a, b, c, z)
hyp2f1(0.5, 0.5, 1.0, 0.5)

# 0F1(; b; z) — Bessel-related — args (b, z)
hyp0f1(1.0, 1.0)

Elliptic Integrals

from scipy.special import ellipk, ellipe, ellipkm1, ellipj

# Complete elliptic integral of the first kind K(m), second kind E(m)
# Note: argument is m = k^2, not k
K = ellipk(0.5)    # K(k^2 = 0.5)
E = ellipe(0.5)    # E(k^2 = 0.5)

# K(1-m) for near m=1 (avoids cancellation)
K1m = ellipkm1(0.001)   # = ellipk(1 - 0.001) but more accurate

# Jacobi elliptic functions sn(u|m), cn(u|m), dn(u|m), ph — args (u, m)
sn, cn, dn, ph = ellipj(1.0, 0.5)

Combinatorics

from scipy.special import comb, perm, factorial, factorial2

# Binomial coefficient C(N, k)
comb(10, 3)                      # 120.0 (float)
comb(10, 3, exact=True)          # 120 (int, slower for large N)
comb(10, 3, repetition=True)     # with repetition = C(N+k-1, k)

# Permutations P(N, k)
perm(10, 3)                      # 720.0
perm(10, 3, exact=True)          # 720 (int)

# Factorial
factorial(10)                    # 3628800.0
factorial(10, exact=True)        # 3628800 (int)
factorial2(7)                    # 7!! = 7*5*3*1 = 105

Logit, Expit (Sigmoid)

from scipy.special import expit, logit, log_expit

# Sigmoid: expit(x) = 1 / (1 + exp(-x))
expit(0.0)   # 0.5
expit(np.array([-3, 0, 3]))

# Inverse sigmoid: logit(p) = log(p/(1-p))
logit(0.5)   # 0.0
logit(0.9)   # ≈ 2.197

# Numerically stable log(sigmoid): log_expit(x) = log(1/(1+exp(-x)))
log_expit(-100.0)   # no underflow

Information-Theoretic Functions

from scipy.special import entr, rel_entr, xlogy, xlog1py, kl_div

# entr(x) = -x*log(x) (0 if x=0, -inf if x<0)
entr(0.5)        # ≈ 0.347

# rel_entr(x, y) = x*log(x/y) (0 if x=0, inf if y=0 and x!=0)
rel_entr(0.3, 0.7)

# xlogy(x, y) = x*log(y) (0 if x=0, even if y=0)
xlogy(0, 0)   # 0 (not NaN)
xlogy(2, 3)   # 2*log(3)

# kl_div(x, y) = x*log(x/y) - x + y
kl_div(0.3, 0.7)

# KL divergence between two distributions
p = np.array([0.2, 0.5, 0.3])
q = np.array([0.1, 0.6, 0.3])
kl = np.sum(rel_entr(p, q))   # KL(P || Q)

Sinc, Si, Ci, Shi, Chi

from scipy.special import sinc, sici, shichi

# Normalized sinc: sin(pi*x) / (pi*x)
sinc(1.5)

# Sine and cosine integrals: Si(x), Ci(x)
Si, Ci = sici(np.pi)

# Hyperbolic sine and cosine integrals: Shi(x), Chi(x)
Shi, Chi = shichi(1.0)

Spherical Harmonics

from scipy.special import sph_harm_y   # SciPy 1.15+

# Y_n^m(theta, phi): args are (n, m, theta, phi)
# n = degree (l), m = order
# theta = POLAR angle (colatitude) in [0, pi]
# phi   = AZIMUTHAL angle in [0, 2*pi]
Y = sph_harm_y(2, 1, np.pi/4, np.pi/3)
# Returns complex value

# Grid of values
theta = np.linspace(0, np.pi, 50)
phi = np.linspace(0, 2*np.pi, 100)
T, P = np.meshgrid(theta, phi)
Y_grid = sph_harm_y(3, 2, T, P)

Legacy sph_harm (deprecated 1.15, removed 1.17): the old sph_harm(m, n, theta, phi) put the order first and — infamously — used theta as the AZIMUTHAL angle and phi as the polar angle, the reverse of the standard physics convention. sph_harm_y fixes both: degree first, and theta/phi carry their conventional meanings.

Zeta Functions

from scipy.special import zeta, zetac

# Riemann zeta: one-argument zeta(x) = sum_{k=1}^∞ 1/k^x
zeta(2)         # ≈ pi^2/6 = 1.6449...

# Hurwitz zeta: zeta(x, q) = sum_{k=0}^∞ 1/(k+q)^x
zeta(2, 1)      # q=1 reduces to the Riemann zeta

# zetac(x) = zeta(x) - 1 (accurate for large x, where zeta ≈ 1)
zetac(2)

Lambert W Function

from scipy.special import lambertw

# Principal branch W_0: W(z) * exp(W(z)) = z
lambertw(1.0)         # ≈ 0.5671 (Omega constant)
lambertw(0)           # 0
lambertw(-1/np.e)     # -1 (branch point)

# Secondary real branch W_{-1} for z in [-1/e, 0)
lambertw(-0.1, k=-1)  # k=-1 branch

# Complex input
lambertw(-1.0 + 0j)

Numerical Stability Notes

FunctionStable AlternativeWhen to Use
gamma(x)gammaln(x)When Γ(x) would overflow/underflow
erfc(x)erfcx(x) * exp(-x**2)Large x (erfc underflows)
i0(x)i0e(x) * exp(-abs(x))Large x
log(1 + x)log1p(x)Small x near 0
exp(x) - 1expm1(x)Small x near 0
p * log(q)xlogy(p, q)p may be 0
p * log(1 + q)xlog1py(p, q)q near 0
from scipy.special import log1p, expm1

# Precision near x=0
np.log(1 + 1e-15)    # 0.0 (catastrophic cancellation)
log1p(1e-15)          # 9.99e-16 (correct)

np.exp(1e-15) - 1     # 0.0 (precision lost)
expm1(1e-15)          # 1e-15 (correct)

Common Gotchas

Bessel argument convention: jv(v, z) is order then argument. Easy to swap. j0(x) and j1(x) (no v) are always faster for fixed integer orders.

Elliptic integral argument: SciPy uses m = k² (the modulus squared), NOT k. Many textbooks use k. ellipk(0.5) computes K(k) where k² = 0.5, i.e. k ≈ 0.707.

Most scipy.special functions are NumPy ufuncs — they accept positional arguments only. jv(v=0, z=1.0) raises TypeError; write jv(0, 1.0).

Spherical harmonics angle convention: use sph_harm_y(n, m, theta, phi) (SciPy 1.15+) — theta is the polar angle (colatitude, [0, π]), phi the azimuth ([0, 2π]). The removed legacy sph_harm had these two SWAPPED (theta = azimuth), a long-standing trap when porting old code.

comb returns float by default even for integer inputs. Use exact=True for exact integer arithmetic (required for large combinatoric calculations).

gamma overflows for z > 171.6. Use gammaln + work in log space, then np.exp at the end.