PCA是将原始数据X通过数学方法降维,得到产生该形式数据的内在原因。
主要步骤为:
1)求原始数据X的协方差矩阵S
2)求S的最大特征值对应的K个特征向量并构成权重矩阵W
3)将W与X内积得到降维数据Z
注意:协方差矩阵计算的是不同属性之间的交互关系。so,如果数据集有2维属性,那么协方差矩阵就是2x2维;如果有4维属性,则协方差矩阵为4x4维。
本文以Iris数据为例,对其进行主成分分析,原始数据X.T为150x4:
[[ 5.1 3.5 1.4 0.2]
[ 4.9 3. 1.4 0.2]
[ 4.7 3.2 1.3 0.2]
...,
[ 6.5 3. 5.2 2. ]
[ 6.2 3.4 5.4 2.3]
[ 5.9 3. 5.1 1.8]]
由于python中numpy.cov()分解协方差矩阵默认以行中存储属性值(而不是列),所以在cov时要对数据进行转置,计算得到的协方差矩阵S为4x4:
[[ 0.68569351 -0.03926846 1.27368233 0.5169038 ]
[-0.03926846 0.18800403 -0.32171275 -0.11798121]
[ 1.27368233 -0.32171275 3.11317942 1.29638747]
[ 0.5169038 -0.11798121 1.29638747 0.58241432]]
对S分解,得到最大两个特征值对应的特征向量构成的eVact为2x4:
[[ 0.36158968 -0.65653988 -0.58099728 0.31725455]
[-0.08226889 -0.72971237 0.59641809 -0.32409435]]
将eVact(2x4)与原始数据X(4x150)做内积得到降维数据lowDData为2x150,转置后为:
[[-1.20372752 -2.20339819]
[-0.94777551 -1.82208822]
[-1.0933017 -2.01121873]
...,
[-2.00596351 -0.27069955]
[-2.39807946 -0.51584852]
[-2.2282685 -0.21616115]]
对应2维图为:
由2维数据可见,基本已经可分,但是只显示了2类,将得到的数据带入FCM聚类算法,得到的错分率和原始数据带入相同。
PCA代码如下:
# -*- coding: utf-8 -*-
"""
Created on Sat Oct 7 20:21:05 2017
@author: wjw
"""
import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
from FCM.fcm import fcm
def readText(filePath):
lines = open(filePath,'r').readlines()
data = []
for line in lines:
dataList = line.split(',')
data.append([float(dataList[0]),float(dataList[1]),float(dataList[2]),
float(dataList[3])])
return np.array(data).T #这里要把矩阵转置成行代表属性列,变成4x150的matrix
def pca(data):
k=2
meanVals = np.mean(data,axis=1) #对每一行(每个属性)求平均值,因为协方差的计算中需要减去均值
# axis=0,求每一列的平均值;axis=1,求每一行的平均值
newMeanvals = []
for meanVal in meanVals:
newMeanvals.append([meanVal]) #单独构成list组成的list
meanRemoved = data - newMeanvals # 这样才能对应相减
covMat = np.cov(meanRemoved)
print(covMat)
print(covMat.shape)
eigVals,eigVects = np.linalg.eig(np.mat(covMat))
#linalg中的eig方法,得到特征值和特征向量
eig = list(zip(eigVals,eigVects)) # 将特征值特征向量压入一个eig
dataframe = pd.DataFrame(eig,columns=['eigVals','eigVects'])#变成dataframe
Ksorted = -np.sort(-dataframe.eigVals)[:k] # 降序排列取前k个
index = []
for i in range(dataframe.eigVals.size):
for k in Ksorted:
if dataframe.eigVals.iloc[i]==k: #iloc,iat,loc,at都是dataFrame中用来取值定位的方法,前者用index后者用行列名
index.append(i)
pickedata = dataframe.iloc[index,:] #得到特征值前k大对应的特征向量,并组合
pickedeigVects =pickedata.eigVects
array=[]
for p in pickedeigVects:
array.append(p.tolist()) #将matrix转换成list
array = np.mat(np.array(array)) #list转换成matrix
print(array)
lowDdata = array * np.mat(data)#将原数据转换成维度较低的数据
return lowDdata.T
def draw(data):
plt.plot(data[:,0],data[:,1],'or')
plt.show()
if __name__=="__main__":
filePath = r"E:\data\iris.txt"
data= readText(filePath)
lowDdata = pca(data)
print(lowDdata)
draw(lowDdata)
fmatrix = fcm(np.array(lowDdata),m=2,k=3)