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