本文主要参考下面的文章,文中的代码基本是把第二篇文章的代码手写实现了一下。

总体代码

  1. """
  2. 总的代码.
  3. Func: 对原始的特征矩阵进行降维, lowDataMat为降维之后返回新的特征矩阵。
  4. Usage: lowDDataMat = pca(dataMat, k)
  5. """
  6. # 零均值化
  7. def zeroMean(dataMat):
  8. # 求各列特征的平均值
  9. meanVal = np.mean(dataMat, axis=0)
  10. newData = dataMat - meanVal
  11. return newData, meanVal
  12. def pca(dataMat,k):
  13. newData,meanVal=zeroMean(dataMat)
  14. covMat=np.cov(newData,rowvar=0) #求协方差矩阵,return ndarray;若rowvar非0,一列代表一个样本,为0,一行代表一个样本
  15. eigVals,eigVects=np.linalg.eig(np.mat(covMat))#求特征值和特征向量,特征向量是按列放的,即一列代表一个特征向量
  16. eigValIndice=np.argsort(eigVals) #对特征值从小到大排序
  17. k_eigValIndice=eigValIndice[-1:-(k+1):-1] #最大的k个特征值的下标
  18. k_eigVect=eigVects[:,k_eigValIndice] #最大的k个特征值对应的特征向量
  19. lowDDataMat=newData*k_eigVect #低维特征空间的数据
  20. return lowDDataMat
  21. # reconMat=(lowDDataMat*k_eigVect.T)+meanVal #重构数据
  22. # return lowDDataMat,reconMat

下面逐步来实现PCA

(0)先准备好数据

  1. import numpy as np
  1. # n维的原始数据,本例中n=2。
  2. data = np.array([[2.5,2.4], [0.5, 0.7], [2.2, 2.9], [1.9, 2.2], [3.1, 3.0], [2.3, 2.7],\
  3. [2, 1.6], [1, 1.1], [1.5, 1.6], [1.1, 0.9]])
  4. print data
  1. [[ 2.5 2.4]
  2. [ 0.5 0.7]
  3. [ 2.2 2.9]
  4. [ 1.9 2.2]
  5. [ 3.1 3. ]
  6. [ 2.3 2.7]
  7. [ 2. 1.6]
  8. [ 1. 1.1]
  9. [ 1.5 1.6]
  10. [ 1.1 0.9]]

(1)零均值化

  1. # (1)零均值化
  2. def zeroMean(dataMat):
  3. # 求各列特征的平均值
  4. meanVal = np.mean(dataMat, axis=0)
  5. newData = dataMat - meanVal
  6. return newData, meanVal
  7. newData, meanVal = zeroMean(data)
  8. print 'the newData is \n', newData
  9. print 'the meanVal is \n', meanVal
  1. the newData is
  2. [[ 0.69 0.49]
  3. [-1.31 -1.21]
  4. [ 0.39 0.99]
  5. [ 0.09 0.29]
  6. [ 1.29 1.09]
  7. [ 0.49 0.79]
  8. [ 0.19 -0.31]
  9. [-0.81 -0.81]
  10. [-0.31 -0.31]
  11. [-0.71 -1.01]]
  12. the meanVal is
  13. [ 1.81 1.91]

(2)对各维特征的协方差矩阵

  1. # (2)求协方差矩阵,rowvar=036表示每列对应一维特征
  2. covMat = np.cov(newData, rowvar=0)
  3. print covMat
  4. # 若rowvar=1表示没行是一维特征,每列表示一个样本,显然咱们的数据不是这样的
  5. # covMat2 = np.cov(newData, rowvar=1)
  6. # print covMat2
  1. [[ 0.61655556 0.61544444]
  2. [ 0.61544444 0.71655556]]

(3)求(2)中的协方差矩阵的特征值和特征向量

  1. # (3)求协方差矩阵的特征值和特征向量,利用numpy中的线性代数模块linalg中的eig函数
  2. eigVals, eigVects = np.linalg.eig(np.mat(covMat))
  3. print '特征值为:\n', eigVals
  4. print '特征向量为\n', eigVects
  1. 特征值为:
  2. [ 0.0490834 1.28402771]
  3. 特征向量为
  4. [[-0.73517866 -0.6778734 ]
  5. [ 0.6778734 -0.73517866]]

上面的结果中:
特征值为:

[ 0.0490834 1.28402771]

特征向量为

[[-0.73517866 -0.6778734 ]

[0.6778734 -0.73517866]]

