SciPy 教程

SciPy 教程

SciPy(Scientific Python)是基于 NumPy 的 Python 科学计算库,提供优化、线性代数、积分、插值、信号处理、统计等高级数学功能,是数据科学和科研计算的核心工具。

SciPy 生态

NumPy      数组与基础数值计算
SciPy      高级科学计算(本教程)
Matplotlib 数据可视化
Pandas     数据处理与分析
SymPy      符号计算
scikit-learn 机器学习

SciPy 模块

模块功能
scipy.constants物理常量
scipy.optimize优化与求根
scipy.sparse稀疏矩阵
scipy.sparse.csgraph图结构算法
scipy.spatial空间数据(KNN、凸包、距离)
scipy.ioMatlab 等格式读写
scipy.interpolate插值
scipy.stats统计分析

快速示例

import numpy as np
from scipy.optimize import minimize
from scipy import stats

# 求函数最小值
result = minimize(lambda x: (x[0]-1)**2 + (x[1]-2)**2,
                  [0, 0])
print(result.x)   # [1. 2.]

# 正态分布
dist = stats.norm(loc=0, scale=1)
print(dist.cdf(1.96))   # 0.975

SciPy 安装

SciPy 安装

1. pip 安装

# 安装
pip install scipy

# 或安装科学计算全家桶
pip install numpy scipy matplotlib pandas

# 指定版本
pip install scipy==1.13.0

2. conda 安装

conda install scipy
# 或
conda install -c conda-forge scipy

3. 验证安装

import scipy
print(scipy.__version__)
# 1.13.1

# 测试常用功能
from scipy import constants
print(constants.pi)          # 3.141592653589793
print(constants.c)           # 光速

# 检查依赖
import numpy
print(numpy.__version__)

4. Jupyter 环境

# Jupyter 中直接使用
# 与 pandas、matplotlib 组合工作

import scipy
import numpy as np
import matplotlib.pyplot as plt

# 交互式绘图
%matplotlib inline

5. 常见问题

# 安装失败
pip install --upgrade pip
pip install numpy --upgrade
pip install scipy

# 版本冲突
# 用虚拟环境或 conda 环境

# 检查构建
python -c "from scipy.optimize import minimize; print('OK')"

SciPy 模块列表

SciPy 模块列表

1. 模块总览

scipy.cluster         聚类(层次聚类、向量量化)
scipy.constants       物理与数学常量
scipy.fft             傅里叶变换
scipy.integrate       积分与常微分方程
scipy.interpolate     插值
scipy.io              输入输出(Matlab等)
scipy.linalg          线性代数
scipy.ndimage         n 维图像处理
scipy.odr             正交距离回归
scipy.optimize        优化与求根
scipy.signal          信号处理
scipy.sparse          稀疏矩阵
scipy.spatial         空间数据结构与算法
scipy.special         特殊函数
scipy.stats           统计

2. 常用模块详解

# 优化
from scipy.optimize import minimize, root, curve_fit

# 线性代数
from scipy.linalg import solve, eig, svd

# 积分
from scipy.integrate import quad, odeint

# 插值
from scipy.interpolate import interp1d, griddata

# 统计
from scipy import stats

# 信号
from scipy.signal import find_peaks, butter, filtfilt

3. 查看模块内容

# 列出模块所有函数
import scipy.optimize as opt
print(dir(opt))

# 查看函数帮助
help(opt.minimize)

# 查看文档
scipy.optimize.minimize?

4. 与 NumPy 分工

# NumPy: 数组、基础数学、随机数
# SciPy: 高级算法(优化/积分/统计/信号)

# 典型工作流
data = np.array(...)          # NumPy 数据
result = scipy.stats.ttest_ind(a, b)  # SciPy 分析
plt.plot(...)                 # Matplotlib 可视化

SciPy 常量模块

SciPy 常量模块

1. 常用常量

from scipy import constants

# 数学常量
constants.pi        # 3.141592653589793
constants.e         # 2.718281828459045
constants.golden    # 黄金比例
constants.inf       # 无穷

