PCA.cpp 5.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172
  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. // 计算压缩成多少维数据损失小于thd
  53. static int computeDim(MatrixXd &val, double thd)
  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() >= thd)
  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, variance_remain_);
  97. // qDebug() << "计算出压缩后的维度为:" << k;
  98. contribution_rate_ = val / val.sum();
  99. // 定义投影矩阵V
  100. MatrixXd V(C.cols(), k);
  101. // 投影矩阵的值为特征向量矩阵的前k维
  102. V = vec.rightCols(k);
  103. std::cout << "qian k wei: " << V << std::endl;
  104. MatrixXd normalRate(k, contribution_rate_.cols());
  105. normalRate = contribution_rate_.bottomRows(k);
  106. // 归一化
  107. normalRate = normalRate / normalRate.sum();
  108. scores_ = MatrixXd::Zero(normalRate.rows(), 1);
  109. scores_ = V * normalRate;
  110. weights_ = MatrixXd::Zero(normalRate.rows(), 1);
  111. weights_ = scores_ / scores_.sum();
  112. // std::cout << "scores_" << scores_ << std::endl;
  113. // std::cout << "weights_" << weights_ << std::endl;
  114. #if 0
  115. // 计算结果:把原始数据进行投影,每个数据点m维向量投影成k维向量
  116. MatrixXd res = X * V;
  117. // 还原的中心化矩阵Y
  118. MatrixXd Y(rows_, cols_);
  119. Y = res * V.transpose();
  120. // 样本每个维度都加上均值就是解压缩样本
  121. Y.rowwise() += meanvecRow;
  122. #endif
  123. }
  124. QVector<double> PCA::weights() const
  125. {
  126. QVector<double> res;
  127. for (int r = 0; r < weights_.rows(); ++r) {
  128. res << weights_(r, 0);
  129. }
  130. return res;
  131. }
  132. QVector<double> PCA::scores() const
  133. {
  134. QVector<double> res;
  135. for (int r = 0; r < scores_.rows(); ++r) {
  136. res << scores_(r, 0);
  137. }
  138. return res;
  139. }