Isis 3 Programmer Reference
PrincipalComponentAnalysis.cpp
1 
6 /* SPDX-License-Identifier: CC0-1.0 */
7 #include <QString>
8 
9 #include "PrincipalComponentAnalysis.h"
10 #include "jama/jama_eig.h"
11 #include "jama/jama_lu.h"
12 
13 using namespace std;
14 
15 namespace Isis {
17  PrincipalComponentAnalysis::PrincipalComponentAnalysis(const int n) {
18  p_dimensions = n;
19  p_statistics.clear();
20  p_statistics.resize(n * n);
21  for(int i = 0; i < n; i++) {
22  for(int j = 0; j <= i; j++) {
23  p_statistics[n*i+j] = new Isis::MultivariateStatistics();
24  p_statistics[n*j+i] = p_statistics[n*i+j];
25  }
26  }
27 
28  p_hasTransform = false;
29  };
30 
31  // create a pca object from the trabsform matrix
32  PrincipalComponentAnalysis::PrincipalComponentAnalysis(TNT::Array2D<double> transform) {
33  if(transform.dim1() != transform.dim2()) {
34  std::string m = "Illegal transform matrix";
35  throw IException(IException::Programmer, m, _FILEINFO_);
36  }
37 
38  p_dimensions = transform.dim1();
39  p_transform = transform;
40  ComputeInverse();
41 
42  p_hasTransform = true;
43  }
44 
45  // Add data for all dimensions
46  // Note: the data should be stored as an array containing
47  // the first dimension in order, then the second, ...
48  void PrincipalComponentAnalysis::AddData(const double *data, const unsigned int count) {
49  // If this PCA object has a transform matrix
50  // we cannot add more data
51  if(p_hasTransform) {
52  std::string m = "Cannot add data to a PCA that has a defined transform matrix";
53  throw IException(IException::Programmer, m, _FILEINFO_);
54  }
55 
56  // add the data to the multivariate stats objects
57  for(int i = 0; i < p_dimensions; i++) {
58  for(int j = 0; j <= i; j++) {
59  p_statistics[p_dimensions*i+j]->AddData(&data[count*i], &data[count*j], count);
60  }
61  }
62  }
63 
64  // Use VDV' decomposition to obtain the eigenvectors
65  void PrincipalComponentAnalysis::ComputeTransform() {
66  if(p_hasTransform) {
67  std::string m = "This PCA already has a computed transform";
68  throw IException(IException::Programmer, m, _FILEINFO_);
69  }
70 
71  TNT::Array2D<double> C(p_dimensions, p_dimensions);
72  for(int i = 0; i < p_dimensions; i++) {
73  for(int j = 0; j < p_dimensions; j++) {
74  C[i][j] = (p_statistics[p_dimensions*i+j])->Correlation();
75  }
76  }
77 
78  JAMA::Eigenvalue<double> E(C);
79  TNT::Array2D<double> D, V;
80 
81  E.getD(D);
82  E.getV(V);
83  p_transform = TNT::Array2D<double>(V.dim1(), V.dim2());
84 
85  // The transform matrix needs to have the eigenvectors
86  // sorted in *descending* order
87  // So we need to reverse the order of V
88  // which is sorted in *ascending* order
89  for(int i = 0; i < V.dim1(); i++) {
90  for(int j = 0; j < V.dim2(); j++) {
91  p_transform[i][j] = V[i][V.dim2()-j-1];
92  }
93  }
94 
95  ComputeInverse();
96 
97  p_hasTransform = true;
98  }
99 
100  // Use LU decomposition to compute the inverse
101  void PrincipalComponentAnalysis::ComputeInverse() {
102  TNT::Array2D<double> id(p_transform.dim1(), p_transform.dim2(), 0.0);
103  for(int i = 0; i < p_transform.dim1(); i++) id[i][i] = 1;
104 
105  JAMA::LU<double> lu(p_transform);
106  if(lu.det() == 0.0) {
107  std::string m = "Cannot take the inverse of the transform matrix";
108  throw IException(IException::Programmer, m, _FILEINFO_);
109  }
110 
111  p_inverse = lu.solve(id);
112  }
113 
114  // Transform the vector into principal component space
115  TNT::Array2D<double> PrincipalComponentAnalysis::Transform(TNT::Array2D<double> data) {
116  if(data.dim1() != 1 || data.dim2() != p_dimensions) {
117  QString m = "Transform input must be of dimension 1 x " + QString::number(p_dimensions);
118  throw IException(IException::Programmer, m, _FILEINFO_);
119  }
120 
121  // the vector times the transform matrix
122  return TNT::matmult(data, p_transform);
123  }
124 
125  // Transform the vector from principal component space
126  TNT::Array2D<double> PrincipalComponentAnalysis::Inverse(TNT::Array2D<double> data) {
127  if(data.dim1() != 1 || data.dim2() != p_dimensions) {
128  QString m = "Transform input must be of dimension 1 x " + QString::number(p_dimensions);
129  throw IException(IException::Programmer, m, _FILEINFO_);
130  }
131 
132  // the vector times the inverse matrix
133  return TNT::matmult(data, p_inverse);
134  }
135 }
Isis::MultivariateStatistics
Container of multivariate statistics.
Definition: MultivariateStatistics.h:54
Isis::IException
Isis exception class.
Definition: IException.h:91
std
Namespace for the standard library.
Isis::E
const double E
Sets some basic constants for use in ISIS programming.
Definition: Constants.h:39
Isis
This is free and unencumbered software released into the public domain.
Definition: Apollo.h:16