以鸢尾花数据为例,用C语言实现PCA算法,并求出每个主成分的贡献率
时间: 2023-12-03 16:42:32 浏览: 153
PCA(Principal Component Analysis)是一种常用的数据降维方法,它可以将高维数据转化为低维数据,同时保留原始数据的主要信息。在这里,我们以鸢尾花数据为例,介绍如何用C语言实现PCA算法,并求出每个主成分的贡献率。
首先,我们需要读取鸢尾花数据集。假设我们已经将数据存储在一个二维数组`data`中,其中每一行代表一条数据,每一列代表一个特征。我们可以使用以下代码读取数据:
```c
float data[150][4]; // 鸢尾花数据集
FILE *fp;
fp = fopen("iris.data", "r");
for(int i=0; i<150; i++) {
fscanf(fp, "%f,%f,%f,%f,%s", &data[i][0], &data[i][1], &data[i][2], &data[i][3], label);
}
fclose(fp);
```
接下来,我们需要对数据进行归一化处理,使每个特征的均值为0,标准差为1。归一化后的数据可以提高PCA算法的效果。归一化处理的代码如下:
```c
float mean[4] = {0, 0, 0, 0}; // 均值
float std_dev[4] = {0, 0, 0, 0}; // 标准差
// 计算均值
for(int i=0; i<4; i++) {
for(int j=0; j<150; j++) {
mean[i] += data[j][i];
}
mean[i] /= 150;
}
// 计算标准差
for(int i=0; i<4; i++) {
for(int j=0; j<150; j++) {
std_dev[i] += (data[j][i] - mean[i]) * (data[j][i] - mean[i]);
}
std_dev[i] = sqrt(std_dev[i] / 149);
}
// 归一化数据
for(int i=0; i<4; i++) {
for(int j=0; j<150; j++) {
data[j][i] = (data[j][i] - mean[i]) / std_dev[i];
}
}
```
接下来,我们可以计算数据的协方差矩阵。协方差矩阵可以反映数据之间的相关性,是PCA算法的关键。协方差矩阵的计算代码如下:
```c
float cov[4][4] = {0}; // 协方差矩阵
// 计算协方差矩阵
for(int i=0; i<4; i++) {
for(int j=0; j<4; j++) {
for(int k=0; k<150; k++) {
cov[i][j] += (data[k][i] * data[k][j]);
}
cov[i][j] /= 149;
}
}
```
接下来,我们需要对协方差矩阵进行特征值分解,得到每个主成分的特征向量和特征值。特征向量代表每个主成分的方向,特征值代表该方向上的重要程度。特征值越大,代表该方向上的信息量越多,对应的主成分的贡献率也越高。特征值分解的代码如下:
```c
float eigenvalues[4]; // 特征值
float eigenvectors[4][4]; // 特征向量
// 计算特征值和特征向量
jacobi(cov, eigenvalues, eigenvectors, 4);
// 对特征值从大到小排序
sort_eigenvalues(eigenvalues, eigenvectors, 4);
```
最后,我们可以根据特征向量和数据计算出每个主成分,并计算每个主成分的贡献率。主成分的计算和贡献率的计算代码如下:
```c
float principal_components[4][150]; // 主成分
float contribution_rate[4]; // 贡献率
// 计算主成分
for(int i=0; i<4; i++) {
for(int j=0; j<150; j++) {
principal_components[i][j] = 0;
for(int k=0; k<4; k++) {
principal_components[i][j] += eigenvectors[i][k] * data[j][k];
}
}
}
// 计算贡献率
for(int i=0; i<4; i++) {
contribution_rate[i] = eigenvalues[i] / (eigenvalues[0] + eigenvalues[1] + eigenvalues[2] + eigenvalues[3]);
}
```
完整的PCA算法代码如下:
```c
#include <stdio.h>
#include <stdlib.h>
#include <math.h>
void jacobi(float a[][4], float d[], float v[][4], int n)
{
int j,iq,ip,i;
float tresh,theta,tau,t,sm,s,h,g,c,b[4],z[4];
for(ip=0;ip<n;ip++)
{
for(iq=0;iq<n;iq++)
v[ip][iq]=0.0;
v[ip][ip]=1.0;
}
for(ip=0;ip<n;ip++)
{
b[ip]=d[ip]=a[ip][ip];
z[ip]=0.0;
}
for(i=1;i<=50;i++)
{
sm=0.0;
for(ip=0;ip<n-1;ip++)
{
for(iq=ip+1;iq<n;iq++)
sm += fabs(a[ip][iq]);
}
if(sm == 0.0)
{
return;
}
if(i<4)
tresh=0.2*sm/(n*n);
else
tresh=0.0;
for(ip=0;ip<n-1;ip++)
{
for(iq=ip+1;iq<n;iq++)
{
g=100.0*fabs(a[ip][iq]);
if(i > 4 && (float)(fabs(d[ip])+g) == (float)fabs(d[ip])
&& (float)(fabs(d[iq])+g) == (float)fabs(d[iq]))
{
a[ip][iq]=0.0;
}
else if(fabs(a[ip][iq]) > tresh)
{
h=d[iq]-d[ip];
if((float)(fabs(h)+g) == (float)fabs(h))
{
t=(a[ip][iq])/h;
}
else
{
theta=0.5*h/(a[ip][iq]);
t=1.0/(fabs(theta)+sqrt(1.0+theta*theta));
if(theta < 0.0)
t = -t;
}
c=1.0/sqrt(1+t*t);
s=t*c;
tau=s/(1.0+c);
h=t*a[ip][iq];
z[ip] -= h;
z[iq] += h;
d[ip] -= h;
d[iq] += h;
a[ip][iq]=0.0;
for(j=0;j<ip;j++)
{
ROTATE(a,j,ip,j,iq)
}
for(j=ip+1;j<iq;j++)
{
ROTATE(a,ip,j,j,iq)
}
for(j=iq+1;j<n;j++)
{
ROTATE(a,ip,j,iq,j)
}
for(j=0;j<n;j++)
{
ROTATE(v,j,ip,j,iq)
}
}
}
}
for(ip=0;ip<n;ip++)
{
b[ip] += z[ip];
d[ip]=b[ip];
z[ip]=0.0;
}
}
}
void sort_eigenvalues(float eigenvalues[], float eigenvectors[][4], int n)
{
float temp_value;
float temp_vector[4];
for(int i=0; i<n-1; i++) {
for(int j=i+1; j<n; j++) {
if(eigenvalues[j] > eigenvalues[i]) {
temp_value = eigenvalues[i];
eigenvalues[i] = eigenvalues[j];
eigenvalues[j] = temp_value;
for(int k=0; k<n; k++) {
temp_vector[k] = eigenvectors[k][i];
eigenvectors[k][i] = eigenvectors[k][j];
eigenvectors[k][j] = temp_vector[k];
}
}
}
}
}
int main()
{
float data[150][4]; // 鸢尾花数据集
FILE *fp;
fp = fopen("iris.data", "r");
for(int i=0; i<150; i++) {
fscanf(fp, "%f,%f,%f,%f,%s", &data[i][0], &data[i][1], &data[i][2], &data[i][3], label);
}
fclose(fp);
float mean[4] = {0, 0, 0, 0}; // 均值
float std_dev[4] = {0, 0, 0, 0}; // 标准差
// 计算均值
for(int i=0; i<4; i++) {
for(int j=0; j<150; j++) {
mean[i] += data[j][i];
}
mean[i] /= 150;
}
// 计算标准差
for(int i=0; i<4; i++) {
for(int j=0; j<150; j++) {
std_dev[i] += (data[j][i] - mean[i]) * (data[j][i] - mean[i]);
}
std_dev[i] = sqrt(std_dev[i] / 149);
}
// 归一化数据
for(int i=0; i<4; i++) {
for(int j=0; j<150; j++) {
data[j][i] = (data[j][i] - mean[i]) / std_dev[i];
}
}
float cov[4][4] = {0}; // 协方差矩阵
// 计算协方差矩阵
for(int i=0; i<4; i++) {
for(int j=0; j<4; j++) {
for(int k=0; k<150; k++) {
cov[i][j] += (data[k][i] * data[k][j]);
}
cov[i][j] /= 149;
}
}
float eigenvalues[4]; // 特征值
float eigenvectors[4][4]; // 特征向量
// 计算特征值和特征向量
jacobi(cov, eigenvalues, eigenvectors, 4);
// 对特征值从大到小排序
sort_eigenvalues(eigenvalues, eigenvectors, 4);
float principal_components[4][150]; // 主成分
float contribution_rate[4]; // 贡献率
// 计算主成分
for(int i=0; i<4; i++) {
for(int j=0; j<150; j++) {
principal_components[i][j] = 0;
for(int k=0; k<4; k++) {
principal_components[i][j] += eigenvectors[i][k] * data[j][k];
}
}
}
// 计算贡献率
for(int i=0; i<4; i++) {
contribution_rate[i] = eigenvalues[i] / (eigenvalues[0] + eigenvalues[1] + eigenvalues[2] + eigenvalues[3]);
}
// 输出每个主成分的贡献率
for(int i=0; i<4; i++) {
printf("主成分%d的贡献率为:%.2f%%\n", i+1, contribution_rate[i]*100);
}
return 0;
}
```
阅读全文