1500字范文,内容丰富有趣,写作好帮手!
1500字范文 > 数据降维:主成分分析法(PCA)

数据降维:主成分分析法(PCA)

时间:2018-10-11 06:20:03

相关推荐

数据降维:主成分分析法(PCA)

手 执 烟 火 以 谋 生 , 心 怀 诗 意 以 谋 爱

目录

1.原理介绍

2.步骤详解

2.1 获取数据

2.2 数据中心化 (标准化)

2.3 求协方差矩阵

2.4计算协方差矩阵的特征值和特征向量

2.5 确定主成分个数

2.6 计算主成分

3.案例分析

3.1题目简介

3.2 读取数据

3.3中心化数据

3.4 求协方差矩阵

3.5 计算特征值

3.6 计算特征值对应的特征向量

3.7 确定主成分个数并计算主成分矩阵

3.8 计算主成分(降维后数据)

4.完整代码(Java)

4.1 方法类 pca.java

4.2 主类 pcamain.java

此文为学习过程所写,如有错误,敬请指正。

1.原理介绍

在很多场景中需要对多变量数据进行观测,在一定程度上增加了数据采集的工作量。更重要的是:多变量之间可能存在相关性,从而增加了问题分析的复杂性。如果对每个指标进行单独分析,其分析结果往往是孤立的,不能完全利用数据中的信息,因此盲目减少指标会损失很多有用的信息,从而产生错误的结论。

所以需要找到一种合理的方法,在减少需要分析的指标同时,尽量减少原指标包含信息的损失,以达到对所收集数据进行全面分析的目的。由于各变量之间存在一定的相关关系,因此可以考虑将关系紧密的变量变成尽可能少的新变量,使这些新变量是两两不相关的,那么就可以用较少的综合指标分别代表存在于各个变量中的各类信息

主成分分析是一种最常用的无监督降维方法,通过降维技术把多个变量化为少数几个主成分的统计分析方法。这些主成分能够反映原始变量的绝大部分信息,它们通常表示为原始变量的某种线性组合。

2.步骤详解

2.1 获取数据

假设现有一组数据,有m条数据,每条数据都有n个评价指标,构成了m*n的原始数据矩阵,即为X,每个变量对应的数据记为X1,X2,X3......Xn。

2.2 数据中心化 (标准化)

不同评价指标往往具有不同的量纲和量纲单位,这样的情况会影响到数据分析的结果,为了消除指标之间的量纲影响,需要进行数据标准化处理,以解决数据指标之间的可比性。原始数据经过数据标准化处理后,各指标处于同一数量级,适合进行综合对比评价。

在这里我们采用零均值法(z-score)对数据进行处理,得到均值为0,标准差为1的服从标准正态分布的数据。

其中,表示第j个指标的样本均值,

表示第j个指标的标准差,仍记中心化后数据矩阵为X。

2.3 求协方差矩阵

对中心化后数据求其协方差矩阵,记为R,则

或者另一种方法:

2.4计算协方差矩阵的特征值和特征向量

通过求协方差矩阵的特征方程:

解得其特征值有

对应的特征向量依次为:

2.5 确定主成分个数

设定一个贡献率阈值,即前p个主成分特征值的累计贡献率高于该值时即可认为这p个主成分可以表示原来n个变量,一般取0.8,0.85,0.9,0.95,0.99等。

2.6 计算主成分

在得到了主成分个数后,就可以利用前p个特征值对应的特征向量对主成分(降维后的数据)进行计算。

3.案例分析

3.1题目简介

已知判断某一水域水质情况的好坏时可以通过x1-x9共9种指标来评判,现有A-K共11条河流的指标测量情况,但是由于指标过多导致在评判以及确定每个指标的重要程度时带来了很大麻烦,所以请你利用一定的数学方法在尽量不损失原有数据信息的前提下减少评判指标。

3.2 读取数据

利用jxl包从Excel读取数据并输出

