用PCA简化数据

概述

  • 优点:降低数据的复杂性,识别最重要的多个特征
  • 缺点:不一定需要,且可能损失有用信息
  • 适用数据类型:数值型数据

数据维度

这是一个二维数据,不同的x值对应着不同的y值。


这是一个一维数据。y值始终不变,我们只需要关注x的不同取值。


这个图大家一看,估计二话不说就知道是二维数据了,很明显不同x值对应不同y值。但我要很遗憾地说错了。这是一维数据。看下图。

虽然在前面的坐标系下,数据看起来是二维的,但是我们通过旋转坐标轴到x'、y'的位置,可以发现它其实是一维的。

也可以说是,我们通过旋转坐标轴,使二维的数据变成了一维的数据(一个重要知识点),即实现了降维

上图也是一个一维数据。在实际情况下,我们很难有那么好的数据,可以用一条直线拟合,所以我们允许存在一些偏差。

主成分分析PCA

通过前面一些数据维度的小例子,其实我们已经了解了主成分分析(Principal Component Analysis,PCA)的一些重要知识点。下面简单介绍下PCA。

主成分,顾名思义就是主要成分。PCA的任务就是找到数据的主要成分来代替原数据,达到实现降维的目的。

主成分要求能够尽可能接近原本数据的分布,尽可能减少降维带来的信息损失。

在PCA中,数据从原来的坐标系转换到了新的坐标系,新坐标系的选择由数据本身决定。第一个新坐标轴(x轴)选择原始数据中方差最大的方向,第二个新坐标轴(y轴)选择和x轴正交且具有最大方差的方向。重复上述过程,次数为原始数据中特征的数目。最后我们会发现,大部分方差都包含在最前面的几个新坐标轴中(主成分),然后,我们就可以忽略剩下的坐标轴,实现了对数据的降维处理。

数据的最大方差给出了数据的最重要信息。

如上图,蓝色圆圈是我们的数据点,比较粗的红线是数据最大方差的方向。假设我们选取它作为新坐标轴,然后进行降维,将数据点都投影到轴上,那么点到线的垂直距离就是我们的信息损失,可以发现这个时候的信息损失是比较小的。

如果换成另一个轴,可以发现有些点到线的距离是比较大的,也就是我们的信息损失比较大。而我们应该选择信息损失尽可能小的坐标轴。

对PCA进行简单介绍后,接下来就是代码实现了。前面有提到,第一个主成分是从数据差异性最大(即方差最大)的方向提取出来的,第二个主成分则来自于差异性次大,且该方向与第一个主成分方向正交。通过数据集的协方差矩阵及其特征值分析,我们就可以求得这些主成分的值。

一旦得到协方差矩阵的特征向量,我们就可以保留最大的N个值。这些特征向量也给出了N个最重要特征的真实结构。我们可以通过将数据乘上这N个特征向量而将它转换到新的空间。

在Numpy中实现PCA

伪代码如下:

去除平均值
计算协方差矩阵
计算协方差矩阵的特征向量和特征值
将特征值从大到小排序
保留最前面的N个特征向量
将数据转换到上述N个特征向量构建的新空间中

代码如下:

import numpy as np

def loadDataSet(fileName, delim = '\t'):
    fr = open(fileName)
    stringArr = [line.strip().split(delim) for line in fr.readlines()]
    dataArr = [list(map(float, line)) for line in stringArr]
    return np.mat(dataArr)

def pca(dataMat, topNfeat = 99999):
    # 去平均值
    meanVals = np.mean(dataMat, axis=0)
    meanRemoved = dataMat - meanVals
    # 计算协方差矩阵
    covMat = np.cov(meanRemoved, rowvar=0)
    # 计算特征值并排序
    eigVals, eigVects = np.linalg.eig(np.mat(covMat))
    eigValInd = np.argsort(eigVals)
    eigValInd = eigValInd[:-(topNfeat + 1):-1]
    redEigVects = eigVects[:, eigValInd]
    # 将数据转换到新空间
    lowDDataMat = meanRemoved * redEigVects
    reconMat = (lowDDataMat * redEigVects.T) + meanVals
    return lowDDataMat, reconMat

pca()函数有两个参数,一个是数据集,一个是特征的个数。如果不指定topNfeat,那么就会返回前99999个特征,或者数据集的全部特征。

下面用一个有1000个数据点,2个特征的数据集来测试下。

dataMat = loadDataSet('testSet.txt')
lowDMat, reconMat = pca(dataMat, 1)

# 可视化原数据和降维数据
import matplotlib.pyplot as plt

fig = plt.figure()
ax = fig.add_subplot(111)
ax.scatter(dataMat[:,0].flatten().A[0], dataMat[:,1].flatten().A[0], marker='^', s = 90)
ax.scatter(reconMat[:,0].flatten().A[0], reconMat[:,1].flatten().A[0], marker='o', s = 50, c='red')
plt.show()

