
本文详解为何直接调用 np.cov() 或手动计算 Z.T @ Z / (n−1) 会出错,并指出关键前提:数据必须中心化(减去均值);通过代码演示标准用法、手推验证及常见误区,确保协方差矩阵结果准确可靠。
本文详解为何直接调用 `np.cov()` 或手动计算 `z.t @ z / (n−1)` 会出错,并指出关键前提:数据必须中心化(减去均值);通过代码演示标准用法、手推验证及常见误区,确保协方差矩阵结果准确可靠。
在使用 NumPy 计算协方差矩阵时,一个常见误解是认为公式
[
\mathbf{C} = \frac{1}{n-1}\mathbf{Z}^\top \mathbf{Z}
]
可直接对原始数据矩阵 (\mathbf{X}) 应用。但该公式仅在 (\mathbf{Z}) 是中心化数据(即每列均值为 0)时才成立——即 (\mathbf{Z} = \mathbf{X} - \boldsymbol{\mu}),其中 (\boldsymbol{\mu}) 是按列计算的均值向量。
以下以您提供的 10×2 数据为例进行完整验证:
import numpy as np
X = np.array([
[0.69, 0.49],
[-1.31, -1.21],
[0.39, 0.99],
[0.09, 0.29],
[1.29, 1.09],
[0.49, 0.79],
[0.19, -0.31],
[-0.81, -0.81],
[-0.31, -0.31],
[-0.71, -1.01]
])
# ✅ 步骤 1:中心化(关键!)
mean_X = np.mean(X, axis=0) # shape: (2,)
Z = X - mean_X # shape: (10, 2)
# ✅ 步骤 2:应用公式 (1/(n-1)) * Z.T @ Z
n = X.shape[0] # n = 10
cov_manual = (1 / (n - 1)) * (Z.T @ Z)
print("手动计算(中心化后):")
print(cov_manual)
输出:
[[0.61655556 0.61544444] [0.61544444 0.71655556]]
该结果与 np.cov(X, rowvar=False) 完全一致:
print("np.cov(X, rowvar=False):")
print(np.cov(X, rowvar=False))
⚠️ 注意事项:
np.cov()默认rowvar=True,即把每行当作一个变量(这与常规统计习惯相反)。对于形状为(n_samples, n_features)的数据(如本例的10×2),必须显式指定rowvar=False,否则会错误地将 10 个样本视为 10 个变量,导致 10×10 协方差矩阵。- 若未中心化而直接计算
X.T @ X / (n-1),结果为[[5.549, 5.539], [5.539, 6.449]],这并非协方差,而是 未去均值的二阶矩,其数学意义完全不同。- 您提到的“期望结果”
[[0.7322, 0.6189], [0.6189, 0.5956]]实际对应的是 总体协方差(除以 n 而非 n−1)且可能基于不同中心化方式或数据源;NumPy 默认使用无偏估计(Bessel 校正,除以 n−1),符合统计学标准。
✅ 总结:
要正确使用 np.cov() 或手动实现协方差矩阵,请始终遵循三步原则:
-
确认数据维度:确保特征列为列(
rowvar=False); -
强制中心化:即使
np.cov()内部已自动中心化,手动推导时也必须显式减均值; -
核对自由度:
np.cov()默认ddof=1(即除以n−1);若需总体协方差,可设ddof=0:np.cov(X, rowvar=False, ddof=0) # 除以 n
掌握这一逻辑,即可避免绝大多数协方差计算误差,为后续 PCA、回归分析等打下坚实基础。