//读取数据public double[][] read(String filepath) throws IOException, BiffException,WriteException {//创建输入流InputStream stream = new FileInputStream(filepath);//获取Excel文件对象Workbook rwb = Workbook.getWorkbook(stream);//获取文件的指定工作表 默认的第一个Sheet sheet = rwb.getSheet("Sheet1");rows = sheet.getRows();cols = sheet.getColumns();double[][] orig = new double[rows][cols];//row为行for(int i=0;i<sheet.getRows();i++) {for(int j=0;j<sheet.getColumns();j++) {String[] str = new String[sheet.getColumns()];Cell cell = null;cell = sheet.getCell(j,i); str[j] = cell.getContents();orig[i][j] = Double.valueOf(str[j]);//original.set(i, j, orig[i][j]);}}return orig;}

输出:

3.3中心化数据

采用均值0化方法对原数据中心化处理

/*** * 使每个样本的均值为0* * @param primary* 原始二维数组矩阵* @return averageArray 中心化后的矩阵*/public double[][] changeAverageToZero(double[][] primary) {int n = primary.length;int m = primary[0].length;double[] sum = new double[m];double[] average = new double[m];double[][] averageArray = new double[n][m];for (int i = 0; i < m; i++) {for (int j = 0; j < n; j++) {sum[i] += primary[j][i];}average[i] = sum[i] / n;}for (int i = 0; i < m; i++) {for (int j = 0; j < n; j++) {averageArray[j][i] = primary[j][i] - average[i];}}return averageArray;}

输出:

3.4 求协方差矩阵