# 物理常量
constants.c         # 光速 299792458.0 m/s
constants.g         # 重力加速度 9.80665 m/s²
constants.h         # 普朗克常数
constants.k         # 玻尔兹曼常数
constants.N_A       # 阿伏伽德罗常数
constants.e         # 元电荷

2. 单位转换

# 长度
constants.inch      # 0.0254 米
constants.mile      # 1609.344 米
constants.astronomical_unit  # 天文单位

# 质量
constants.pound     # 0.45359237 千克
constants.ounce     # 0.028349523125 千克

# 时间
constants.minute    # 60 秒
constants.hour      # 3600 秒
constants.day       # 86400 秒
constants.year      # 31556952 秒

# 温度
constants.zero_Celsius  # 273.15 开尔文

# 压力
constants.atm       # 标准大气压(帕斯卡)

3. 前缀单位

# 单位前缀
constants.kilo      # 1e3
constants.mega      # 1e6
constants.giga      # 1e9
constants.milli     # 1e-3
constants.micro     # 1e-6
constants.nano      # 1e-9

# 示例
# 1 兆帕 = 1e6 帕
pressure_pa = 5 * constants.mega

4. 查找常量

# 查找含指定词的常量
constants.find("speed of light")
# ['speed of light', 'speed of light in vacuum']

# 查看物理常量字典
constants.physical_constants["speed of light in vacuum"]
# (299792458.0, 'm s^-1', 0.0)

SciPy 优化器

SciPy 优化器

1. 函数最小值

import numpy as np
from scipy.optimize import minimize

# 目标函数
def f(x):
    return (x[0] - 1)**2 + (x[1] - 2)**2

# 最小化
result = minimize(f, [0, 0])   # 初始点
print(result.x)        # [1. 2.](最优解)
print(result.fun)      # 最小值
print(result.success)  # 是否成功

# 带约束
result = minimize(
    f, [0, 0],
    bounds=[(-5, 5), (-5, 5)]    # 变量范围
)
print(result.x)

2. 约束优化

# 不等式约束 x + y >= 1
cons = [{"type": "ineq",
         "fun": lambda x: x[0] + x[1] - 1}]

result = minimize(f, [0, 0], constraints=cons)
print(result.x)

# 等式约束 x + y = 1
cons = [{"type": "eq",
         "fun": lambda x: x[0] + x[1] - 1}]
result = minimize(f, [0, 1], constraints=cons)

3. 求根(root)

from scipy.optimize import root

# 解方程 f(x) = 0
def func(x):
    return x**2 - 4

sol = root(func, 1.0)     # 从 1 开始找
print(sol.x)              # [2.]

# 多变量方程组
def eqs(vars):
    x, y = vars
    return [x**2 + y**2 - 1, x - y]

sol = root(eqs, [1, 1])
print(sol.x)

4. 曲线拟合

from scipy.optimize import curve_fit

# 生成带噪声数据
x = np.linspace(0, 10, 50)
true_a, true_b = 2.5, 1.3
y_true = true_a * np.exp(-true_b * x)
y = y_true + np.random.normal(0, 0.1, x.size)

# 拟合函数
def model(x, a, b):
    return a * np.exp(-b * x)

popt, pcov = curve_fit(model, x, y)
a, b = popt
print(f"拟合结果: a={a:.2f}, b={b:.2f}")
# 与原参数接近 2.5, 1.3

# 误差
perr = np.sqrt(np.diag(pcov))
print(f"参数误差: {perr}")

5. 全局优化

from scipy.optimize import differential_evolution

# 全局最小值(避免局部最优)
result = differential_evolution(
    lambda x: np.sin(x[0]) * x[0] + x[1]**2,
    bounds=[(-10, 10), (-10, 10)]
)
print(result.x)

SciPy 稀疏矩阵

SciPy 稀疏矩阵

1. 什么是稀疏矩阵

当矩阵中绝大多数元素为 0 时,用稀疏矩阵存储可节省内存并加速运算。

# 稠密矩阵
import numpy as np
dense = np.array([
    [0, 0, 1],
    [2, 0, 0],
    [0, 3, 0]
])

