NumPy 线性代数与数据应用
目标
- 掌握矩阵运算、特征值分解、SVD
- 求解线性方程组
- 使用 NumPy 进行数据清洗与特征工程
- 多项式拟合与信号处理入门
完整代码
1. 矩阵运算
import numpy as np
# ============================================================
# 矩阵乘法
# ============================================================
A = np.array([[1, 2], [3, 4]])
B = np.array([[5, 6], [7, 8]])
# 三种等价写法
C1 = A @ B # Python 3.5+ 推荐
C2 = np.matmul(A, B)
C3 = np.dot(A, B)
print(f"矩阵乘法:\n{C1}")
# 逐元素乘法
print(f"逐元素乘法:\n{A * B}")
# 向量点积
a = np.array([1, 2, 3])
b = np.array([4, 5, 6])
print(f"点积: {np.dot(a, b)}") # 32
print(f"内积: {np.inner(a, b)}") # 32
print(f"外积:\n{np.outer(a, b)}") # 3×3 矩阵
# ============================================================
# 矩阵属性
# ============================================================
print(f"\n行列式: {np.linalg.det(A):.2f}")
print(f"逆矩阵:\n{np.linalg.inv(A)}")
print(f"转置: {(A @ np.linalg.inv(A)).round(10)}") # 应等于单位矩阵
# ============================================================
# 求解线性方程组 Ax = b
# ============================================================
# 3x + y = 9
# x + 2y = 8
A = np.array([[3, 1], [1, 2]])
b = np.array([9, 8])
x = np.linalg.solve(A, b)
print(f"\n方程组的解: x={x[0]:.1f}, y={x[1]:.1f}") # x=2, y=3
print(f"验证 Ax-b = {A @ x - b}") # 应接近 [0,0]
2. 特征值与特征向量
# ============================================================
# 特征值分解
# ============================================================
A = np.array([[4, 2], [1, 3]])
eigenvalues, eigenvectors = np.linalg.eig(A)
print(f"特征值: {eigenvalues}")
print(f"特征向量:\n{eigenvectors}")
# 验证: A @ v = λ @ v
for i in range(len(eigenvalues)):
v = eigenvectors[:, i]
λ = eigenvalues[i]
print(f"验证 A·v{i}: {A @ v}")
print(f"验证 λ{i}·v: {λ * v}")
print(f"误差: {np.linalg.norm(A @ v - λ * v):.2e}\n")
# ============================================================
# SVD 奇异值分解
# ============================================================
M = np.array([[1, 2, 3], [4, 5, 6]])
U, S, Vt = np.linalg.svd(M, full_matrices=False)
print(f"U (左奇异向量):\n{U}")
print(f"Σ (奇异值): {S}")
print(f"Vt (右奇异向量转置):\n{Vt}")
# 重构
M_reconstructed = U @ np.diag(S) @ Vt
print(f"重构矩阵:\n{M_reconstructed}")
print(f"重构误差: {np.linalg.norm(M - M_reconstructed):.2e}")
3. 数据清洗与统计
# ============================================================
# 处理缺失值
# ============================================================
data = np.array([1.0, 2.0, np.nan, 4.0, np.nan, 6.0])
# 检测 NaN
nan_mask = np.isnan(data)
print(f"\nNaN 位置: {nan_mask}")
print(f"NaN 数量: {np.sum(nan_mask)}")
# 填充 NaN(用均值)
clean_mean = np.where(nan_mask, np.nanmean(data), data)
print(f"均值填充: {clean_mean}")
# 填充 NaN(用前向填充)
from numpy.lib.stride_tricks import sliding_window_view
# 简化:用 np.nan_to_num
clean_zero = np.nan_to_num(data, nan=0)
print(f"零填充: {clean_zero}")
# ============================================================
# 描述性统计
# ============================================================
scores = np.array([78, 85, 92, 88, 76, 95, 89, 83, 91, 87])
print(f"\n均值: {np.mean(scores):.1f}")
print(f"中位数: {np.median(scores):.1f}")
print(f"标准差: {np.std(scores):.1f}")
print(f"方差: {np.var(scores):.1f}")
# 分位数
print(f"25%: {np.percentile(scores, 25):.1f}")
print(f"50%: {np.percentile(scores, 50):.1f}")
print(f"75%: {np.percentile(scores, 75):.1f}")
# 检测异常值(Z-score 方法)
z_scores = (scores - scores.mean()) / scores.std()
outliers = np.abs(z_scores) > 2
print(f"异常值 (|z| > 2): {scores[outliers]}")
# ============================================================
# 相关系数
# ============================================================
x = np.array([1, 2, 3, 4, 5])
y = np.array([2, 4, 5, 4, 6])
corr_matrix = np.corrcoef(x, y)
print(f"\n相关系数矩阵:\n{corr_matrix}")
print(f"皮尔逊相关系数: {corr_matrix[0, 1]:.4f}")
# ============================================================
# 直方图与分箱
# ============================================================
data = np.random.randn(1000)
hist, bin_edges = np.histogram(data, bins=20)
print(f"\n直方图计数: {hist}")
print(f"分箱边界: {bin_edges}")
# 数字分箱
ages = np.array([15, 22, 35, 42, 55, 68, 73])
bins = [0, 18, 35, 60, 100]
labels = ["未成年", "青年", "中年", "老年"]
categories = np.digitize(ages, bins) - 1
print(f"年龄分组索引: {categories}")
print(f"年龄分组: {np.array(labels)[categories]}")
4. 多项式拟合
# ============================================================
# 多项式回归
# ============================================================
x = np.array([0, 1, 2, 3, 4, 5])
y = np.array([1.1, 3.5, 8.2, 15.9, 26.2, 39.1]) # 大致 y = x² + 2x + 1 + noise
# 拟合二次多项式
coeffs = np.polyfit(x, y, deg=2) # 返回 [a, b, c] 对应 ax² + bx + c
print(f"\n拟合系数: a={coeffs[0]:.4f}, b={coeffs[1]:.4f}, c={coeffs[2]:.4f}")
# 预测
x_new = np.linspace(0, 6, 50)
y_pred = np.polyval(coeffs, x_new)
# 计算 R²
y_fit = np.polyval(coeffs, x)
ss_res = np.sum((y - y_fit) ** 2)
ss_tot = np.sum((y - np.mean(y)) ** 2)
r2 = 1 - ss_res / ss_tot
print(f"R² = {r2:.6f}")
5. 信号处理(简易)
# ============================================================
# FFT 快速傅里叶变换
# ============================================================
sr = 1000 # 采样率 1000Hz
t = np.linspace(0, 1, sr, endpoint=False)
# 50Hz + 120Hz 的两个正弦波叠加
signal = np.sin(2 * np.pi * 50 * t) + 0.5 * np.sin(2 * np.pi * 120 * t)
# FFT
fft_vals = np.fft.fft(signal)
freqs = np.fft.fftfreq(len(signal), 1 / sr)
# 取正频率部分
positive_mask = freqs > 0
freqs_pos = freqs[positive_mask]
magnitude = np.abs(fft_vals[positive_mask])
# 找峰值频率
peak_idx = np.argsort(magnitude)[-5:] # 前5个最大幅值
print(f"\n主要频率成分 (Hz): {freqs_pos[peak_idx]}")
print(f"对应幅值: {magnitude[peak_idx].round(2)}")
# 应能找到 50Hz 和 120Hz
关键要点
| 函数 |
说明 |
np.linalg.solve(A, b) |
解 Ax = b |
np.linalg.eig(A) |
特征值与特征向量 |
np.linalg.svd(M) |
奇异值分解 |
np.polyfit(x, y, deg) |
多项式拟合 |
np.fft.fft(signal) |
快速傅里叶变换 |
np.corrcoef(x, y) |
相关系数矩阵 |
np.isnan(x) |
检测 NaN |