scipy.linalg.eig为何对协方差矩阵返回复特征值?

28 投票
2 回答
20843 浏览
提问于 2025-04-17 09:42

协方差矩阵的特征值应该是实数且非负的,因为协方差矩阵是对称的,并且是半正定的。

不过,看看下面这个用scipy做的实验:

>>> a=np.random.random(5)
>>> b=np.random.random(5)
>>> ab = np.vstack((a,b)).T
>>> C=np.cov(ab)
>>> eig(C)
7.90174997e-01 +0.00000000e+00j,
2.38344473e-17 +6.15983679e-17j,
2.38344473e-17 -6.15983679e-17j,
-1.76100435e-17 +0.00000000e+00j,   
5.42658040e-33 +0.00000000e+00j

但是,在Matlab中复现上面的例子是正确的:

a = [0.6271, 0.4314, 0.3453, 0.8073, 0.9739]
b = [0.1924, 0.3680, 0.0568, 0.1831, 0.0176]
C=cov([a;b])
eig(C)
-0.0000
-0.0000
 0.0000
 0.0000
 0.7902

2 个回答

5

你遇到的问题是因为浮点数的精度有限,导致了数值不稳定。

需要注意的是:

(1) MATLAB也返回了负值,但它的打印格式设置为short,所以你看不到存储在内存中的双精度数的全部精度。你可以使用format long g来打印更多的小数位。

(2) numpy的linalg.eig返回的所有虚部都接近机器的精度。因此,你可以把它们当作零来处理。

44

你提到了两个问题:

  1. 通过 scipy.linalg.eig 得到的特征值不是实数。
  2. 有些特征值是负数。

这两个问题都是因为在使用浮点运算的迭代算法时,出现了截断和舍入误差。这种情况是不可避免的。值得注意的是,Matlab 的结果也出现了负特征值。

现在,来看看这个问题更有趣的一面:为什么 Matlab 的结果是实数,而 SciPy 的结果却有一些复数部分呢?

Matlab 的 eig 函数会检测输入的矩阵是否是实对称矩阵或厄米矩阵,并在这种情况下使用 Cholesky 分解。你可以在 eig 文档 中找到关于 chol 参数的描述。而在 SciPy 中,这个过程并不是自动进行的。

如果你想使用一个能够利用实对称矩阵或厄米矩阵结构的算法,可以使用 scipy.linalg.eigh。对于问题中的例子:

>>> eigh(C, eigvals_only=True)
array([ -3.73825923e-17,  -1.60154836e-17,   8.11704449e-19,
         3.65055777e-17,   7.90175615e-01])

如果你将结果四舍五入到与 Matlab 打印的相同精度,这个结果就和 Matlab 的结果一样了。

撰写回答