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
13using namespace std;
14
15namespace Isis {
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 exception class.
Definition IException.h:91
@ Programmer
This error is for when a programmer made an API call that was illegal.
Definition IException.h:146
Container of multivariate statistics.
PrincipalComponentAnalysis(const int n)
Constructs the PrincipalComponentAnalysis object.
const double E
Sets some basic constants for use in ISIS programming.
Definition Constants.h:39
This is free and unencumbered software released into the public domain.
Definition Apollo.h:16
Namespace for the standard library.