第二次作业:主成分分析:步骤、应用及代码实现。代码可以用任何你熟悉的编程语言。
主成分分析:
一、基本概念和相关理解:
1.基本概念
在用统计方法研究多变量的课题时,变量个数太多就会增加课题的复杂性。所以我们为了使变量个数少且获得的信息量大,要采用主成分分析。
在很多情况下,变量之间是有一定的相关关系的,当两个变量之间有一定相关关系时,可以解释为这两个变量反应此课题的信息有一定的重叠。
主成分分析对于原先提出的所有变量,将重复的变量删去多余,建立尽可能少的新变量,且这些新变量两两不相关的,同时这些新变量在反映课题的信息方面尽可能保持原有的信息。
而主成分分析,便是一种统计方法。通过正交变换将一组可能存在相关性的变量转换为一组线性不相关的变量,转换后的这组变量就被称之为主成分。
2.具体理解
以实际应用为例,比如我们在描述物体的性质时,当我们观察的物体只需要用少数几个性质来区分的时候,例如只有他的温度,体积,质量,表面积时,那么我们可以很容易的根据这几个性质来区分,来对物体进行分析。但如果,我们所需要观察的性质增加时,如果不对这些性质进行归类化进行分析,则会大大增加复杂度。所以在这种情况下,我们便需要将复杂的数据降维,使得降维之后的变量减少,并尽量保证互不相关。
从线性代数的角度来看,PCA的目标就是使用另一组基去重新描述得到的数据空间。
二、主成分分析的步骤:
1.数据矩阵化:
首先,我们假设我们要分析的样本有n个特质,而同时该样本共有k个,则我们可以不妨可以用如下的一个矩阵来进行表示,有多少行就有多少个样本,有多少列就有多少个特质:
而由于,我们分析的对象是其特质,故可以将矩阵简化,即将每一列均用一个列向量来表示,这样我们就可以具体的来分析其成分了。
2.对每个特征求均值,并进行去均值处理,即用原来的数据减去均值,得出来的新数据们得出一个新的矩阵:
3.计算得出来的新矩阵的协方差矩阵,并由其协方差矩阵计算其特征值和特征向量。(如下图所示:)4.对特征值进行排序:即根据特征值的大小进行降序排序,此时我们便可以选取主成分分析之后样本的维数了。即若我们原本是一个有十个特征的样本,在进行了特征值排序之后,我们就可以根据特征值的大小和特征值所对应的特征来进行选取2或3(举个栗子),从而实现降维操作:
如图所述:若我们想利用PCA将其将为一个维度为b的样本进行分析,那么我们便可以对特征向量矩阵先取其前b行,再令其与原始数据矩阵Z相乘,便得到了一个新的矩阵Y,也就得出了降维之后的数据。
5.步骤总结:从线性代数的角度来理解PCA就是利用线性变换,使得利用另一组基来重新描述数据空间。通过降维的操作,提取出新的互不相关的主成分,来使数据的分析更为简洁。
而PCA从图形角度来思考(如上图所示),如果我们把所有的样点全部绘制在坐标轴内,坐标轴代表物体的一个特质,而图内的每一个点在坐标轴上的投影就代表了其在该特性上的程度大小。(本图以三位坐标系为例,实际操作中会有更多的维度,所以甚至并不能画出,但是思想是这个意思)
我们进行PCA操作的时候,其实就是一个对数据坐标轴进行旋转的操作,将新生成的坐标轴(们)旋转到数据角度上那些最重要的方向,进而通过特征值分析,确定出需要保留的主成分个数,舍弃其他主成分,实现数据的降维。
三、主成分分析的应用:
就我的理解而言,主成分分析可以应用于大多数的数据分析当中,尤其是当数据量极为庞大的时候,我们可以利用主成分分析的方法来对数据进行降维,从而降低数据分析的工作量,节约进行数据分析的时间。对于本专业领域而言,在图像处理,视频处理等方面均有所应用。
四、主成分分析的C++实现:(代码略长,因为在前面附上了好多网上找的用来算特征值和特征向量的函数,从加粗的int main开始起是我写的主函数。
目测matlab确实要好整的多,用C++应该也可以是有一个Eigen的数据库应该可以简便运算,不过最后还是硬算得,所以就很长。。)
#include
#include <math.h>
#include
#include
#include
using namespace std;
#define N 10
int n=10,flag;
void power();
void matrixFact();
void Findmax();
void newZ();
void printResult();
double m,next_m,eclipse,sum,A[N][N],z[N],y[N],next_z[N];
void reckonResidue()
{
float sum=0;
for (int i=1;i<=n;i++)
{
sum+=abs(z[i]-next_z[i]);
}
if (sum<0.001)
{
flag=1;
}
return ;
}
void readData()
{
for (int i=1;i<=n;i++)
{
for (int j=1;j<=n;j++)
{
cin>>A[i][j];
}
}
return ;
}
void power()
{
m=next_m;
flag=0;
eclipse=1e-5;
for (int i=1;i<=n;i++)
{
z[i]=1;
next_z[i]=1;
}
do
{
sum=0;
for (int i=1;i<=n;i++)
z[i]=next_z[i];
matrixFact();
Findmax();
newZ();
reckonResidue();
}while (!flag);
printResult();
return ;
}
void matrixFact()
{
for (int i=1;i<=n;i++)
{
sum=0;
for (int j=1;j<=n;j++)
{
sum+=A[i][j]*z[j];
}
y[i]=sum;
}
for (int i=1;i<=n;i++)
{
cout<<y[i]<<" ";
}
cout<<endl;
return ;
}
void Findmax()
{
m=0;
for (int i=1;i<=n;i++)
{
if (abs(m)<abs(y[i]))
{
m=y[i];
}
}
cout<<“m:”<<m<<endl;
return ;
}
void newZ()
{
for (int i=1;i<=n;i++)
{
next_z[i]=y[i]/m;
}
return ;
}
void printResult()
{
cout<<“矩阵A的最大特征值为:”<<m<<endl;
cout<<“其对应的特征向量为:(”;
for (int i=1;i<=n;i++)
{
cout<<next_z[i];
if (i!=n)
{
cout<<",";
}
}
cout<<")"<<endl;
return ;
}
void matrixx(double A[N][N],double x[N],double v[N])
{
for(int i=0;i<N;i++)
{
v[i]=0;
for(int j=0;j<N;j++)
v[i]+=A[i][j]*x[j];
}
}
double slove(double v[N])
{
double max;
for(int i=0;i<N-1;i++) max=v[i]>v[i+1]?v[i]:v[i+1];
return max;
}
int main()
{
int item = 5;//假设一共要检测5个样品
int catagory = 10;//这五个样品都有十个参数
double a[5][10]={{1,2,3,4,5,6,7,8,9,10},
{1,3,5,7,9,11,13,15,17,19},
{2,3,5,7,11,13,19,17,23,29},
{31,37,41,43,47,53,59,61,67,71},
{49,39,2,45,45,6,67,45,66,89}};//这是列出来的矩阵(按这样列出来方便看列向量)
double average[10];//设置一个数组,代表每一种参数的五种样品平均值
for(int i=0;i<10;i++)
{
average[i]=(a[0][i]+a[1][i]+a[2][i]+a[3][i]+a[4][i])/5;//计算平均值
cout<<average[i]<<" "<<endl;//输出平均值数组(便与调试需要)
}
for(int i=0;i<10;i++)
{
for(int j=0;j<5;j++)
{
a[j][i]=a[j][i]-average[i];//将原来的数组减去平均值数组,从而便于计算协方差矩阵
}
}
for ( int i = 0; i < 5; i++ )
{
for ( int j = 0; j < 10; j++ )
{
cout <<a[i][j]<<" ";
}
cout<<endl;
}//用于调试
double conv[10][10]={{1,2,3,4,5,6,7,8,9,10},
{1,3,5,7,9,11,13,15,17,19},
{2,3,5,7,11,13,19,17,23,29},
{31,37,41,43,47,53,59,61,67,71},
{1,2,3,4,5,6,7,8,9,10},
{1,3,5,7,9,11,13,15,17,19},
{2,3,5,7,11,13,19,17,23,29},
{31,37,41,43,47,53,59,61,67,71},
{31,37,41,43,47,53,59,61,67,71},
{42,33,32,45,45,56,67,45,66,89}};//设置一个10*10的矩阵作为协方差矩阵(初始化的值是随便写的)
for (int i=0;i<10;i++) //协方差矩阵计算
{
for (int j=0;j<10;j++)
{
for (int k=0;k<5;k++)
{
conv[i][j] += a[k][i]*a[k][j];
}
}
}
for ( int i = 0; i < 10; i++ )
{
for ( int j = 0; j < 10; j++ )
{
cout <<conv[i][j]<<" ";
}
cout<<endl;
}//用于调试
for (int i=1;i<=n;i++)
{
for (int j=1;j<=n;j++)
{
A[i][j]=conv[i][j];
}
}
power();//此处调用了上面的部分函数,函数来源CSDN,用于求解矩阵特征值和特征向量
double p[10]={0,0,0,0,0,0,0,0,0,0};//若我们在实际应用中,主成分分析将原来的十个特征化为一个特征,则我们使其用特征向量p来表示(此处直接取了特征向量矩阵的第一行)
for (int i=1;i<=n;i++)
{
p[i]=next_z[i];
}
double Y[5]={1,1,1,1,1};//这就是最终得出来的那个矩阵
for(int i=0;i<5;i++)
{
for(int j=0;j<10;j++)
{
Y[i]+=p[i]*a[i][j];
}
}//计算Y的每一个值,即求出降维之后的数据
cout<<“降维之后的数据:”<<endl;
cout<<"(";
for(int i=0;i<4;i++)
{
cout<<Y[i]<<",";
}
cout<<Y[4]<<")";
return 0;
}
程序运行结果: