SciPy 科学计算库快速入门
·

目录
- 简介
- 安装与配置
- 核心模块概览
- scipy.optimize - 优化与拟合
- scipy.integrate - 积分计算
- scipy.interpolate - 插值
- scipy.linalg - 线性代数
- scipy.stats - 统计
- scipy.signal - 信号处理
- scipy.sparse - 稀疏矩阵
- 综合实例
- 总结与资源
简介
SciPy(Scientific Python)是一个开源的Python库,用于科学和技术计算。它构建在NumPy之上,提供了大量用于数学、科学和工程计算的算法和工具。
SciPy 与 NumPy 的关系
- NumPy: 提供基础的多维数组对象和基本操作
- SciPy: 在NumPy基础上提供更高级的数学算法和工具
主要特点
- 丰富的科学计算功能
- 与NumPy无缝集成
- 高效的C/Fortran底层实现
- 广泛的文档和社区支持
安装与配置
安装方法
# 使用 pip 安装
pip install scipy
# 使用 conda 安装(推荐)
conda install scipy
# 安装完整科学计算环境
pip install numpy scipy matplotlib pandas
验证安装
import scipy
print(scipy.__version__)
基本导入方式
# 导入整个库
import scipy
# 导入特定模块
from scipy import optimize, integrate, stats
# 常用组合导入
import numpy as np
from scipy import optimize
import matplotlib.pyplot as plt
核心模块概览
| 模块 | 功能描述 |
|---|---|
scipy.optimize |
优化和根查找算法 |
scipy.integrate |
积分和微分方程求解 |
scipy.interpolate |
插值和平滑样条 |
scipy.linalg |
线性代数运算 |
scipy.stats |
统计分布和检验 |
scipy.signal |
信号处理工具 |
scipy.sparse |
稀疏矩阵处理 |
scipy.fft |
快速傅里叶变换 |
scipy.special |
特殊数学函数 |
scipy.optimize - 优化与拟合
1. 函数最小化
import numpy as np
from scipy import optimize
import matplotlib.pyplot as plt
# 定义目标函数:Rosenbrock函数(测试优化算法的经典函数)
def rosenbrock(x):
return (1 - x[0])**2 + 100 * (x[1] - x[0]**2)**2
# 初始猜测点
x0 = np.array([-1, 2])
# 使用 BFGS 算法进行无约束最小化
result = optimize.minimize(rosenbrock, x0, method='BFGS')
print(f"最小值点: {result.x}")
print(f"最小值: {result.fun}")
print(f"迭代次数: {result.nit}")
print(f"是否成功: {result.success}")
2. 曲线拟合
from scipy.optimize import curve_fit
# 定义拟合函数模型
def func(x, a, b, c):
"""指数衰减函数模型"""
return a * np.exp(-b * x) + c
# 生成模拟数据
x_data = np.linspace(0, 4, 50)
y_true = func(x_data, 2.5, 1.3, 0.5)
noise = np.random.normal(0, 0.1, len(x_data))
y_data = y_true + noise
# 进行曲线拟合
popt, pcov = curve_fit(func, x_data, y_data)
print(f"拟合参数: a={popt[0]:.3f}, b={popt[1]:.3f}, c={popt[2]:.3f}")
# 计算参数的标准差
perr = np.sqrt(np.diag(pcov))
print(f"参数标准差: {perr}")
3. 根查找
from scipy.optimize import fsolve, brentq
# 定义方程
def equation(x):
return x**3 - 2*x**2 - 5*x + 6
# 使用 fsolve 求解(需要初始猜测)
root1 = fsolve(equation, x0=0)
print(f"根1: {root1[0]:.6f}")
# 使用 brentq 在区间内查找(更稳健)
root2 = brentq(equation, -2, 0)
root3 = brentq(equation, 0, 3)
print(f"根2: {root2:.6f}")
print(f"根3: {root3:.6f}")
4. 约束优化
# 定义目标函数
def objective(x):
return x[0]**2 + x[1]**2
# 定义约束条件
def constraint1(x):
return x[0] + x[1] - 1 # 等式约束: x[0] + x[1] = 1
# 约束定义
constraints = {'type': 'eq', 'fun': constraint1}
bounds = [(0, None), (0, None)] # x[0] >= 0, x[1] >= 0
# 求解
result = optimize.minimize(
objective,
x0=[0.5, 0.5],
method='SLSQP',
bounds=bounds,
constraints=constraints
)
print(f"约束优化结果: x = {result.x}")
print(f"目标函数值: {result.fun}")
scipy.integrate - 积分计算
1. 数值积分
from scipy import integrate
import numpy as np
# 定义被积函数
def integrand(x):
return np.exp(-x**2)
# 一重积分
result, error = integrate.quad(integrand, 0, np.inf)
print(f"∫[0,∞] e^(-x²) dx = {result:.10f}")
print(f"误差估计: {error:.2e}")
print(f"理论值 √π/2 = {np.sqrt(np.pi)/2:.10f}")
# 二重积分
def integrand2(y, x):
return x * y
result2, error2 = integrate.dblquad(integrand2, 0, 1, 0, 1)
print(f"二重积分结果: {result2}")
2. 常微分方程求解
from scipy.integrate import odeint, solve_ivp
import numpy as np
import matplotlib.pyplot as plt
# 定义ODE: dy/dt = -2y
def model(y, t):
return -2 * y
# 初始条件和时间点
y0 = 1
t = np.linspace(0, 5, 100)
# 使用 odeint 求解
y = odeint(model, y0, t)
# 使用 solve_ivp(推荐新方法)
sol = solve_ivp(lambda t, y: -2*y, [0, 5], [y0], t_eval=t)
# 绘制结果
plt.plot(t, y, 'b-', label='odeint')
plt.plot(sol.t, sol.y[0], 'r--', label='solve_ivp')
plt.plot(t, np.exp(-2*t), 'g:', label='解析解')
plt.xlabel('t')
plt.ylabel('y')
plt.legend()
plt.title('ODE 求解比较')
3. 洛伦兹吸引子(复杂ODE示例)
def lorenz(t, state, sigma=10, rho=28, beta=8/3):
"""洛伦兹系统"""
x, y, z = state
dxdt = sigma * (y - x)
dydt = x * (rho - z) - y
dzdt = x * y - beta * z
return [dxdt, dydt, dzdt]
# 初始条件
initial_state = [1.0, 1.0, 1.0]
t_span = [0, 50]
t_eval = np.linspace(0, 50, 10000)
# 求解
sol = solve_ivp(lorenz, t_span, initial_state, t_eval=t_eval, method='RK45')
# 3D 可视化
from mpl_toolkits.mplot3d import Axes3D
fig = plt.figure(figsize=(10, 8))
ax = fig.add_subplot(111, projection='3d')
ax.plot(sol.y[0], sol.y[1], sol.y[2], linewidth=0.5)
ax.set_xlabel('X')
ax.set_ylabel('Y')
ax.set_zlabel('Z')
ax.set_title('Lorenz Attractor')
scipy.interpolate - 插值
1. 一维插值
from scipy import interpolate
import numpy as np
import matplotlib.pyplot as plt
# 原始数据点
x = np.linspace(0, 10, 10)
y = np.sin(x)
# 更精细的插值点
x_new = np.linspace(0, 10, 100)
# 线性插值
f_linear = interpolate.interp1d(x, y, kind='linear')
y_linear = f_linear(x_new)
# 三次样条插值
cs = interpolate.CubicSpline(x, y)
y_cubic = cs(x_new)
# 绘制比较
plt.scatter(x, y, color='red', s=50, label='原始数据')
plt.plot(x_new, np.sin(x_new), 'k-', label='真实函数')
plt.plot(x_new, y_linear, 'b--', label='线性插值')
plt.plot(x_new, y_cubic, 'g:', label='三次样条')
plt.legend()
plt.title('插值方法比较')
2. 二维插值
from scipy.interpolate import griddata
# 生成随机散点数据
np.random.seed(42)
points = np.random.rand(100, 2)
values = np.sin(points[:, 0] * 10) * np.cos(points[:, 1] * 10)
# 创建规则网格
grid_x, grid_y = np.mgrid[0:1:100j, 0:1:100j]
# 不同插值方法
grid_z_nearest = griddata(points, values, (grid_x, grid_y), method='nearest')
grid_z_linear = griddata(points, values, (grid_x, grid_y), method='linear')
grid_z_cubic = griddata(points, values, (grid_x, grid_y), method='cubic')
# 可视化
fig, axes = plt.subplots(1, 3, figsize=(15, 4))
methods = ['Nearest', 'Linear', 'Cubic']
grids = [grid_z_nearest, grid_z_linear, grid_z_cubic]
for ax, method, grid in zip(axes, methods, grids):
ax.imshow(grid.T, extent=(0, 1, 0, 1), origin='lower', cmap='viridis')
ax.scatter(points[:, 0], points[:, 1], c='red', s=10)
ax.set_title(f'{method} Interpolation')
scipy.linalg - 线性代数
1. 基本矩阵运算
from scipy import linalg
import numpy as np
# 创建矩阵
A = np.array([[1, 2, 3],
[4, 5, 6],
[7, 8, 10]])
# 行列式
det = linalg.det(A)
print(f"行列式: {det:.4f}")
# 逆矩阵
A_inv = linalg.inv(A)
print(f"逆矩阵:\n{A_inv}")
# 验证
print(f"A @ A_inv =\n{np.round(A @ A_inv, 10)}")
# 矩阵范数
print(f"Frobenius范数: {linalg.norm(A)}")
print(f"2-范数: {linalg.norm(A, 2)}")
2. 特征值分解
# 特征值和特征向量
eigenvalues, eigenvectors = linalg.eig(A)
print("特征值:")
print(eigenvalues)
print("\n特征向量:")
print(eigenvectors)
# 验证: A @ v = λ * v
for i in range(len(eigenvalues)):
lhs = A @ eigenvectors[:, i]
rhs = eigenvalues[i] * eigenvectors[:, i]
print(f"\n特征向量 {i+1} 验证: {np.allclose(lhs, rhs)}")
3. 线性方程组求解
# 求解 Ax = b
b = np.array([1, 2, 3])
# 方法1: 使用 solve
x = linalg.solve(A, b)
print(f"解 x: {x}")
print(f"验证 Ax = b: {np.allclose(A @ x, b)}")
# 方法2: LU分解
lu, piv = linalg.lu_factor(A)
x_lu = linalg.lu_solve((lu, piv), b)
print(f"LU分解解: {x_lu}")
# 方法3: 对于大规模稀疏系统,使用稀疏矩阵求解器
from scipy.sparse import csr_matrix
from scipy.sparse.linalg import spsolve
A_sparse = csr_matrix(A)
x_sparse = spsolve(A_sparse, b)
print(f"稀疏求解器解: {x_sparse}")
4. 奇异值分解 (SVD)
# SVD分解
U, s, Vh = linalg.svd(A)
print(f"U 矩阵:\n{U}")
print(f"奇异值: {s}")
print(f"Vh 矩阵:\n{Vh}")
# 重构原矩阵
S = np.diag(s)
A_reconstructed = U @ S @ Vh
print(f"\n重构矩阵:\n{A_reconstructed}")
print(f"重构误差: {np.max(np.abs(A - A_reconstructed)):.2e}")
scipy.stats - 统计
1. 概率分布
from scipy import stats
import numpy as np
import matplotlib.pyplot as plt
# 正态分布
mu, sigma = 0, 1
normal_dist = stats.norm(mu, sigma)
# 概率密度函数 (PDF)
x = np.linspace(-5, 5, 100)
pdf = normal_dist.pdf(x)
# 累积分布函数 (CDF)
cdf = normal_dist.cdf(x)
# 随机采样
samples = normal_dist.rvs(size=1000)
# 绘制
fig, axes = plt.subplots(1, 2, figsize=(12, 4))
axes[0].plot(x, pdf, 'b-', label='PDF')
axes[0].hist(samples, bins=30, density=True, alpha=0.7, label='Samples')
axes[0].legend()
axes[0].set_title('Normal Distribution PDF')
axes[1].plot(x, cdf, 'r-', label='CDF')
axes[1].legend()
axes[1].set_title('Normal Distribution CDF')
2. 统计检验
# 生成两组数据
np.random.seed(42)
group1 = np.random.normal(100, 10, 100) # 均值100,标准差10
group2 = np.random.normal(105, 10, 100) # 均值105,标准差10
# t检验
t_stat, p_value = stats.ttest_ind(group1, group2)
print(f"t统计量: {t_stat:.4f}")
print(f"p值: {p_value:.4f}")
print(f"显著性水平 α=0.05: {'拒绝原假设' if p_value < 0.05 else '不拒绝原假设'}")
# 单样本t检验
t_stat2, p_value2 = stats.ttest_1samp(group1, 100)
print(f"\n单样本t检验:")
print(f"t统计量: {t_stat2:.4f}, p值: {p_value2:.4f}")
# 卡方检验
observed = np.array([10, 20, 30])
expected = np.array([15, 15, 30])
chi2, p_chi2 = stats.chisquare(observed, expected)
print(f"\n卡方检验: χ²={chi2:.4f}, p={p_chi2:.4f}")
3. 描述性统计
# 生成数据
data = np.random.normal(50, 15, 1000)
# 描述性统计
print(f"均值: {np.mean(data):.2f}")
print(f"中位数: {np.median(data):.2f}")
print(f"标准差: {np.std(data):.2f}")
print(f"方差: {np.var(data):.2f}")
print(f"偏度: {stats.skew(data):.4f}")
print(f"峰度: {stats.kurtosis(data):.4f}")
# 百分位数
percentiles = [25, 50, 75]
print(f"\n百分位数 {percentiles}: {np.percentile(data, percentiles)}")
# 综合描述统计
desc = stats.describe(data)
print(f"\n综合统计:\n{desc}")
scipy.signal - 信号处理
1. 滤波器设计
from scipy import signal
import numpy as np
import matplotlib.pyplot as plt
# 生成含噪声的信号
t = np.linspace(0, 1, 1000, endpoint=False)
clean_signal = np.sin(2 * np.pi * 5 * t) + 0.5 * np.sin(2 * np.pi * 50 * t)
noise = np.random.normal(0, 0.3, len(t))
noisy_signal = clean_signal + noise
# 设计低通滤波器
sos = signal.butter(10, 20, 'low', fs=1000, output='sos')
filtered_signal = signal.sosfilt(sos, noisy_signal)
# 绘制
plt.figure(figsize=(12, 6))
plt.plot(t, noisy_signal, 'b-', alpha=0.5, label='含噪声信号')
plt.plot(t, filtered_signal, 'r-', label='滤波后信号')
plt.plot(t, clean_signal, 'g--', label='原始信号')
plt.xlabel('时间 [s]')
plt.ylabel('幅值')
plt.legend()
plt.title('低通滤波效果')
2. 快速傅里叶变换 (FFT)
from scipy.fft import fft, fftfreq
# 采样参数
fs = 1000 # 采样频率
T = 1/fs # 采样周期
t = np.linspace(0, 1, fs, endpoint=False)
# 复合信号
signal_data = np.sin(2 * np.pi * 50 * t) + 0.5 * np.sin(2 * np.pi * 120 * t)
# FFT
yf = fft(signal_data)
xf = fftfreq(fs, T)[:fs//2]
# 绘制频谱
plt.figure(figsize=(12, 4))
plt.plot(xf, 2.0/fs * np.abs(yf[0:fs//2]))
plt.xlabel('频率 [Hz]')
plt.ylabel('幅值')
plt.title('频谱分析')
plt.grid()
3. 卷积和相关
# 信号
x = np.array([1, 2, 3, 4, 5])
h = np.array([1, 0, -1])
# 卷积
conv_result = signal.convolve(x, h, mode='full')
print(f"卷积结果: {conv_result}")
# 互相关
corr_result = signal.correlate(x, h, mode='full')
print(f"互相关结果: {corr_result}")
# 自相关
auto_corr = signal.correlate(x, x, mode='full')
print(f"自相关结果: {auto_corr}")
scipy.sparse - 稀疏矩阵
1. 稀疏矩阵创建
from scipy.sparse import csr_matrix, csc_matrix, coo_matrix
import numpy as np
# 密集矩阵
dense_matrix = np.array([
[1, 0, 0, 2],
[0, 3, 0, 0],
[0, 0, 4, 0],
[5, 0, 0, 6]
])
# 转换为稀疏矩阵格式
sparse_csr = csr_matrix(dense_matrix)
sparse_csc = csc_matrix(dense_matrix)
sparse_coo = coo_matrix(dense_matrix)
print(f"原始矩阵大小: {dense_matrix.nbytes} bytes")
print(f"CSR格式数据: {sparse_csr.data}")
print(f"CSR格式索引: {sparse_csr.indices}")
print(f"CSR格式指针: {sparse_csr.indptr}")
# 从COO格式创建(适合构建)
row = np.array([0, 1, 2, 3, 3])
col = np.array([0, 1, 2, 0, 3])
data = np.array([1, 3, 4, 5, 6])
coo = coo_matrix((data, (row, col)), shape=(4, 4))
print(f"\nCOO矩阵:\n{coo.toarray()}")
2. 稀疏矩阵运算
from scipy.sparse.linalg import spsolve, eigsh
# 创建大型稀疏矩阵
n = 1000
diagonals = [np.ones(n), -2*np.ones(n), np.ones(n)]
A_sparse = sparse.diags(diagonals, [-1, 0, 1], format='csr')
# 稀疏矩阵向量乘法
x = np.random.rand(n)
b = A_sparse @ x
# 求解稀疏线性系统
x_solved = spsolve(A_sparse, b)
print(f"求解误差: {np.max(np.abs(x - x_solved)):.2e}")
# 计算部分特征值(对于大型矩阵更高效)
eigenvalues, eigenvectors = eigsh(A_sparse, k=5, which='SM')
print(f"\n最小5个特征值: {eigenvalues}")
综合实例
实例1:数据拟合与预测
import numpy as np
from scipy.optimize import curve_fit
from scipy import stats
import matplotlib.pyplot as plt
# 模拟实验数据(带噪声)
np.random.seed(42)
x_data = np.linspace(0, 10, 50)
true_params = [2.5, -1.0, 0.5] # 真实参数
# 多项式模型 + 噪声
def polynomial(x, a, b, c):
return a * x**2 + b * x + c
y_true = polynomial(x_data, *true_params)
y_data = y_true + np.random.normal(0, 2, len(x_data))
# 拟合
popt, pcov = curve_fit(polynomial, x_data, y_data)
# 计算R²
y_pred = polynomial(x_data, *popt)
ss_res = np.sum((y_data - y_pred)**2)
ss_tot = np.sum((y_data - np.mean(y_data))**2)
r_squared = 1 - (ss_res / ss_tot)
# 置信区间
perr = np.sqrt(np.diag(pcov))
print("拟合结果:")
print(f"参数: a={popt[0]:.3f}±{perr[0]:.3f}, b={popt[1]:.3f}±{perr[1]:.3f}, c={popt[2]:.3f}±{perr[2]:.3f}")
print(f"R² = {r_squared:.4f}")
# 可视化
plt.figure(figsize=(10, 6))
plt.scatter(x_data, y_data, label='实验数据', alpha=0.6)
plt.plot(x_data, y_true, 'g--', label='真实曲线')
plt.plot(x_data, y_pred, 'r-', label='拟合曲线')
plt.xlabel('x')
plt.ylabel('y')
plt.legend()
plt.title('多项式拟合')
实例2:物理系统仿真
from scipy.integrate import solve_ivp
import numpy as np
import matplotlib.pyplot as plt
# 单摆系统(非线性)
def pendulum(t, y, g=9.81, L=1.0):
"""
y[0] = 角度 θ
y[1] = 角速度 ω
"""
theta, omega = y
dtheta_dt = omega
domega_dt = -(g/L) * np.sin(theta)
return [dtheta_dt, domega_dt]
# 初始条件:大角度(非线性效应明显)
y0 = [np.pi/2, 0] # 90度,静止释放
t_span = [0, 10]
t_eval = np.linspace(0, 10, 1000)
# 求解
sol = solve_ivp(pendulum, t_span, y0, t_eval=t_eval, method='RK45')
# 可视化
fig, axes = plt.subplots(2, 1, figsize=(10, 8))
# 角度随时间变化
axes[0].plot(sol.t, sol.y[0])
axes[0].set_xlabel('时间 [s]')
axes[0].set_ylabel('角度 [rad]')
axes[0].set_title('单摆运动')
axes[0].grid()
# 相空间图(角度 vs 角速度)
axes[1].plot(sol.y[0], sol.y[1])
axes[1].set_xlabel('角度 [rad]')
axes[1].set_ylabel('角速度 [rad/s]')
axes[1].set_title('相空间图')
axes[1].grid()
plt.tight_layout()
实例3:图像处理(结合scipy.ndimage)
from scipy import ndimage
import numpy as np
import matplotlib.pyplot as plt
# 创建测试图像
np.random.seed(42)
image = np.zeros((100, 100))
image[30:70, 30:70] = 1 # 正方形
image += np.random.normal(0, 0.1, image.shape)
# 高斯滤波
filtered = ndimage.gaussian_filter(image, sigma=2)
# 边缘检测
sobel_x = ndimage.sobel(image, axis=0)
sobel_y = ndimage.sobel(image, axis=1)
sobel_magnitude = np.sqrt(sobel_x**2 + sobel_y**2)
# 形态学操作
from scipy.ndimage import binary_erosion, binary_dilation
binary = image > 0.5
eroded = binary_erosion(binary)
dilated = binary_dilation(binary)
# 可视化
fig, axes = plt.subplots(2, 3, figsize=(12, 8))
axes[0, 0].imshow(image, cmap='gray')
axes[0, 0].set_title('原始图像')
axes[0, 1].imshow(filtered, cmap='gray')
axes[0, 1].set_title('高斯滤波')
axes[0, 2].imshow(sobel_magnitude, cmap='gray')
axes[0, 2].set_title('Sobel边缘检测')
axes[1, 0].imshow(binary, cmap='gray')
axes[1, 0].set_title('二值化')
axes[1, 1].imshow(eroded, cmap='gray')
axes[1, 1].set_title('腐蚀')
axes[1, 2].imshow(dilated, cmap='gray')
axes[1, 2].set_title('膨胀')
for ax in axes.flat:
ax.axis('off')
总结与资源
SciPy 核心要点
- 优化 (optimize): 函数最小化、曲线拟合、根查找
- 积分 (integrate): 数值积分、ODE求解
- 插值 (interpolate): 一维/二维插值、样条
- 线性代数 (linalg): 矩阵运算、分解、求解
- 统计 (stats): 分布、检验、描述统计
- 信号处理 (signal): 滤波、FFT、卷积
- 稀疏矩阵 (sparse): 高效存储和运算
学习资源
- 官方文档: https://docs.scipy.org/doc/scipy/
- SciPy Lecture Notes: https://scipy-lectures.org/
- GitHub: https://github.com/scipy/scipy
- Stack Overflow: 搜索 “scipy” 标签
最佳实践
- 优先使用 NumPy: 基础数组操作使用 NumPy
- 选择合适的算法: 根据问题特点选择优化方法
- 注意数值稳定性: 处理病态问题时谨慎
- 利用稀疏性: 大规模稀疏问题使用稀疏矩阵
- 验证结果: 重要计算进行结果验证
与其他库的配合
| 库 | 用途 |
|---|---|
| NumPy | 基础数组操作 |
| Matplotlib | 数据可视化 |
| Pandas | 数据处理 |
| Scikit-learn | 机器学习 |
| SymPy | 符号计算 |
本教程基于 SciPy 1.10+ 版本编写,建议保持库版本更新以获得最佳体验。
AtomGit 是由开放原子开源基金会联合 CSDN 等生态伙伴共同推出的新一代开源与人工智能协作平台。平台坚持“开放、中立、公益”的理念,把代码托管、模型共享、数据集托管、智能体开发体验和算力服务整合在一起,为开发者提供从开发、训练到部署的一站式体验。
更多推荐
所有评论(0)