# 稀疏矩阵(只存非零值)
from scipy.sparse import csr_matrix
sparse = csr_matrix(dense)
print(sparse)
#   (0, 2)	1
#   (1, 0)	2
#   (2, 1)	3

2. 创建稀疏矩阵

from scipy.sparse import csr_matrix, csc_matrix, coo_matrix

# 从稠密转换
A = csr_matrix([[1, 0, 0], [0, 2, 0]])

# 从 (行,列,值) 三元组
row = [0, 1, 2]
col = [0, 2, 1]
data = [10, 20, 30]
B = coo_matrix((data, (row, col)), shape=(3, 3))

# 常用格式
csr_matrix    # 行压缩(运算快)
csc_matrix    # 列压缩(列操作快)
coo_matrix    # 坐标格式(便于构建)
dok_matrix    # 字典格式(便于赋值)

3. 常用操作

import numpy as np
from scipy.sparse import csr_matrix

A = csr_matrix([[1, 0, 0], [0, 2, 0], [0, 0, 3]])

# 属性
A.shape          # (3, 3)
A.nnz            # 非零元素数:3
A.data           # 非零值数组

# 运算
A + A            # 加法
A * A            # 矩阵乘法
A.multiply(A)    # 逐元素乘法
A.T              # 转置

# 转换
A.toarray()      # 转稠密
A.todense()      # 转矩阵

# 提取
A[0, 0]          # 1
A.getrow(1)      # 第 1 行

4. 大型稀疏矩阵

# 对角矩阵
from scipy.sparse import diags, eye

D = diags([1, 2, 3], [0], shape=(3, 3))
I = eye(5)                # 单位稀疏矩阵

# 随机稀疏矩阵
from scipy.sparse import random
R = random(1000, 1000, density=0.01)
print(R.nnz)              # 约 10000 个非零

# 内存对比
print(R.nbytes)           # 稀疏存储大小
print(R.toarray().nbytes) # 稠密存储大小

5. 线性方程组求解

from scipy.sparse.linalg import spsolve, eigs

# 稀疏求解 Ax = b
A = csr_matrix([[4, 1], [1, 3]])
b = np.array([1, 2])
x = spsolve(A, b)
print(x)   # [0.09... 0.63...]

# 特征值(前 k 个)
eigenvalues, eigenvectors = eigs(A, k=1)
print(eigenvalues)

SciPy 图结构

SciPy 图结构

scipy.sparse.csgraph 提供图论算法,用于处理稀疏矩阵表示的图:最短路径、连通分量、最小生成树等。

1. 图的表示

import numpy as np
from scipy.sparse import csr_matrix
from scipy.sparse.csgraph import shortest_path

# 邻接矩阵(0 表示无边)
# 节点: A B C D
# 边: A-B(1) B-C(2) A-D(4) C-D(1)
adj = np.array([
    [0, 1, 0, 4],   # A
    [1, 0, 2, 0],   # B
    [0, 2, 0, 1],   # C
    [4, 0, 1, 0]    # D
])
graph = csr_matrix(adj)

2. 最短路径

from scipy.sparse.csgraph import shortest_path, dijkstra, floyd_warshall

# 所有节点间最短路径
dist = shortest_path(graph, method="auto")
print(dist)
# [[0. 1. 3. 4.]
#  [1. 0. 2. 3.]
#  [3. 2. 0. 1.]
#  [4. 3. 1. 0.]]

# Dijkstra 单源
dist = dijkstra(graph, indices=0)
print(dist)   # [0. 1. 3. 4.]

# Floyd-Warshall(所有对)
dist = floyd_warshall(graph)

# 返回路径
dist, pred = dijkstra(graph, indices=0, return_predecessors=True)

3. 连通分量

from scipy.sparse.csgraph import connected_components

# 两个连通部分
adj2 = np.array([
    [0, 1, 0, 0],   # A-B 连通
    [1, 0, 0, 0],
    [0, 0, 0, 1],   # C-D 连通
    [0, 0, 1, 0]
])

n_components, labels = connected_components(
    csr_matrix(adj2), directed=False)
