Python科学计算(第2版)
上QQ阅读APP看书,第一时间看更新

3.3.3 特征值和特征向量

n×n的矩阵A可以看作n维空间中的线性变换。若x为n维空间中的一个向量,那么Ax的矩阵乘积就是对x进行线性变换之后的向量。如果x是线性变换的特征向量,那么经过这个线性变换之后,得到的新向量仍然与原来的x保持在同一方向上,但其长度也许会改变。特征向量的长度在该线性变换下缩放的比例称为其特征值。即特征向量x满足如下等式,λ的值可以是一个任意复数:

Axx

下面以二维平面上的线性变换矩阵为例,演示特征值和特征变量的几何含义。通过linalg.eig(A)计算矩阵A的两个特征值evalues和特征向量evectors,在evectors中,每一列是一个特征向量。

    A = np.array([[1, -0.3], [-0.1, 0.9]])
    evalues, evectors = linalg.eig(A)
                 evalues                          evectors          
    ----------------------------------  ----------------------------
    [ 1.13027756+0.j,  0.76972244+0.j]  [[ 0.91724574,  0.79325185],
    [-0.3983218 ,  0.60889368]]

图3-7显示了变换前后的向量。在图中,蓝色箭头为变换之前的向量,红色箭头为变换之后的向量。粗箭头为变换前后的特征向量。可以看出特征向量变换前后方向没有改变,只是长度发生了变化。长度的变化倍数由特征值决定:一个变为原来的1.13倍长,一个变为原来的0.77倍长。

图3-7 线性变换将蓝色箭头变换为红色箭头

numpy.linalg模块中也有eig( )函数,与之不同的是,scipy.linalg模块中的eig( )函数支持计算广义特征值和广义特征向量,它们满足如下等式,其中B是一个n×n的矩阵:

AxBx

广义特征向量可以用于椭圆拟合,椭圆拟合的公式与原理可以参考下面的论文:

http://research.microsoft.com/pubs/67845/ellipse-pami.pdf

用广义特征向量计算椭圆拟合。

椭圆上的点满足如下方程,其中a,b,c,d,e,f为椭圆的参数,(x,y)为平面上的坐标点:

f(x,y)=ax2 +bxy+cy2 +dx+ey+f=0

所谓椭圆拟合,就是指给出一组平面上的点(xi ,yi ),找到一组椭圆参数,使得∑f(xi ,yi )2最小。显然这是一个最小化问题,可以使用上节介绍的优化算法optimize.leastsq( )求解。为了避免参数全为0的平凡解,需要一点小技巧,请读者自行演练一下。下面给出论文中用广义特征向量计算椭圆拟合的方法:

首先定义D是一个n×6的矩阵,其中n为点的个数,D中的每一行与一个坐标点相对应。a为拟合椭圆的系数:a =[a,b,c,d,e,f]T。则a满足如下方程:

DT DaCa

其中C是一个6×6的矩阵:

显然上式符合广义特征向量的等式,因此可以用linalg.eig( )求解。下面首先使用椭圆的参数方程计算某个椭圆上随机的60个点,并引入一些随机噪声:

    np.random.seed(42)
    t = np.random.uniform(0, 2*
np.pi, 60)
    
    alpha = 0.4
    a = 0.5
    b = 1.0
    x = 1.0 + a*
np.cos(t)*
np.cos(alpha) - b*
np.sin(t)*
np.sin(alpha)
    y = 1.0 + a*
np.cos(t)*
np.sin(alpha) - b*
np.sin(t)*
np.cos(alpha)
    x += np.random.normal(0, 0.05, size=len(x))
    y += np.random.normal(0, 0.05, size=len(y))

❶当传递第二个参数时,eig( )计算广义特征值和向量。evectors中共有6个特征向量,❷将这6个特征向量代入椭圆方程中,计算平均误差,❸并挑选误差最小的特征向量作为椭圆的参数p。图3-8显示了参数p所表示的椭圆以及数据点。

图3-8 用广义特征向量计算的拟合椭圆

    D = np.c_[x**
2, x*
y, y**
2, x, y, np.ones_like(x)]
    A = np.dot(D.T, D)
    C = np.zeros((6, 6))
    C[[0, 1, 2], [2, 1, 0]] = 2, -1, 2
    evalues, evectors = linalg.eig(A, C)     ❶
    evectors = np.real(evectors)
    err = np.mean(np.dot(D, evectors)**
2, 0) ❷
    p = evectors[:, np.argmin(err) ]         ❸
    print p
    [-0.55214278  0.5580915  -0.23809922  0.54584559 -0.08350449 -0.14852803]