PCA.cpp 4.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137
  1. #include "PCA.h"
  2. #include <QDebug>
  3. #include <iostream>
  4. using namespace Eigen;
  5. /**
  6. // QVector<QVector<double>> pdata = {
  7. // { 4.1, 0.04, 0.19, 0.01, 0.36, 0.05 }, { 3.9, 0.08, 0.38, 0.18, 0.19, 0.04 },
  8. // { 5.9, 0.07, 0.23, 0.04, 0.36, 0.07 }, { 4.9, 0.23, 1.03, 0.3, 1.4, 0.09 },
  9. // { 4, 0.14, 0.57, 0.11, 1.12, 0.05 }, { 6.1, 0.23, 1.19, 0.47, 0.93, 0.07 },
  10. // { 8.2, 0.21, 0.88, 0.2, 0.97, 0.1 }, { 6.5, 0.13, 0.8, 0.26, 0.52, 0.09 },
  11. // { 7.5, 0.16, 1.28, 0.88, 0.8, 0.12 }, { 6.8, 0.04, 0.17, 0.02, 0.24, 0.11 },
  12. // { 7.4, 0.05, 0.33, 0.06, 0.39, 0.1 }, { 5.7, 0.08, 0.6, 0.47, 0.46, 0.08 },
  13. // { 2.4, 0.14, 0.23, 0.04, 0.94, 0.02 }, { 2.2, 0.24, 0.35, 0.06, 1.78, 0.02 },
  14. // { 1.9, 0.11, 0.3, 0.1, 0.5, 0.04 }, { 1.8, 0.06, 0.28, 0.1, 0.29, 0.03 },
  15. // { 15.9, 0.2, 1.05, 0.21, 1.84, 0.12 }, { 18.9, 0.09, 0.82, 0.34, 0.58, 0.36 },
  16. // { 18.8, 0.12, 0.8, 0.39, 0.76, 0.26 }, { 16.8, 0.27, 1.74, 1.51, 1.78, 0.32 },
  17. // };
  18. QVector<QVector<double>> pdata = {
  19. { 66, 64, 65, 65, 65 }, { 65, 63, 63, 65, 64 }, { 57, 58, 63, 59, 66 }, { 67, 69, 65, 68, 64 },
  20. { 61, 61, 62, 62, 63 }, { 64, 65, 63, 63, 63 }, { 64, 63, 63, 63, 64 }, { 63, 63, 63, 63, 63 },
  21. { 65, 64, 65, 66, 64 }, { 67, 69, 69, 68, 67 }, { 62, 63, 65, 64, 64 }, { 68, 67, 65, 67, 65 },
  22. { 65, 65, 66, 65, 64 }, { 62, 63, 64, 62, 66 }, { 64, 66, 66, 65, 67 },
  23. };
  24. PCA pca(pdata, 0.0);
  25. pca.compute();
  26. */
  27. // 把输入的数据矩阵中心化并返回
  28. static void featurenormalize(MatrixXd &X)
  29. {
  30. // 计算每一维度均值
  31. VectorXd meanval = X.colwise().mean();
  32. // 计算每一标准差
  33. VectorXd stdval = ((X.rowwise() - meanval.transpose()).array().square().colwise().sum() / (X.rows() - 1)).sqrt();
  34. // 数据进行标准化
  35. X = (X.rowwise() - meanval.transpose()).array().rowwise() / stdval.transpose().array();
  36. }
  37. // 计算数据矩阵X 的协方差矩阵C
  38. static void computeCov(MatrixXd &X, MatrixXd &C)
  39. {
  40. // 计算协方差矩阵C = X^T * X / (n-1);
  41. C = X.adjoint() * X;
  42. C = C.array() / (X.rows() - 1);
  43. }
  44. // 计算协方差矩阵C的特征值val和特征向量vec
  45. static void computeEig(MatrixXd &C, MatrixXd &vec, MatrixXd &val)
  46. {
  47. // 计算特征值和特征向量,使用selfadjont按照对阵矩阵的算法去计算,可以让产生的vec和val按照有序排列
  48. SelfAdjointEigenSolver<MatrixXd> eig(C);
  49. vec = eig.eigenvectors();
  50. val = eig.eigenvalues();
  51. }
  52. // 计算压缩成多少维数据损失小于95%
  53. static int computeDim(MatrixXd &val)
  54. {
  55. int dim;
  56. double sum = 0;
  57. for (int i = val.rows() - 1; i >= 0; --i) {
  58. sum += val(i, 0);
  59. dim = i;
  60. if (sum / val.sum() >= 0.95)
  61. break;
  62. }
  63. return val.rows() - dim;
  64. }
  65. PCA::PCA(const QVector<QVector<double>> &source, double thd) : variance_remain_(thd)
  66. {
  67. rows_ = source.size(); // 行数
  68. cols_ = source.at(0).size(); // 列数
  69. X = MatrixXd::Zero(rows_, cols_);
  70. C = MatrixXd::Zero(cols_, cols_);
  71. for (int i = 0; i < rows_; ++i) {
  72. for (int j = 0; j < cols_; ++j) {
  73. X(i, j) = source[i][j];
  74. }
  75. }
  76. }
  77. void PCA::compute()
  78. {
  79. // 定义特征向量矩阵vec,特征值val
  80. MatrixXd vec, val;
  81. // 计算每一维度均值
  82. MatrixXd meanval = X.colwise().mean();
  83. RowVectorXd meanvecRow = meanval;
  84. // 零均值化
  85. featurenormalize(X);
  86. // std::cout << X << std::endl;
  87. // 计算协方差
  88. computeCov(X, C);
  89. // std::cout << C << std::endl;
  90. // 计算特征值和特征向量
  91. computeEig(C, vec, val);
  92. // 问题,特征顺序好像有问题,对不上指标顺序
  93. // std::cout << vec << std::endl;
  94. // std::cout << val << std::endl;
  95. // 计算损失率,确定降低维数(原本的n维向量即降低到dim维向量)
  96. int k = computeDim(val);
  97. qDebug() << "计算出压缩后的维度为:" << k;
  98. // 定义投影矩阵V
  99. MatrixXd V(C.cols(), k);
  100. // 投影矩阵的值为特征向量矩阵的前k维
  101. V = vec.rightCols(k);
  102. // 计算结果:把原始数据进行投影,每个数据点m维向量投影成k维向量
  103. MatrixXd res = X * V;
  104. // 还原的中心化矩阵Y
  105. MatrixXd Y(rows_, cols_);
  106. Y = res * V.transpose();
  107. // 样本每个维度都加上均值就是解压缩样本
  108. Y.rowwise() += meanvecRow;
  109. // std::cout << Y << std::endl;
  110. }