/*** * 计算协方差矩阵* * @param matrix* 中心化后的矩阵* @return result 协方差矩阵*/public double[][] getVarianceMatrix(double[][] matrix) {int n = matrix.length;// 行数int m = matrix[0].length;// 列数double[][] result = new double[m][m];// 协方差矩阵for (int i = 0; i < m; i++) {for (int j = 0; j < m; j++) {double temp = 0;for (int k = 0; k < n; k++) {temp += matrix[k][i] * matrix[k][j];}result[i][j] = temp / (n - 1);}}/*或者用以下方法计算:X的转置乘以X,在除以行数Matrix X=new Matrix(matrix);result = X.transpose().times(X).getArray();for (int i = 0; i < m; i++) {for (int j = 0; j < m; j++) {result[i][j] = result[i][j] / n;}}*/return result;}

输出:

3.5 计算特征值

对角线上数据为特征值

/*** 求特征值矩阵* * @param matrix* 协方差矩阵* @return result 向量的特征值二维数组矩阵*/public double[][] getEigenvalueMatrix(double[][] matrix) {Matrix A = new Matrix(matrix);// 由特征值组成的对角矩阵,eig()获取特征值//A.eig().getD().print(10, 6);double[][] result = A.eig().getD().getArray();return result;}

输出:

3.6 计算特征值对应的特征向量

/*** 标准化矩阵(特征向量矩阵)* * @param matrix* 特征值矩阵* @return result 标准化后的二维数组矩阵*/public double[][] getEigenVectorMatrix(double[][] matrix) {Matrix A = new Matrix(matrix);//A.eig().getV().print(6, 2);double[][] result = A.eig().getV().getArray();return result;}

输出:

3.7 确定主成分个数并计算主成分矩阵

/*** 寻找主成分* * @param prinmaryArray* 原始二维数组数组* @param eigenvalue* 特征值二维数组* @param eigenVectors* 特征向量二维数组* @return principalMatrix 主成分矩阵*/public Matrix getPrincipalComponent(double[][] primaryArray,double[][] eigenvalue, double[][] eigenVectors) {Matrix A = new Matrix(eigenVectors);// 定义一个特征向量矩阵double[][] tEigenVectors = A.transpose().getArray();// 特征向量转置Map<Integer, double[]> principalMap = new HashMap<Integer, double[]>();// key=主成分特征值,value=该特征值对应的特征向量TreeMap<Double, double[]> eigenMap = new TreeMap<Double, double[]>(Collections.reverseOrder());// key=特征值,value=对应的特征向量;初始化为翻转排序,使map按key值降序排列double total = 0;// 存储特征值总和int index = 0, n = eigenvalue.length;double[] eigenvalueArray = new double[n];// 把特征值矩阵对角线上的元素放到数组eigenvalueArray里for (int i = 0; i < n; i++) {for (int j = 0; j < n; j++) {if (i == j)eigenvalueArray[index] = eigenvalue[i][j];}index++;}for (int i = 0; i < tEigenVectors.length; i++) {double[] value = new double[tEigenVectors[0].length];value = tEigenVectors[i];eigenMap.put(eigenvalueArray[i], value);}// 求特征总和for (int i = 0; i < n; i++) {total += eigenvalueArray[i];}// 选出前几个主成分double temp = 0;int principalComponentNum = 0;// 主成分数List<Double> plist = new ArrayList<Double>();// 主成分特征值for (double key : eigenMap.keySet()) {if (temp / total <= threshold) {temp += key;plist.add(key);principalComponentNum++;}}System.out.println("\n" + "当前阈值: " + threshold);System.out.println("取得的主成分数: " + principalComponentNum + "\n");System.out.println("主成分(特征向量)对应的特征值和其贡献率依次为:");for (int i = 0; i<principalComponentNum; i++) {System.out.println(plist.get(i)+"\t"+plist.get(i)*100/total+"%");}// 往主成分map里输入数据for (int i = 0; i < plist.size(); i++) {if (eigenMap.containsKey(plist.get(i))) {principalMap.put(i, eigenMap.get(plist.get(i)));}}// 把map里的值存到二维数组里double[][] principalArray = new double[principalMap.size()][];Iterator<Entry<Integer, double[]>> it = principalMap.entrySet().iterator();for (int i = 0; it.hasNext(); i++) {principalArray[i] = it.next().getValue();}/*double[][] principalArray1 = new double[principalArray.length][principalArray[0].length+1];for(int i=0;i<principalArray1.length ;i++) {for(int j=0;j<principalArray1.length ;j++) {if(j==0) {principalArray1[i][j] = plist.get(i);}else {principalArray1[i][j] = principalArray[i][j-1];}}}*/Matrix principalMatrix = new Matrix(principalArray);return principalMatrix;}

输出:

3.8 计算主成分(降维后数据)

/*** 矩阵相乘* * @param primary* 原始二维数组* * @param matrix* 主成分矩阵* * @return result 结果矩阵*/public Matrix getResult(double[][] primary, Matrix matrix) {Matrix primaryMatrix = new Matrix(primary);Matrix result = primaryMatrix.times(matrix.transpose());return result;}

输出:

4.完整代码(Java)

程序中对Excel数据进行操作的库为jxl,矩阵运算库为jama。

4.1 方法类 pca.java

package PCA;import java.io.FileInputStream;import java.io.IOException;import java.io.InputStream;import java.util.ArrayList;import java.util.Collections;import java.util.HashMap;import java.util.Iterator;import java.util.List;import java.util.Map;import java.util.Map.Entry;import java.util.TreeMap;import Jama.Matrix;import jxl.Cell;import jxl.Sheet;import jxl.Workbook;import jxl.read.biff.BiffException;import jxl.write.WriteException;/** 算法步骤:* 1)将原始数据按列组成n行m列矩阵X* 2)特征中心化。即每一维的数据都减去该维的均值,使每一维的均值都为0* 3)求出协方差矩阵* 4)求出协方差矩阵的特征值及对应的特征向量* 5)将特征向量按对应的特征值大小从上往下按行排列成矩阵,取前k行组成矩阵p* 6)Y=PX 即为降维到k维后的数据*/public class pca {private static final double threshold = 0.99;// 特征值阈值int rows,cols;//输出二维矩阵public void matrixoutput(double[][] x) {for(int i=0;i<x.length;i++) {for(int j=0;j<x[0].length;j++) {System.out.print(x[i][j]+" ");}System.out.println();}}//读取数据public double[][] read(String filepath) throws IOException, BiffException,WriteException {//创建输入流InputStream stream = new FileInputStream(filepath);//获取Excel文件对象Workbook rwb = Workbook.getWorkbook(stream);//获取文件的指定工作表 默认的第一个Sheet sheet = rwb.getSheet("Sheet1");rows = sheet.getRows();cols = sheet.getColumns();double[][] orig = new double[rows][cols];//row为行for(int i=0;i<sheet.getRows();i++) {for(int j=0;j<sheet.getColumns();j++) {String[] str = new String[sheet.getColumns()];Cell cell = null;cell = sheet.getCell(j,i); str[j] = cell.getContents();orig[i][j] = Double.valueOf(str[j]);//original.set(i, j, orig[i][j]);}}return orig;}/*** * 使每个样本的均值为0* * @param primary* 原始二维数组矩阵* @return averageArray 中心化后的矩阵*/public double[][] changeAverageToZero(double[][] primary) {int n = primary.length;int m = primary[0].length;double[] sum = new double[m];double[] average = new double[m];double[][] averageArray = new double[n][m];for (int i = 0; i < m; i++) {for (int j = 0; j < n; j++) {sum[i] += primary[j][i];}average[i] = sum[i] / n;}for (int i = 0; i < m; i++) {for (int j = 0; j < n; j++) {averageArray[j][i] = primary[j][i] - average[i];}}return averageArray;}/*** * 计算协方差矩阵* * @param matrix* 中心化后的矩阵* @return result 协方差矩阵*/public double[][] getVarianceMatrix(double[][] matrix) {int n = matrix.length;// 行数int m = matrix[0].length;// 列数double[][] result = new double[m][m];// 协方差矩阵for (int i = 0; i < m; i++) {for (int j = 0; j < m; j++) {double temp = 0;for (int k = 0; k < n; k++) {temp += matrix[k][i] * matrix[k][j];}result[i][j] = temp / (n - 1);}}/*或者用以下方法计算:X的转置乘以X,在除以行数Matrix X=new Matrix(matrix);result = X.transpose().times(X).getArray();for (int i = 0; i < m; i++) {for (int j = 0; j < m; j++) {result[i][j] = result[i][j] / n;}}*/return result;}/*** 求特征值矩阵* * @param matrix* 协方差矩阵* @return result 向量的特征值二维数组矩阵*/public double[][] getEigenvalueMatrix(double[][] matrix) {Matrix A = new Matrix(matrix);// 由特征值组成的对角矩阵,eig()获取特征值//A.eig().getD().print(10, 6);double[][] result = A.eig().getD().getArray();return result;}/*** 标准化矩阵(特征向量矩阵)* * @param matrix* 特征值矩阵* @return result 标准化后的二维数组矩阵*/public double[][] getEigenVectorMatrix(double[][] matrix) {Matrix A = new Matrix(matrix);//A.eig().getV().print(6, 2);double[][] result = A.eig().getV().getArray();return result;}/*** 寻找主成分* * @param prinmaryArray* 原始二维数组数组* @param eigenvalue* 特征值二维数组* @param eigenVectors* 特征向量二维数组* @return principalMatrix 主成分矩阵*/public Matrix getPrincipalComponent(double[][] primaryArray,double[][] eigenvalue, double[][] eigenVectors) {Matrix A = new Matrix(eigenVectors);// 定义一个特征向量矩阵double[][] tEigenVectors = A.transpose().getArray();// 特征向量转置Map<Integer, double[]> principalMap = new HashMap<Integer, double[]>();// key=主成分特征值,value=该特征值对应的特征向量TreeMap<Double, double[]> eigenMap = new TreeMap<Double, double[]>(Collections.reverseOrder());// key=特征值,value=对应的特征向量;初始化为翻转排序,使map按key值降序排列double total = 0;// 存储特征值总和int index = 0, n = eigenvalue.length;double[] eigenvalueArray = new double[n];// 把特征值矩阵对角线上的元素放到数组eigenvalueArray里for (int i = 0; i < n; i++) {for (int j = 0; j < n; j++) {if (i == j)eigenvalueArray[index] = eigenvalue[i][j];}index++;}for (int i = 0; i < tEigenVectors.length; i++) {double[] value = new double[tEigenVectors[0].length];value = tEigenVectors[i];eigenMap.put(eigenvalueArray[i], value);}// 求特征总和for (int i = 0; i < n; i++) {total += eigenvalueArray[i];}// 选出前几个主成分double temp = 0;int principalComponentNum = 0;// 主成分数List<Double> plist = new ArrayList<Double>();// 主成分特征值for (double key : eigenMap.keySet()) {if (temp / total <= threshold) {temp += key;plist.add(key);principalComponentNum++;}}System.out.println("\n" + "当前阈值: " + threshold);System.out.println("取得的主成分数: " + principalComponentNum + "\n");System.out.println("主成分(特征向量)对应的特征值和其贡献率依次为:");for (int i = 0; i<principalComponentNum; i++) {System.out.println(plist.get(i)+"\t"+plist.get(i)*100/total+"%");}// 往主成分map里输入数据for (int i = 0; i < plist.size(); i++) {if (eigenMap.containsKey(plist.get(i))) {principalMap.put(i, eigenMap.get(plist.get(i)));}}// 把map里的值存到二维数组里double[][] principalArray = new double[principalMap.size()][];Iterator<Entry<Integer, double[]>> it = principalMap.entrySet().iterator();for (int i = 0; it.hasNext(); i++) {principalArray[i] = it.next().getValue();}/*double[][] principalArray1 = new double[principalArray.length][principalArray[0].length+1];for(int i=0;i<principalArray1.length ;i++) {for(int j=0;j<principalArray1.length ;j++) {if(j==0) {principalArray1[i][j] = plist.get(i);}else {principalArray1[i][j] = principalArray[i][j-1];}}}*/Matrix principalMatrix = new Matrix(principalArray);return principalMatrix;}/*** 矩阵相乘* * @param primary* 原始二维数组* * @param matrix* 主成分矩阵* * @return result 结果矩阵*/public Matrix getResult(double[][] primary, Matrix matrix) {Matrix primaryMatrix = new Matrix(primary);Matrix result = primaryMatrix.times(matrix.transpose());return result;}}

4.2 主类 pcamain.java

package PCA;import Jama.Matrix;import jxl.read.biff.BiffException;import jxl.write.WriteException;import java.io.IOException;public class pcamain {public static void main(String[] args) throws IOException, BiffException, WriteException {// TODO Auto-generated catch block//SelectData selectData = new SelectData();pca pca = new pca();//获得样本集double[][] primaryArray = pca.read("pca.xls");System.out.println("--------------------原始数据矩阵---------------------");//pca.matrixoutput(primaryArray);Matrix A=new Matrix(primaryArray);A.print(10, 3);double[][] averageArray = pca.changeAverageToZero(primaryArray);System.out.println();System.out.println("--------------------均值0化后的数据-------------------");System.out.println(averageArray.length + "行," + averageArray[0].length + "列");//pca.matrixoutput(averageArray);Matrix B=new Matrix(averageArray);B.print(10, 3);System.out.println();System.out.println("---------------------协方差矩阵-----------------------");double[][] varMatrix = pca.getVarianceMatrix(averageArray);//pca.matrixoutput(varMatrix);Matrix C=new Matrix(varMatrix);C.print(10, 3);System.out.println();System.out.println("------------------特征值矩阵--------------------------");double[][] eigenvalueMatrix = pca.getEigenvalueMatrix(varMatrix);//pca.matrixoutput(eigenvalueMatrix);Matrix D=new Matrix(eigenvalueMatrix);D.print(15, 10);System.out.println();System.out.println("------------------特征向量矩阵-----------------------");double[][] eigenVectorMatrix = pca.getEigenVectorMatrix(varMatrix);//pca.matrixoutput(eigenVectorMatrix);Matrix E=new Matrix(eigenVectorMatrix);E.print(10, 3);System.out.println();System.out.println("-------------------主成分矩阵-------------------------");Matrix principalMatrix = pca.getPrincipalComponent(primaryArray, eigenvalueMatrix, eigenVectorMatrix);principalMatrix.transpose().print(10, 3);System.out.println();System.out.println("--------------------降维后的矩阵------------------------");Matrix resultMatrix = pca.getResult(primaryArray, principalMatrix);int c = resultMatrix.getColumnDimension(); //列数int r = resultMatrix.getRowDimension();//行数System.out.print(r + "行," + c + "列");resultMatrix.print(10, 3);}}

本内容不代表本网观点和政治立场,如有侵犯你的权益请联系我们处理。
网友评论
网友评论仅供其表达个人看法,并不表明网站立场。