print(n_components)   # 2
print(labels)         # [0 0 1 1]

4. 最小生成树

from scipy.sparse.csgraph import minimum_spanning_tree

mst = minimum_spanning_tree(csr_matrix(adj))
print(mst.toarray())
# 只保留最小生成树的边
# A-B(1) B-C(2) C-D(1)

# 总权重
print(mst.sum())   # 4.0

5. 其他图算法

from scipy.sparse.csgraph import (
    breadth_first_order,   # BFS
    depth_first_order,     # DFS
    breadth_first_tree,    # BFS 树
    depth_first_tree,      # DFS 树
)

# BFS 遍历顺序
order = breadth_first_order(csr_matrix(adj), 0)[0]
print(order)   # [0 1 3 2]

# DFS
order = depth_first_order(csr_matrix(adj), 0)[0]
print(order)

SciPy 空间数据

SciPy 空间数据

scipy.spatial 提供空间数据结构和算法:KD 树(最近邻)、凸包、距离计算、Delaunay 三角剖分等。

1. 距离计算

from scipy.spatial import distance

# 两点间距离
p1, p2 = [0, 0], [3, 4]
distance.euclidean(p1, p2)       # 5.0
distance.manhattan(p1, p2)       # 7.0
distance.chebyshev(p1, p2)       # 4.0
distance.cosine([1, 0], [0, 1])  # 1.0

# 距离矩阵
points = [[0, 0], [1, 1], [3, 4]]
dist_matrix = distance.cdist(points, points)
print(dist_matrix)

2. KD 树(最近邻搜索)

from scipy.spatial import KDTree
import numpy as np

# 数据点
data = np.array([
    [1, 1], [2, 3], [4, 5],
    [5, 2], [3, 4]
])
tree = KDTree(data)

# 查询最近邻
query = [2, 2]
dist, idx = tree.query(query)
print(f"最近点: {data[idx]}, 距离: {dist:.2f}")

# 查询 k 个最近邻
dist, idx = tree.query(query, k=3)
print("最近 3 个:", data[idx])

# 半径搜索
idx = tree.query_ball_point(query, r=2.0)
print("半径内:", data[idx])

# 批量查询
queries = [[0, 0], [5, 5]]
dist, idx = tree.query(queries)
print(idx)

3. 凸包

from scipy.spatial import ConvexHull
import matplotlib.pyplot as plt

# 随机点集
points = np.random.rand(20, 2) * 10

# 计算凸包
hull = ConvexHull(points)

# 顶点
print(hull.vertices)
# 周长与面积
print(f"周长: {hull.area:.2f}")
print(f"面积: {hull.volume:.2f}")

# 可视化
plt.scatter(points[:, 0], points[:, 1])
for simplex in hull.simplices:
    plt.plot(points[simplex, 0], points[simplex, 1], "r-")
plt.show()

4. Delaunay 三角剖分

from scipy.spatial import Delaunay

points = np.array([[0, 0], [1, 0], [0, 1], [1, 1]])
tri = Delaunay(points)
print(tri.simplices)   # 三角形顶点索引

# 判断点是否在三角网内
test = [0.3, 0.3]
in_tri = tri.find_simplex(test) >= 0
print(f"点在三角网内: {in_tri}")

5. 沃罗诺伊图

from scipy.spatial import Voronoi

points = np.random.rand(10, 2) * 10
vor = Voronoi(points)

# 顶点
print(vor.vertices)
# 区域
print(vor.regions)

SciPy Matlab 数组

SciPy Matlab 数组

1. 读取 Matlab 文件

from scipy.io import loadmat

# 读取 .mat 文件
data = loadmat("data.mat")
print(data.keys())        # 变量列表

# 获取变量
x = data["x"]
print(type(x))            # numpy.ndarray
print(x.shape)

2. 写入 Matlab 文件

from scipy.io import savemat
import numpy as np

# 保存多个变量
data = {
    "x": np.array([1, 2, 3]),
    "y": np.random.rand(3, 3),
    "text": "hello"
}
savemat("output.mat", data)

# 读取验证
from scipy.io import loadmat
loaded = loadmat("output.mat")
print(loaded["x"])