结果如下。


原始数据(三角)及第一主成分(圆点)

topNfeat设为2,或者不设时,数据是一样的。如下。

示例

这里有一份半导体制造的数据集,一共有590个特征,下面来试试看能不能进行降维处理。

该数据集有很多缺失值,所以需要先用平均值进行填充。

def replaceNanWithMean():
    dataMat = loadDataSet('secom.data', ' ')
    numFeat = dataMat.shape[1]
    for i in range(numFeat):
        # 计算所有非NaN的平均值
        meanVal = np.mean(dataMat[np.nonzero(~np.isnan(dataMat[:,i].A))[0], I])
        # 将所有NaN置为平均值
        dataMat[np.nonzero(np.isnan(dataMat[:,i].A))[0], i] = meanVal
    return dataMat

replaceNanWithMean()函数加载数据集并进行缺失值处理。

接下来,对数据集进行去均值,计算协方差矩阵,特征值分析。然后观察下特征值。

dataMat = replaceNanWithMean()
meanVals = np.mean(dataMat, axis=0)
meanRemoved = dataMat - meanVals
covMat = np.cov(meanRemoved, rowvar=0)
eigVals, eigVects = np.linalg.eig(np.mat(covMat))
eigVals

output:
array([ 5.34151979e+07,  2.17466719e+07,  8.24837662e+06,  2.07388086e+06,
        1.31540439e+06,  4.67693557e+05,  2.90863555e+05,  2.83668601e+05,
        2.37155830e+05,  2.08513836e+05,  1.96098849e+05,  1.86856549e+05,
        1.52422354e+05,  1.13215032e+05,  1.08493848e+05,  1.02849533e+05,
        1.00166164e+05,  8.33473762e+04,  8.15850591e+04,  7.76560524e+04,
        6.66060410e+04,  6.52620058e+04,  5.96776503e+04,  5.16269933e+04,
 ......
        0.00000000e+00,  0.00000000e+00,  0.00000000e+00,  0.00000000e+00,
        0.00000000e+00,  0.00000000e+00,  0.00000000e+00,  0.00000000e+00,
        0.00000000e+00,  0.00000000e+00,  0.00000000e+00,  0.00000000e+00,
        0.00000000e+00,  0.00000000e+00,  0.00000000e+00,  0.00000000e+00,
        0.00000000e+00,  0.00000000e+00,  0.00000000e+00,  0.00000000e+00,
        0.00000000e+00,  0.00000000e+00,  0.00000000e+00,  0.00000000e+00,
        0.00000000e+00,  0.00000000e+00,  0.00000000e+00,  0.00000000e+00,
        0.00000000e+00,  0.00000000e+00,  0.00000000e+00,  0.00000000e+00,
        0.00000000e+00,  0.00000000e+00,  0.00000000e+00,  0.00000000e+00,
        0.00000000e+00,  0.00000000e+00])

这里省略部分输出。

通过观察特征值,可以发现有超过20%的特征值都是0。这就意味着这些特征都是其他特征的副本,也就是说,它们可以通过其他特征来表示,而本身并没有提供额外信息。

接下来可视化前20个主成分占总方差的百分比。

eigValInd = np.argsort(eigVals)       
eigValInd = eigValInd[::-1]
sortedEigVals = eigVals[eigValInd]
total = sum(sortedEigVals)
varPercentage = sortedEigVals/total*100

plt.rcParams['font.sans-serif'] = ['SimHei']

fig = plt.figure()
ax = fig.add_subplot(111)
ax.plot(range(1, 21), varPercentage[:20], marker='^')
plt.xlabel('主成分数目')
plt.ylabel('方差的百分比')
plt.show()

结果如下。


可以看出大部分方差都包含在前面的几个主成分。

舍弃后面的主成分并不会损失太多信息。

如果只保留前6个,则数据集从590个特征简化成6个,大概实现了100:1的压缩。

下表给出了Top7和第20个主成分的方差百分比和累积方差百分比。从表中可以看出,前6个主成分就覆盖数据96.8%的方差,而前20个则覆盖了99.3%的方差。


小结

降维技术使得数据变得更易使用,并且能够去除数据中的噪声。

PCA可以从数据中识别主要特征,它是通过沿着数据最大方差方向旋转坐标轴来实现的。选择方差最大的方向作为第一条坐标轴,后续坐标轴则与前面的坐标轴正交。

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 202,723评论 5 476
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,080评论 2 379
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 149,604评论 0 335
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,440评论 1 273
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,431评论 5 364
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,499评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,893评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,541评论 0 256
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,751评论 1 296
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,547评论 2 319
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,619评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,320评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,890评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,896评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,137评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,796评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,335评论 2 342

推荐阅读更多精彩内容