特征值0.0490834对应的特征向量是第一列(-0.73517866 0.6778734)

(4)降维到k维(k < n)

1.原始的输入特征 d 维,一共有 n 个样本,那么原始的特征矩阵为 A:[n, d],注意这个矩阵已经零均值化
2.保留 topK 的特征向量作为特征变换矩阵: [d, d] ->T: [d,k], 意义是把 d 维向量投影到 k 个主成分分量上面。
3.对原始特征矩阵进行降维: A T, 维度 [n,d] × [d, k] = [n,k],so easy

  1. # (4)保留主要的成分,将特征值按照从大到小的顺序排序,选择其中最大的k个,
  2. # 然后将对应的k个特征向量分别作为列向量组成的特征向量矩阵。
  3. # 比如本例子中保留1.28402771对应的特征向量(-0.6778734 -0.73517866)^T
  4. k = 1 # 此例中取k = 1
  5. eigValIndice = np.argsort(eigVals) # 从小到大排序
  6. n_eigValIndice = eigValIndice[-1:-(k+1):-1] # 取值最大的k个下标
  7. n_eigVect = eigVects[:, n_eigValIndice] # 取对应的k个特征向量
  8. print n_eigVect
  9. print n_eigVect.shape
  10. lowDataMat = newData*n_eigVect # 低维特征空间的数据
  11. reconMat = (lowDataMat * n_eigVect.T) + meanVal # 重构数据,得到降维之后的数据
  12. print '将样本点投影到选取的低维特征向量上,实际使用的是这个结果作为新的特征:\n', lowDataMat
  13. print '降维之后的样本:\n', reconMat
  1. [[-0.6778734 ]
  2. [-0.73517866]]
  3. (2L, 1L)

将样本点投影到选取的低维特征向量上,实际使用的是这个结果作为新的特征:

  1. [[-0.82797019]
  2. [ 1.77758033]
  3. [-0.99219749]
  4. [-0.27421042]
  5. [-1.67580142]
  6. [-0.9129491 ]
  7. [ 0.09910944]
  8. [ 1.14457216]
  9. [ 0.43804614]
  10. [ 1.22382056]]
  11. 降维之后的样本:
  12. [[ 2.37125896 2.51870601]
  13. [ 0.60502558 0.60316089]
  14. [ 2.48258429 2.63944242]
  15. [ 1.99587995 2.11159364]
  16. [ 2.9459812 3.14201343]
  17. [ 2.42886391 2.58118069]
  18. [ 1.74281635 1.83713686]
  19. [ 1.03412498 1.06853498]
  20. [ 1.51306018 1.58795783]
  21. [ 0.9804046 1.01027325]]

降维之后的样本:

[[ 2.37125896 2.51870601]
[ 0.60502558 0.60316089]
[ 2.48258429 2.63944242]
[ 1.99587995 2.11159364]
[ 2.9459812 3.14201343]
[ 2.42886391 2.58118069]
[ 1.74281635 1.83713686]
[ 1.03412498 1.06853498]
[ 1.51306018 1.58795783]
[ 0.9804046 1.01027325]]
原始样本:
[[ 2.5 2.4]
[ 0.5 0.7]
[ 2.2 2.9]
[ 1.9 2.2]
[ 3.1 3. ]
[ 2.3 2.7]
[ 2. 1.6]
[ 1. 1.1]
[ 1.5 1.6]
[ 1.1 0.9]]
通过比较可以看出,通过降维之后我们成功地实现了特征从二维降到了一维,降维之后会和原始数据有一定的变化,
我们可以认为通过这种方式消除了一部分的噪声(当然实际上很可能损失了部分真实信息)。
—————————————————————-分割线————————————————————————————-

利用sklearn实现PCA

  1. # 原始数据
  2. data = np.array([[2.5,2.4], [0.5, 0.7], [2.2, 2.9], [1.9, 2.2], [3.1, 3.0], [2.3, 2.7],\
  3. [2, 1.6], [1, 1.1], [1.5, 1.6], [1.1, 0.9]])
  4. # print data
  1. # 好吧,就是这么简单
  2. from sklearn.decomposition import PCA
  3. pca = PCA(n_components=1)
  4. new_feature = pca.fit_transform(data)
  5. print new_feature

[[-0.82797019]
[ 1.77758033]
[-0.99219749]
[-0.27421042]
[-1.67580142]
[-0.9129491 ]
[ 0.09910944]
[ 1.14457216]
[ 0.43804614]
[ 1.22382056]]