3. 其他格式

# scipy.io 支持
loadmat / savemat     Matlab .mat
whosmat               查看 .mat 变量名
mmread / mmwrite      Matrix Market 格式

# 用 whosmat 只读变量名(不加载数据)
from scipy.io import whosmat
info = whosmat("data.mat")
print(info)   # [(name, shape, dtype), ...]

4. 其他文件格式处理

# 科学计算常用格式
import numpy as np

# NPZ(推荐,NumPy 原生)
np.savez("data.npz", x=np.array([1,2,3]),
         y=np.array([[1,2],[3,4]]))
d = np.load("data.npz")
print(d["x"])

# CSV(pandas)
import pandas as pd
df = pd.DataFrame({"a": [1, 2], "b": [3, 4]})
df.to_csv("data.csv", index=False)

# HDF5(大数据,h5py)
# pip install h5py
import h5py
with h5py.File("data.h5", "w") as f:
    f.create_dataset("x", data=[1, 2, 3])

SciPy 插值

SciPy 插值

插值(interpolation)是用已知数据点估计中间未知值的方法,scipy.interpolate 提供多种插值工具。

1. 一维插值

import numpy as np
from scipy.interpolate import interp1d
import matplotlib.pyplot as plt

# 已知数据点
x = np.array([0, 1, 2, 3, 4])
y = np.array([0, 1, 4, 9, 16])

# 线性插值
f_linear = interp1d(x, y, kind="linear")

# 三次样条
f_cubic = interp1d(x, y, kind="cubic")

# 新点求值
x_new = 2.5
print(f"线性: {f_linear(x_new):.2f}")   # 6.5
print(f"三次: {f_cubic(x_new):.2f}")    # 6.56

# 插值方式
kind="linear"   线性
kind="cubic"    三次样条
kind="quadratic" 二次
kind="nearest"  最近邻
kind="zero"     零阶保持

2. 样条插值(样条)

from scipy.interpolate import CubicSpline

cs = CubicSpline(x, y)

# 平滑曲线
x_smooth = np.linspace(0, 4, 100)
y_smooth = cs(x_smooth)

# 导数
print(cs(x_new))          # 值
print(cs(x_new, 1))       # 一阶导
print(cs(x_new, 2))       # 二阶导

plt.plot(x, y, "o", label="数据")
plt.plot(x_smooth, y_smooth, label="三次样条")
plt.legend()
plt.show()

3. 多维插值

from scipy.interpolate import griddata

# 不规则散点数据
points = np.random.rand(50, 2) * 10
values = np.sin(points[:, 0]) + np.cos(points[:, 1])

# 网格插值
grid_x, grid_y = np.mgrid[0:10:50j, 0:10:50j]
z = griddata(points, values,
             (grid_x, grid_y),
             method="cubic")   # linear/cubic/nearest

4. 样条插值对象

from scipy.interpolate import UnivariateSpline

# 带平滑的样条(可处理噪声)
spl = UnivariateSpline(x, y, k=3, s=0.5)
# k: 阶数, s: 平滑因子

# 拟合
print(spl(x_new))

5. 应用场景

  • 数据补全(缺失值插值)
  • 曲线平滑
  • 图像缩放(二维插值)
  • 数值模拟网格生成

Scipy 显著性检验

SciPy 显著性检验

scipy.stats 提供完整的统计检验工具,用于判断数据差异是否显著。

1. t 检验

from scipy import stats
import numpy as np

# 独立样本 t 检验(两组差异)
group_a = np.array([85, 90, 78, 92, 88])
group_b = np.array([70, 75, 72, 68, 74])

t_stat, p_value = stats.ttest_ind(group_a, group_b)
print(f"t={t_stat:.3f}, p={p_value:.4f}")

# p < 0.05 → 差异显著
if p_value < 0.05:
    print("两组差异显著")
else:
    print("差异不显著")

# 配对 t 检验(前后对比)
before = np.array([80, 82, 79, 85, 81])
after  = np.array([90, 88, 85, 92, 89])
t_stat, p = stats.ttest_rel(before, after)

