主成分分析

主要思想:移动坐标轴,将n维特征映射到k维上(k<n),这k维是全新的正交特征。
这k维特征称为主元,是又一次构造出来的k维特征。而不是简单地从n维特征中去除其余n-k维特征。



假设有如下数据:

上面的数据这么解读,表示有两个点:

这两个点在初始坐标系下(也就是自然基e1 = [1,0] e2 = [0,1] )下坐标值为:


随着坐标系的不同,X1,X2 的值会不断变化,都是在坐标轴上的投影长度,要想值尽可能得分配给X1X2,借鉴最小二乘法的思想,就是让

假设:

根据点积的几何意义,可以表示:

那么:

上式其实是一个二次型:
这里矩阵P 就是二次型,是一个对称矩阵,可以进行如下的奇异值分解

其中U为正交矩阵,即UUT = I,
对角矩阵,奇异值1>奇异值2

把P带回去:

因为U是正交矩阵,所以令

n也是单位矩阵

继续会带:

最初的问题就转化为:

用拉格朗日乘子法计算上述条件极值,结果是当n1 = 1,n2 = 0时取到极值。
因此可以推出要寻找的主元1,即:

总结下:

同理,


协方差矩阵

按行来解读,得到


按列解读,得到

矩阵就变成了

样本方差为:

样本协方差为:

两相比较可以得到一个新的矩阵,也就是协方差矩阵:
协方差矩阵

P、Q都可以进行奇异值分解:

可见,协方差矩阵Q的奇异值分解和P相差无几,只是奇异值缩小了n倍,但是不妨碍奇异值之间的大小关系,所以在实际问题中,往往都是直接分解协方差矩阵。

python实现

基本步骤:

  • 对数据进行归一化处理(代码中并不是这么做的,而是直接减去均值)
  • 计算归一化后的数据集的协方差矩阵
  • 计算协方差矩阵的特征值和特征向量
  • 保留最重要的k个特征(通常k要小于n)。也能够自己制定。也能够选择一个阈值,然后通过前k个特征值之和减去后面n-k个特征值之和大于这个阈值,则选择这个k
  • 找出k个特征值相应的特征向量
  • 将m * n的数据集乘以k个n维的特征向量的特征向量(n * k),得到最后降维的数据。

1.引入包

import numpy as np
import pandas as pd
import matplotlib.pyplot as plt

2.定义一个均值函数

# 计算均值,要求输入数据为numpy的矩阵格式,行表示样本数,列表示特征    
def meanX(dataX):
    return np.mean(dataX,axis=0)
    #axis=0表示依照列来求均值。假设输入list,则axis=1

2.编写pca方法

"""
參数:
    - XMat:传入的是一个numpy的矩阵格式,行表示样本数,列表示特征    
    - k:表示取前k个特征值相应的特征向量
返回值:
    - finalData:參数一指的是返回的低维矩阵,相应于输入參数二
    - reconData:參数二相应的是移动坐标轴后的矩阵
"""
def pca(XMat, k):
    average = meanX(XMat) 
    m, n = np.shape(XMat)
    data_adjust = []
    avgs = np.tile(average, (m, 1))
    data_adjust = XMat - avgs
    covX = np.cov(data_adjust.T)   #计算协方差矩阵
    featValue, featVec=  np.linalg.eig(covX)  #求解协方差矩阵的特征值和特征向量
    index = np.argsort(-featValue) #依照featValue进行从大到小排序
    finalData = []
    if k > n:
        print "k must lower than feature number"
        return
    else:
        #注意特征向量时列向量。而numpy的二维矩阵(数组)a[m][n]中,a[1]表示第1行值
        selectVec = np.matrix(featVec.T[index[:k]]) #所以这里须要进行转置
        finalData = data_adjust * selectVec.T 
        reconData = (finalData * selectVec) + average  
    return finalData, reconData

3.载入数据集的函数

# 把txt文件读取为矩阵
def read_txt_to_array(path, delimiter='\t'):  # delimiter是数据分隔符
    fp = open(path, 'r', encoding='utf-8')
    string = fp.read()  # string是一行字符串,该字符串包含文件所有内容
    fp.close()
    row_list = string.splitlines()  # splitlines默认参数是‘\n’
    data_list = [[float(i) for i in row.strip().split(delimiter)] for row in row_list]
    return np.array(data_list)

4.可视化结果(k=2的情况)

def plotBestFit(data1, data2):    
    dataArr1 = np.array(data1)
    dataArr2 = np.array(data2)

    m = np.shape(dataArr1)[0]
    axis_x1 = []
    axis_y1 = []
    axis_x2 = []
    axis_y2 = []
    for i in range(m):
        axis_x1.append(dataArr1[i,0])
        axis_y1.append(dataArr1[i,1])
        axis_x2.append(dataArr2[i,0]) 
        axis_y2.append(dataArr2[i,1])                 
    fig = plt.figure()
    ax = fig.add_subplot(111)
    ax.scatter(axis_x1, axis_y1, s=50, c='red', marker='s')
    ax.scatter(axis_x2, axis_y2, s=50, c='blue')
    plt.xlabel('x1'); plt.ylabel('x2');
    plt.savefig("outfile.png")
    plt.show()  

5.测试

def main():    
    datafile = "data.txt"
    XMat = read_txt_to_array(datafile)
    k = 2
    return pca(XMat, k)
if __name__ == "__main__":
    finalData, reconMat = main()
    plotBestFit(finalData, reconMat)
©著作权归作者所有,转载或内容合作请联系作者
【社区内容提示】社区部分内容疑似由AI辅助生成,浏览时请结合常识与多方信息审慎甄别。
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

相关阅读更多精彩内容

友情链接更多精彩内容