# 单样本 t 检验
t_stat, p = stats.ttest_1samp(group_a, 80)

2. 卡方检验

# 独立性检验(分类变量)
observed = np.array([
    [30, 20],    # 男: 喜欢/不喜欢
    [25, 25]     # 女: 喜欢/不喜欢
])
chi2, p, dof, expected = stats.chi2_contingency(observed)
print(f"chi2={chi2:.3f}, p={p:.4f}")

# 拟合优度
observed = [10, 20, 30]
expected = [20, 20, 20]
chi2, p = stats.chisquare(observed, expected)

3. 方差分析(ANOVA)

# 单因素方差分析(多组比较)
group1 = [85, 88, 90, 82]
group2 = [70, 75, 72, 78]
group3 = [90, 92, 95, 88]

f_stat, p = stats.f_oneway(group1, group2, group3)
print(f"F={f_stat:.3f}, p={p:.4f}")

# p < 0.05 → 至少一组显著不同

4. 非参数检验

# 数据非正态时使用

# Mann-Whitney U(两独立样本)
u_stat, p = stats.mannwhitneyu(group_a, group_b)

# Wilcoxon(配对)
w_stat, p = stats.wilcoxon(before, after)

# Kruskal-Wallis(多组)
h_stat, p = stats.kruskal(group1, group2, group3)

# 符号检验
p = stats.binom_test(8, 10, 0.5)

5. 正态性检验

# Shapiro-Wilk(小样本)
data = np.random.normal(0, 1, 100)
w_stat, p = stats.shapiro(data)
print(f"p={p:.4f}")
# p > 0.05 → 符合正态分布

# D'Agostino K^2
stat, p = stats.normaltest(data)

# Kolmogorov-Smirnov
stat, p = stats.kstest(data, "norm")

# QQ 图可视化
import matplotlib.pyplot as plt
stats.probplot(data, dist="norm", plot=plt)
plt.show()

6. 检验选择指南

# 两独立组,正态 → ttest_ind
# 两配对组,正态 → ttest_rel
# 多组,正态 → f_oneway
# 非正态 → mannwhitneyu / wilcoxon / kruskal
# 分类数据 → chi2_contingency
# 正态性检查 → shapiro / normaltest

SciPy 总结与资源

SciPy 总结与资源

1. 知识回顾

  • 常量:物理常量与单位转换
  • 优化:minimize/root/curve_fit/differential_evolution
  • 稀疏矩阵:csr/csc 存储与求解
  • 图算法:最短路径、连通分量、最小生成树
  • 空间数据:KDTree、凸包、Delaunay、距离
  • 插值:interp1d、CubicSpline、griddata
  • 统计:t 检验、卡方、ANOVA、非参数检验

2. 与其他库配合

# 数据科学工作流
import numpy as np
import pandas as pd
from scipy import stats, optimize
import matplotlib.pyplot as plt

# 1. pandas 读取与清洗
df = pd.read_csv("data.csv")

# 2. NumPy 数值处理
x = df["x"].values

# 3. SciPy 统计分析
t, p = stats.ttest_1samp(x, 0)

# 4. 可视化
plt.hist(x, bins=30)
plt.show()

3. 进阶方向

  • 信号处理:scipy.signal(滤波、频谱)
  • 图像处理:scipy.ndimage(卷积、形态学)
  • 微分方程:scipy.integrate.odeint/solve_ivp
  • 机器学习:scikit-learn(构建于 SciPy 之上)

4. 官方资源

官网:      scipy.org
文档:      docs.scipy.org
教程:      docs.scipy.org/doc/scipy/tutorial
GitHub:    github.com/scipy/scipy
参考指南:  docs.scipy.org/doc/scipy/reference

5. 实践建议

  1. 从 stats 和 optimize 入手,最常用
  2. 大矩阵用稀疏格式
  3. 对照 NumPy 文档区分两者职责
  4. 用官方教程示例动手实验
  5. 阅读 API 参考(参数与返回值)

SciPy 是 Python 科学计算的基石,掌握后能高效解决科研与工程中的数值问题。

第 1 / 12 节