node-red-contrib-prib-functions 0.19.2 → 0.20.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.github/workflows/codeql-analysis.yml +3 -3
- package/.github/workflows/npmpublish.yml +6 -6
- package/.vs/VSWorkspaceState.json +7 -0
- package/.vs/node-red-contrib-prib-functions/v17/.wsuo +0 -0
- package/README.md +6 -2
- package/arima/index.js +18 -0
- package/dataAnalysis/arrayAllRowsSwap.js +15 -0
- package/dataAnalysis/arrayCompareToPrecision.js +34 -0
- package/dataAnalysis/arrayDifference.js +14 -0
- package/dataAnalysis/arrayDifferenceSeasonal.js +15 -0
- package/dataAnalysis/arrayDifferenceSeasonalSecondOrder.js +20 -0
- package/dataAnalysis/arrayDifferenceSecondOrder.js +14 -0
- package/dataAnalysis/arrayForEachRange.js +38 -0
- package/dataAnalysis/arrayOverlay.js +13 -0
- package/dataAnalysis/arrayProduct.js +11 -0
- package/dataAnalysis/arrayRandom.js +14 -0
- package/dataAnalysis/arrayReduceRange.js +11 -0
- package/dataAnalysis/arrayScale.js +11 -0
- package/dataAnalysis/arraySum.js +11 -0
- package/dataAnalysis/arraySumSquared.js +11 -0
- package/dataAnalysis/arraySwap.js +11 -0
- package/dataAnalysis/dataAnalysis.html +31 -14
- package/dataAnalysis/dataAnalysis.js +10 -1
- package/dataAnalysis/generateMatrixFunction.js +89 -0
- package/dataAnalysis/generateVectorFunction.js +25 -0
- package/dataAnalysis/pca.js +472 -325
- package/dataAnalysis/svd.js +239 -0
- package/echart/echart.html +68 -0
- package/echart/echart.js +85 -0
- package/echart/icons/chart-671.png +0 -0
- package/echart/lib/echarts.js +95886 -0
- package/lib/Chart.js +177 -0
- package/lib/Column.js +99 -0
- package/lib/GraphDB.js +14 -0
- package/lib/Table.js +185 -0
- package/lib/objectExtensions.js +361 -0
- package/matrix/matrix.js +2 -48
- package/monitor/BarGauge.js +8 -0
- package/monitor/Dataset.js +29 -0
- package/monitor/DialGauge.js +109 -0
- package/monitor/DialNeedle.js +36 -0
- package/monitor/Format.js +74 -0
- package/monitor/centerElement.js +14 -0
- package/monitor/compareElements.js +95 -0
- package/monitor/defs.js +23 -0
- package/monitor/extensions.js +906 -0
- package/monitor/functions.js +36 -0
- package/monitor/json2xml.js +103 -0
- package/monitor/monitorSystem.html +198 -0
- package/monitor/monitorSystem.js +322 -0
- package/monitor/svgHTML.js +179 -0
- package/monitor/svgObjects.js +64 -0
- package/package.json +18 -6
- package/test/00-objectExtensions.js +94 -0
- package/test/04-tables.js +33 -0
- package/test/data/.config.nodes.json +608 -0
- package/test/data/.config.nodes.json.backup +608 -0
- package/test/data/.config.runtime.json +4 -0
- package/test/data/.config.runtime.json.backup +3 -0
- package/test/data/.config.users.json +21 -0
- package/test/data/.config.users.json.backup +21 -0
- package/test/data/.flow.json.backup +2448 -2207
- package/test/data/float32vector10.npy +0 -0
- package/test/data/flow.json +2412 -2191
- package/test/data/int2matrix2x3.npy +0 -0
- package/test/data/package-lock.json +158 -0
- package/test/data/package.json +11 -0
- package/test/dataAnalysisExtensions.js +472 -0
- package/test/dataAnalysisPCA.js +54 -0
- package/test/dataAnalysisSVD.js +31 -0
- package/test/euclideanDistance.js +2 -2
- package/test/transformNumPy.js +132 -0
- package/transform/NumPy.js +303 -0
- package/transform/transform.html +12 -0
- package/transform/transform.js +34 -2
package/dataAnalysis/pca.js
CHANGED
|
@@ -1,32 +1,40 @@
|
|
|
1
|
-
|
|
1
|
+
require("./arrayAllRowsSwap");
|
|
2
|
+
require("./arrayForEachRange.js");
|
|
3
|
+
require("./arrayReduceRange.js");
|
|
4
|
+
require("./arrayScale.js");
|
|
5
|
+
require("./arraySwap.js");
|
|
6
|
+
require("./arraySum.js");
|
|
7
|
+
require("./arraySumSquared.js");
|
|
8
|
+
const generatedMatrixFunction = require("./generateMatrixFunction");
|
|
9
|
+
const generateVectorFunction=require("./generateVectorFunction.js")
|
|
10
|
+
|
|
11
|
+
function PCA() { //Principal Component Analysis
|
|
2
12
|
}
|
|
3
|
-
PCA.prototype.
|
|
4
|
-
|
|
13
|
+
PCA.prototype.rowType=Array;
|
|
14
|
+
PCA.prototype.getDeviationMatrix=function(matrix) {
|
|
15
|
+
const rows=matrix.length;
|
|
16
|
+
const onesMatrix=this.getMatrix(rows,rows,1);
|
|
17
|
+
const scaled=this.scale(this.multiply(onesMatrix, matrix),1/rows)
|
|
18
|
+
return this.subtract(matrix, scaled);
|
|
5
19
|
}
|
|
6
|
-
PCA.prototype.
|
|
7
|
-
|
|
20
|
+
PCA.prototype.getDeviationScores=function(deviation) {
|
|
21
|
+
const transposed=this.transpose(deviation)
|
|
22
|
+
return this.multiply(transposed, deviation);
|
|
8
23
|
}
|
|
9
|
-
PCA.prototype.
|
|
10
|
-
return this.scale(devSumOfSquares, 1
|
|
24
|
+
PCA.prototype.getVarianceCovariance=function(devSumOfSquares, sample) {
|
|
25
|
+
return this.scale(devSumOfSquares, 1/devSumOfSquares.length);
|
|
11
26
|
}
|
|
12
|
-
PCA.prototype.
|
|
13
|
-
const
|
|
14
|
-
|
|
15
|
-
const eigenvalues = result.S;
|
|
16
|
-
return eigenvalues.map((value,i)=>{
|
|
17
|
-
return {
|
|
18
|
-
eigenvalue:value,
|
|
19
|
-
vector:eigenvectors.map((vector,j)=>-vector[i]) //HACK prevent completely negative vectors
|
|
20
|
-
}
|
|
21
|
-
});
|
|
27
|
+
PCA.prototype.getVarianceCovarianceSample=function(devSumOfSquares) {
|
|
28
|
+
const factor=devSumOfSquares.length-1
|
|
29
|
+
return this.scale(devSumOfSquares, 1/factor);
|
|
22
30
|
}
|
|
23
|
-
|
|
24
|
-
PCA.prototype.
|
|
31
|
+
|
|
32
|
+
PCA.prototype.getAdjustedData=function(data, ...vectorObjs){ // reduced after removing some dimensions
|
|
25
33
|
const vectors = vectorObjs.map((v)=>v.vector);
|
|
26
|
-
const matrixMinusMean = this.
|
|
34
|
+
const matrixMinusMean = this.getDeviationMatrix(data);
|
|
27
35
|
const adjustedData = this.multiply(vectors, this.transpose(matrixMinusMean));
|
|
28
|
-
const
|
|
29
|
-
const avgData =
|
|
36
|
+
const rows=data.length
|
|
37
|
+
const avgData =this.scale(multiply(this.getMatrix(rows,rows,1),data), -1/rows); //NOTE get the averages to add back
|
|
30
38
|
return {
|
|
31
39
|
adjustedData: adjustedData,
|
|
32
40
|
formattedAdjustedData:formatData(adjustedData, 2),
|
|
@@ -35,365 +43,504 @@ PCA.prototype.computeAdjustedData=function(data, ...vectorObjs){
|
|
|
35
43
|
};
|
|
36
44
|
}
|
|
37
45
|
// Get original data set from reduced data set (decompress)
|
|
38
|
-
PCA.prototype.
|
|
46
|
+
PCA.prototype.getOriginalData=function(adjustedData, vectors, avgData) {
|
|
39
47
|
const originalWithoutMean = this.transpose(multiply(transpose(vectors), adjustedData));
|
|
40
48
|
const originalWithMean = this.subtract(originalWithoutMean, avgData);
|
|
41
49
|
return {
|
|
42
50
|
originalData: originalWithMean,
|
|
43
51
|
formattedOriginalData: this.formatData(originalWithMean, 2)
|
|
44
52
|
}
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
const
|
|
53
|
+
}
|
|
54
|
+
PCA.prototype.getPercentageExplained=function(vectors, ...selected) {
|
|
55
|
+
const total = vectors.map((v)=>v.eigenvalue).sum();
|
|
56
|
+
const explained = selected.map((v)=>v.eigenvalue).sum();
|
|
48
57
|
return (explained / total);
|
|
49
58
|
}
|
|
50
59
|
PCA.prototype.getEigenVectors=function(data) {
|
|
51
|
-
|
|
60
|
+
const deviationMatrix=this.getDeviationMatrix(data);
|
|
61
|
+
const deviationScores=this.getDeviationScores(deviationMatrix)
|
|
62
|
+
const matrix=this.getVarianceCovariance(deviationScores)
|
|
63
|
+
const result = this.svd(matrix);
|
|
64
|
+
const eigenvectors = result.U;
|
|
65
|
+
const eigenvalues = result.S;
|
|
66
|
+
return eigenvalues.map((value,i)=>{
|
|
67
|
+
return {
|
|
68
|
+
eigenvalue:value,
|
|
69
|
+
vector:eigenvectors.map((vector,j)=>-vector[i]) //prevent completely negative vectors
|
|
70
|
+
}
|
|
71
|
+
});
|
|
52
72
|
}
|
|
53
|
-
PCA.prototype.
|
|
73
|
+
PCA.prototype.getTopResult=function(data){
|
|
54
74
|
const eigenVectors = this.getEigenVectors(data);
|
|
55
|
-
const sorted =
|
|
75
|
+
const sorted = eigenVectors.sort((a, b)=>b.eigenvalue-a.eigenvalue);
|
|
56
76
|
const selected = sorted[0].vector;
|
|
57
|
-
return this.
|
|
77
|
+
return this.getAdjustedData(data, selected);
|
|
58
78
|
}
|
|
59
79
|
PCA.prototype.formatData=function(data, precision) {
|
|
60
80
|
const factor= Math.pow(10, precision || 2);
|
|
61
81
|
return data.map((d,i)=>d.map((n)=>Math.round(n * factor) / factor))
|
|
62
82
|
}
|
|
63
|
-
PCA.prototype.
|
|
64
|
-
if(!a
|
|
83
|
+
PCA.prototype.testIsMatrix=(a)=>{
|
|
84
|
+
if(!(a instanceof Array)) throw Error("Not matrix at row level, found type of "+typeof a)
|
|
85
|
+
const row=a[0];
|
|
86
|
+
if(row instanceof Array) return;
|
|
87
|
+
if(row instanceof this.rowType) return;
|
|
88
|
+
throw Error("Not matrix at column level, found type of "+typeof a[0])
|
|
65
89
|
}
|
|
90
|
+
const sumVector1=generateVectorFunction({
|
|
91
|
+
code:"returnValue+=vector[index]*matrix[index][col]",
|
|
92
|
+
args:["matrix","col"],
|
|
93
|
+
returnValue:0
|
|
94
|
+
})
|
|
95
|
+
const multiplyMatrix=generatedMatrixFunction({
|
|
96
|
+
code:"setElement(sumVector1(element,bMatrix,columnOffset));",
|
|
97
|
+
args:["bMatrix"],
|
|
98
|
+
returnValue:"Object.create(Object.getPrototypeOf(matrix))"
|
|
99
|
+
})
|
|
100
|
+
|
|
66
101
|
PCA.prototype.multiply=function(a, b){
|
|
67
|
-
this.
|
|
68
|
-
this.
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
102
|
+
this.testIsMatrix(a);
|
|
103
|
+
this.testIsMatrix(b);
|
|
104
|
+
const rows=a.length;
|
|
105
|
+
if(a[0].length !== b.length) throw Error("Non-conformable matrices, left columns: "+a[0].length+" != right rows "+b.length);
|
|
106
|
+
const columns=b[0].length;
|
|
107
|
+
const result=this.getMatrix(rows,columns);
|
|
108
|
+
for(let ri=0;ri<rows;ri++){
|
|
109
|
+
for(let ci=0;ci<columns;ci++){
|
|
110
|
+
result[ri][ci]=sumVector1(a[ri],b,ci);
|
|
111
|
+
// result[ri][ci]=a[ri].reduce((sum,value,rci)=>sum+value*b[rci][ci],0);
|
|
77
112
|
}
|
|
78
113
|
}
|
|
79
|
-
return
|
|
114
|
+
return result
|
|
80
115
|
}
|
|
116
|
+
|
|
81
117
|
PCA.prototype.subtract=function(a,b) {
|
|
82
118
|
if(!(a.length === b.length && a[0].length === b[0].length)) throw Error('Both A and B should have the same dimensions');
|
|
83
|
-
const
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
119
|
+
const rows=a.length;
|
|
120
|
+
const columns=a[0].length;
|
|
121
|
+
const result=this.getMatrix(rows,columns);
|
|
122
|
+
for(let ri=0;ri<rows;ri++){
|
|
123
|
+
for(let ci=0;ci<columns;ci++){
|
|
124
|
+
result[ri][ci]=a[ri][ci]-b[ri][ci];
|
|
125
|
+
}
|
|
89
126
|
}
|
|
90
|
-
return result
|
|
127
|
+
return result
|
|
91
128
|
}
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
129
|
+
function mapMatrix(matrix,callFunction) {
|
|
130
|
+
if(callFunction)
|
|
131
|
+
return matrix.map((row,ri)=>row.map((cell,ci)=>callFunction(cell,ri,ci,matrix)));
|
|
132
|
+
return this.cloneMatrix(matrix);
|
|
133
|
+
};
|
|
134
|
+
|
|
135
|
+
const mapVector=generateVectorFunction({
|
|
136
|
+
code:"vector[index]=fromVector[index]",
|
|
137
|
+
args:["fromVector"],
|
|
138
|
+
});
|
|
139
|
+
|
|
140
|
+
const cloneVector=generateVectorFunction({
|
|
141
|
+
code:"returnValue[index]=vector[index]",
|
|
142
|
+
args:["type=Array"],
|
|
143
|
+
});
|
|
144
|
+
|
|
145
|
+
function cloneMatrix(matrix){
|
|
146
|
+
const columns=matrix.length
|
|
147
|
+
const rows=matrix[0].length
|
|
148
|
+
const result=new Array(rows)
|
|
149
|
+
for(let ri=0;ri<rows;ri++){
|
|
150
|
+
const row=new this.rowType(columns);
|
|
151
|
+
mapVector(row,matrix[ri]);
|
|
152
|
+
result[ri]=row;
|
|
99
153
|
}
|
|
100
154
|
return result;
|
|
101
155
|
}
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
156
|
+
const transposeVector=generateVectorFunction({
|
|
157
|
+
code:"vector[index]=fromVector[index][column]",
|
|
158
|
+
args:["fromVector","column"],
|
|
159
|
+
});
|
|
160
|
+
//const mapVector=generateVectorFunction("returnValue[index]=vector[index]",[])
|
|
161
|
+
PCA.prototype.map=mapMatrix;
|
|
162
|
+
PCA.prototype.cloneMatrix=cloneMatrix;
|
|
163
|
+
PCA.prototype.mapTranspose=function(matrix) {
|
|
164
|
+
const columns=matrix.length
|
|
165
|
+
const rows=matrix[0].length
|
|
166
|
+
const result=new this.rowType(rows)
|
|
167
|
+
for(let ri=0;ri<rows;ri++){
|
|
168
|
+
const row=new this.rowType(columns);
|
|
169
|
+
transposeVector(row,matrix,ri)
|
|
170
|
+
result[ri]=row;
|
|
109
171
|
}
|
|
110
172
|
return result;
|
|
111
173
|
}
|
|
112
|
-
PCA.prototype.transpose=
|
|
113
|
-
|
|
114
|
-
|
|
174
|
+
PCA.prototype.transpose=PCA.prototype.mapTranspose
|
|
175
|
+
PCA.prototype.forEachRow=function(matrix,callFunction) {
|
|
176
|
+
matrix.forEach(row,RowIndex=>callFunction(row,rowIndex,matrix))
|
|
177
|
+
return this
|
|
178
|
+
}
|
|
179
|
+
PCA.prototype.forEachRowColumn=function(matrix,rowIndex,callFunction) {
|
|
180
|
+
const row=matrix[rowIndex];
|
|
181
|
+
const columns=row[0].length;
|
|
182
|
+
for(let columnIndex=0;columnIndex<columns;columnIndex++)
|
|
183
|
+
callFunction(row[columnIndex],rowIndex,columnIndex,matrix)
|
|
184
|
+
return this
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
PCA.prototype.forEachColumn=function(matrix,columnIndex,callFunction) {
|
|
188
|
+
const rows=matrix.length;
|
|
189
|
+
for(let rowIndex=0;rowIndex<rows;rowIndex++)
|
|
190
|
+
callFunction(matrix[rowIndex][columnIndex],rowIndex,columnIndex,matrix)
|
|
191
|
+
return this
|
|
192
|
+
}
|
|
193
|
+
PCA.prototype.forEachCell=function(matrix,callFunction) {
|
|
194
|
+
const rows=matrix.length;
|
|
195
|
+
const columns=matrix[0].length;
|
|
196
|
+
for(let rowIndex=0;rowIndex<rows;rowIndex++){
|
|
197
|
+
const row=matrix[rowIndex];
|
|
198
|
+
for(let columnIndex=0;columnIndex<columns;columnIndex++)
|
|
199
|
+
callFunction(row[columnIndex],rowIndex,columnIndex,matrix)
|
|
200
|
+
}
|
|
201
|
+
return this
|
|
115
202
|
}
|
|
116
|
-
PCA.prototype.
|
|
117
|
-
|
|
203
|
+
PCA.prototype.forEachSetCell=function(matrix,callFunction) {
|
|
204
|
+
const rows=matrix.length;
|
|
205
|
+
const columns=matrix[0].length;
|
|
206
|
+
for(let rowIndex=0;rowIndex<rows;rowIndex++){
|
|
207
|
+
const row=matrix[rowIndex];
|
|
208
|
+
for(let columnIndex=0;columnIndex<columns;columnIndex++)
|
|
209
|
+
row[columnIndex]=callFunction(rowIndex,columnIndex,cell,row,matrix)
|
|
210
|
+
}
|
|
211
|
+
return this
|
|
118
212
|
}
|
|
119
|
-
PCA.prototype.
|
|
120
|
-
let temp;
|
|
121
|
-
const prec = Math.pow(2, -52) // double presision
|
|
122
|
-
const tolerance = 1.e-64 // precision;
|
|
123
|
-
const itmax = 50;
|
|
124
|
-
let c = 0;
|
|
125
|
-
let i = 0;
|
|
126
|
-
let j = 0;
|
|
127
|
-
let k = 0;
|
|
128
|
-
let l = 0;
|
|
129
|
-
const u = this.clone(A);
|
|
130
|
-
const m = u.length;
|
|
131
|
-
const n = u[0].length;
|
|
132
|
-
if (m < n) throw "Need more rows than columns"
|
|
213
|
+
PCA.prototype.scale=function(matrix,factor){return this.map(matrix,c=>c*factor)};
|
|
133
214
|
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
215
|
+
function getMatrix(rows=2,columns=rows,fill=0) {
|
|
216
|
+
const matrix=new Array(rows);
|
|
217
|
+
for(let i=0; i<rows; i++) matrix[i]=new this.rowType(columns).fill(fill);
|
|
218
|
+
return matrix;
|
|
219
|
+
}
|
|
138
220
|
|
|
139
|
-
|
|
140
|
-
a = Math.abs(a)
|
|
141
|
-
b = Math.abs(b)
|
|
142
|
-
if(a > b) return a * Math.sqrt(1.0 + (b * b / a / a))
|
|
143
|
-
if(b == 0.0) return a
|
|
144
|
-
return b * Math.sqrt(1.0 + (a * a / b / b))
|
|
145
|
-
}
|
|
146
|
-
function rep(s, v, k=0) {
|
|
147
|
-
const n=s[k],
|
|
148
|
-
ret=Array(n);
|
|
149
|
-
let i;
|
|
150
|
-
if(k === s.length - 1) {
|
|
151
|
-
for(i=n-2; i >= 0; i-=2) {
|
|
152
|
-
ret[i+1] = v;
|
|
153
|
-
ret[i] = v;
|
|
154
|
-
}
|
|
155
|
-
if(i === -1) ret[0] = v;
|
|
156
|
-
return ret;
|
|
157
|
-
}
|
|
158
|
-
const kPlusOne=k++;
|
|
159
|
-
for (i= n-1; i>=0; i--) {
|
|
160
|
-
ret[i] = rep(s, v, kPlusOne);
|
|
161
|
-
}
|
|
162
|
-
return ret;
|
|
163
|
-
}
|
|
164
|
-
let f = 0.0;
|
|
165
|
-
let g = 0.0;
|
|
166
|
-
let h = 0.0;
|
|
167
|
-
let x = 0.0;
|
|
168
|
-
let y = 0.0;
|
|
169
|
-
let z = 0.0;
|
|
170
|
-
let s = 0.0;
|
|
221
|
+
PCA.prototype.getMatrix=getMatrix
|
|
171
222
|
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
223
|
+
PCA.prototype.pythag=(a,b)=>{
|
|
224
|
+
if(b == 0.0) return a
|
|
225
|
+
const a2 = a**2
|
|
226
|
+
const b2 = b**2
|
|
227
|
+
return a > b ? a * Math.sqrt(1.0 + b2/a2) : b * Math.sqrt(1.0 + a2/b2)
|
|
228
|
+
};
|
|
229
|
+
PCA.prototype.rep=function(s,v,k=0){
|
|
230
|
+
const n=s[k],returnVector=new this.rowType(n).fill(0);
|
|
231
|
+
if(k === s.length-1){
|
|
232
|
+
returnVector.fill(v,0,n-1)
|
|
233
|
+
} else {
|
|
234
|
+
const kPlusOne=k+1;
|
|
235
|
+
for(let i=n-1;i>=0;i--) returnVector[i]=this.rep(s,v,kPlusOne);
|
|
236
|
+
}
|
|
237
|
+
return returnVector;
|
|
238
|
+
}
|
|
239
|
+
module.exports = PCA;
|
|
240
|
+
|
|
241
|
+
PCA.prototype.getDeterminant=function(matrix){
|
|
242
|
+
const result = this.getDecomposed(matrix);
|
|
243
|
+
return result.lum.product()*result.toggle;
|
|
244
|
+
}
|
|
245
|
+
PCA.prototype.getDecomposed=function(matrix){
|
|
246
|
+
// Crout's LU decomposition for matrix determinant and inverse
|
|
247
|
+
// lum is lower & upper matrix
|
|
248
|
+
// perm is row permuations vector
|
|
249
|
+
const rows = matrix.length, rowsMinus1 = rows-1;
|
|
250
|
+
const perm = new this.rowType(rows).fill(0.0);
|
|
251
|
+
let toggle = +1; // even (+1) or odd (-1) row permutatuions
|
|
252
|
+
const result=Object.assign([],matrix);
|
|
253
|
+
const lum=Object.assign([],matrix);
|
|
254
|
+
|
|
255
|
+
for(let ri=0; ri<rows; ++ri) perm[ri]=ri
|
|
256
|
+
let piv=rows;
|
|
257
|
+
for(let j=0; j<rowsMinus1; ++j) {
|
|
258
|
+
const lumRowJ=lum[j];
|
|
259
|
+
let max = Math.abs(lumRowJ[j]);
|
|
260
|
+
for(let ri=j+1; ri<rows; ++ri) { // pivot index
|
|
261
|
+
const xrij = Math.abs(lum[ri][j]);
|
|
262
|
+
if(xrij > max) {
|
|
263
|
+
max = xrij;
|
|
264
|
+
piv = ri;
|
|
265
|
+
}
|
|
266
|
+
}
|
|
267
|
+
|
|
268
|
+
if(piv != j) {
|
|
269
|
+
lum.swap(piv,j)
|
|
270
|
+
perm.swap(piv,j);
|
|
271
|
+
toggle = -toggle;
|
|
272
|
+
}
|
|
273
|
+
const xjj = lumRowJ[j];
|
|
274
|
+
if(xjj !== 0.0) {
|
|
275
|
+
for(let i=j+1; i<rows; ++i) {
|
|
276
|
+
const lumRow=lum[i]
|
|
277
|
+
const xij = lumRow[j]/xjj;
|
|
278
|
+
lumRow[j] = xij;
|
|
279
|
+
for(let ci=0; ci<columns; ++ci) lumRow[ci] -= xij*lumRowJ[ci]
|
|
280
|
+
}
|
|
281
|
+
}
|
|
282
|
+
}
|
|
283
|
+
return {toggle:toggle,lum:lum,perm:perm}
|
|
284
|
+
}
|
|
285
|
+
|
|
286
|
+
PCA.prototype.getReduced=function(lum, b){
|
|
287
|
+
const rows = lum.length, rowsMinus1 = rows-1;
|
|
288
|
+
const x=new this.rowType(n).fill(0.0)
|
|
289
|
+
let sum;
|
|
290
|
+
for(let ri=0; ri<rows; ++ri) x[ri]=b[ri];
|
|
291
|
+
for(let ri=1; ri<rows; ++ri) {
|
|
292
|
+
const lumRow=lum[ri];
|
|
293
|
+
sum=x[ri];
|
|
294
|
+
for(let ci=0;ci<columns;ci++) sum-=lumRow[ci]*x[ci];
|
|
295
|
+
x[ri]=sum;
|
|
296
|
+
}
|
|
297
|
+
x[rowsMinus1] /= lum[rowsMinus1][rowsMinus1];
|
|
298
|
+
for (let ri=rows-2; ri>0; --ri) {
|
|
299
|
+
sum=x[ri];
|
|
300
|
+
for(let ci=0;ci<columns;ci++) sum-=lumRow[ci]*x[ci];
|
|
301
|
+
x[ri]=sum/lumRow[ri];
|
|
302
|
+
}
|
|
303
|
+
return x;
|
|
304
|
+
}
|
|
305
|
+
|
|
306
|
+
function svd(matrix) {//singular value decomposition
|
|
307
|
+
const eps=this.rowType instanceof Float64Array?
|
|
308
|
+
2**-52:
|
|
309
|
+
this.rowType instanceof Float32Array?2**-23:Number.EPSILON;
|
|
310
|
+
let precision = eps;
|
|
311
|
+
const tolerance = 1.e-64;
|
|
312
|
+
const itmax = 50;
|
|
313
|
+
const rows=matrix.length
|
|
314
|
+
const rowOffsetEnd=rows-1;
|
|
315
|
+
const columns=matrix[0].length;
|
|
316
|
+
const columnOffsetEnd=columns-1;
|
|
317
|
+
if (rows < columns) throw "Need more rows than columns"
|
|
318
|
+
let temp;
|
|
319
|
+
let c= 0;
|
|
320
|
+
let i = 0;
|
|
321
|
+
let j = 0;
|
|
322
|
+
let k = 0;
|
|
323
|
+
let l = 0;
|
|
324
|
+
const u = this.cloneMatrix(matrix);
|
|
325
|
+
const e = new this.rowType(columns).fill(0.0); //vector1
|
|
326
|
+
const q = new this.rowType(columns).fill(0.0); //vector2
|
|
327
|
+
const v = this.rep([columns, columns], 0);
|
|
328
|
+
//Householder's reduction to bidiagonal form
|
|
329
|
+
let f = 0.0;
|
|
330
|
+
let g = 0.0;
|
|
331
|
+
let h = 0.0;
|
|
332
|
+
let x = 0.0;
|
|
333
|
+
let y = 0.0;
|
|
334
|
+
let z = 0.0;
|
|
335
|
+
let s = 0.0;
|
|
336
|
+
|
|
337
|
+
for(i=0; i<columns; i++) {
|
|
338
|
+
e[i] = g; //vector
|
|
339
|
+
const uRow=u[i]
|
|
340
|
+
const iPlus1=i+1;
|
|
341
|
+
const sum=u.reduceRange(i,rowOffsetEnd,(previousValue,row)=>previousValue+row[i]**2)
|
|
342
|
+
if(sum <= tolerance)
|
|
343
|
+
g = 0.0;
|
|
344
|
+
else {
|
|
345
|
+
f = uRow[i];
|
|
346
|
+
g = Math.sqrt(sum)*(f<0?1:-1);
|
|
347
|
+
h = f * g - sum
|
|
348
|
+
uRow[i] = f - g;
|
|
349
|
+
for(j=iPlus1; j<columns; j++) {
|
|
350
|
+
const factor=u.reduceRange(i,rowOffsetEnd,(previousValue,row)=>previousValue+row[i]*row[j])/h;
|
|
351
|
+
for(let ri=i;ri<=rowOffsetEnd;ri++) u[ri][j]+=factor*u[ri][i]
|
|
194
352
|
}
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
353
|
+
}
|
|
354
|
+
q[i] = g
|
|
355
|
+
const sumCols=u.reduceRange(iPlus1,columnOffsetEnd,(previousValue,cell,ci)=>previousValue+u[i][ci]**2)
|
|
356
|
+
if(sumCols <= tolerance)
|
|
357
|
+
g = 0.0
|
|
358
|
+
else {
|
|
359
|
+
f = uRow[iPlus1]
|
|
360
|
+
g = Math.sqrt(sumCols) * (f<0? 1:-1)
|
|
361
|
+
h = f * g - sumCols
|
|
362
|
+
uRow[iPlus1] = f - g;
|
|
363
|
+
for(let ci=iPlus1;ci<=columnOffsetEnd;ci++) e[ci]=uRow[ci]/h;
|
|
364
|
+
for(j = iPlus1; j < rows; j++) {
|
|
365
|
+
const uj=u[j];
|
|
366
|
+
const sum=uj.reduceRange(iPlus1,columnOffsetEnd,(previousValue,column,ci)=>previousValue+column*uRow[ci]);
|
|
367
|
+
for(let ci=iPlus1;ci<=columnOffsetEnd;ci++) uj[ci]+=sum*e[ci]; }
|
|
368
|
+
}
|
|
369
|
+
y = Math.abs(q[i]) + Math.abs(e[i])
|
|
370
|
+
if(y>x) x=y
|
|
371
|
+
}
|
|
372
|
+
l=columns;
|
|
373
|
+
// accumulation of right hand transformations
|
|
374
|
+
for(i=columnOffsetEnd; i != -1; i += -1) {
|
|
375
|
+
if (g != 0.0) {
|
|
376
|
+
const uRow=u[i]
|
|
377
|
+
h = g * uRow[i+1]
|
|
378
|
+
for(j=l; j<columns; j++)
|
|
379
|
+
v[j][i] = uRow[j] / h //u is array, v is square of columns
|
|
380
|
+
for(j=l; j < columns; j++) {
|
|
381
|
+
const sum=v.reduceRange(l,columnOffsetEnd,(previousValue,row,ri)=>previousValue+u[i][ri] * row[j])
|
|
382
|
+
for(let ci=l;ci<columns;ci++){
|
|
383
|
+
const vRow=v[ci];
|
|
384
|
+
vRow[j]+=vRow[i]*sum
|
|
214
385
|
}
|
|
215
386
|
}
|
|
216
|
-
y = Math.abs(q[i]) + Math.abs(e[i])
|
|
217
|
-
if(y > x)
|
|
218
|
-
x = y
|
|
219
387
|
}
|
|
388
|
+
const vRow=v[i]
|
|
389
|
+
for(j=l; j<columns; j++) {
|
|
390
|
+
vRow[j] = 0;
|
|
391
|
+
v[j][i] = 0;
|
|
392
|
+
}
|
|
393
|
+
vRow[i] = 1;
|
|
394
|
+
g = e[i]
|
|
395
|
+
l = i
|
|
396
|
+
}
|
|
220
397
|
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
398
|
+
// accumulation of left hand transformations
|
|
399
|
+
for (i = columns - 1; i != -1; i += -1) {
|
|
400
|
+
l = i + 1
|
|
401
|
+
g = q[i]
|
|
402
|
+
const uRow=u[i];
|
|
403
|
+
for(j=l; j<columns; j++) uRow[j]=0;
|
|
404
|
+
if (g != 0.0)
|
|
405
|
+
for(let i=0;i<rows;i++) u[i][i];
|
|
406
|
+
else {
|
|
407
|
+
h = uRow[i] * g
|
|
408
|
+
for (j = l; j < columns; j++) {
|
|
409
|
+
const factor=u.reduceRange(l,rowOffsetEnd,(previousValue,row)=>previousValue+row[i]*row[j])/h;
|
|
410
|
+
for(let ci=i;ci<=rowOffsetEnd;ci++){
|
|
411
|
+
const row=u[ci];
|
|
412
|
+
row[j]+=row[i]*factor
|
|
233
413
|
}
|
|
234
414
|
}
|
|
235
|
-
for
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
v[i][i] = 1;
|
|
240
|
-
g = e[i]
|
|
241
|
-
l = i
|
|
242
|
-
}
|
|
415
|
+
for(let ri=i;ri<=rowOffsetEnd;ri++) u[ri][i]/=g;
|
|
416
|
+
}
|
|
417
|
+
uRow[i]++;
|
|
418
|
+
}
|
|
243
419
|
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
s = 0.0
|
|
254
|
-
for (k = l; k < m; k++) s += u[k][i] * u[k][j];
|
|
255
|
-
f = s / h
|
|
256
|
-
for (k = i; k < m; k++) u[k][j] += f * u[k][i];
|
|
420
|
+
// diagonalization of the bidiagonal form
|
|
421
|
+
precision = precision * x;
|
|
422
|
+
for (k = columns - 1; k != -1; k += -1) {
|
|
423
|
+
for (var iteration = 0; iteration < itmax; iteration++) { // test f splitting
|
|
424
|
+
let test_convergence = false
|
|
425
|
+
for (l=k; l>=0; l--) {
|
|
426
|
+
if (Math.abs(e[l]) <= precision) {
|
|
427
|
+
test_convergence = true
|
|
428
|
+
break
|
|
257
429
|
}
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
let test_convergence = false
|
|
271
|
-
for(l=k; l != -1; l--) {
|
|
272
|
-
if(Math.abs(e[l]) <= prec) {
|
|
273
|
-
test_convergence = true
|
|
430
|
+
if (Math.abs(q[l - 1]) <= precision)
|
|
431
|
+
break
|
|
432
|
+
}
|
|
433
|
+
if (!test_convergence) { // cancellation of e[l] if l>0
|
|
434
|
+
c = 0.0
|
|
435
|
+
s = 1.0
|
|
436
|
+
const l1 = l - 1
|
|
437
|
+
for(i=l; i<=k; i++) {
|
|
438
|
+
const ei=e[i];
|
|
439
|
+
f = s * ei
|
|
440
|
+
e[i] = c * ei
|
|
441
|
+
if (Math.abs(f) <= precision)
|
|
274
442
|
break
|
|
443
|
+
g = q[i]
|
|
444
|
+
h = this.pythag(f, g)
|
|
445
|
+
q[i] = h
|
|
446
|
+
c = g / h
|
|
447
|
+
s = -f / h
|
|
448
|
+
for(let ci=l;ci<=columnOffsetEnd;ci++){
|
|
449
|
+
const row=u[ci];
|
|
450
|
+
const cl1 = row[l1]
|
|
451
|
+
const ci = row[i]
|
|
452
|
+
row[l1] = cl1 * c + (ci * s)
|
|
453
|
+
row[i] = -cl1 * s + (ci * c)
|
|
275
454
|
}
|
|
276
|
-
if (Math.abs(q[l - 1]) <= prec)
|
|
277
|
-
break
|
|
278
455
|
}
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
if (Math.abs(f) <= prec)
|
|
287
|
-
break
|
|
288
|
-
g = q[i]
|
|
289
|
-
h = pythag(f, g)
|
|
290
|
-
q[i] = h
|
|
291
|
-
c = g / h
|
|
292
|
-
s = -f / h
|
|
293
|
-
for(j = 0; j<m; j++) {
|
|
294
|
-
y = u[j][l1]
|
|
295
|
-
z = u[j][i]
|
|
296
|
-
u[j][l1] = y * c + (z * s)
|
|
297
|
-
u[j][i] = -y * s + (z * c)
|
|
298
|
-
}
|
|
299
|
-
}
|
|
456
|
+
}
|
|
457
|
+
// test f convergence
|
|
458
|
+
z = q[k]
|
|
459
|
+
if(l == k) { //convergence
|
|
460
|
+
if(z < 0.0) { //q[k] is made non-negative
|
|
461
|
+
q[k] = -z
|
|
462
|
+
for(let ci=l;ci<=columnOffsetEnd;ci++) v[ci][k]*=-1;
|
|
300
463
|
}
|
|
301
|
-
//
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
464
|
+
break //break out of iteration loop and move on to next k value
|
|
465
|
+
}
|
|
466
|
+
if(iteration >= itmax - 1)
|
|
467
|
+
throw 'Error: no convergence.'
|
|
468
|
+
// shift from bottom 2x2 minor
|
|
469
|
+
x = q[l]
|
|
470
|
+
const kMinus1=k-1;
|
|
471
|
+
y = q[kMinus1]
|
|
472
|
+
g = e[kMinus1]
|
|
473
|
+
h = e[k]
|
|
474
|
+
const z2=z**2
|
|
475
|
+
f = ((y**2 - z2 + g**2 - h*h)) / (2.0 * h * y)
|
|
476
|
+
g = this.pythag(f, 1.0)
|
|
477
|
+
const fg=f+f< 0.0?-g:g;
|
|
478
|
+
f = (x**2 - z2 + h * (y / fg - h)) / x
|
|
479
|
+
// next QR transformation
|
|
480
|
+
c = 1.0
|
|
481
|
+
s = 1.0
|
|
482
|
+
for(i=l+1; i <= k; i++) {
|
|
483
|
+
const iMinus1=i-1;
|
|
484
|
+
g = e[i]
|
|
485
|
+
y = q[i]
|
|
486
|
+
h = s * g
|
|
487
|
+
g = c * g
|
|
488
|
+
z = this.pythag(f, h)
|
|
489
|
+
e[iMinus1] = z
|
|
490
|
+
c = f / z
|
|
491
|
+
s = h / z
|
|
492
|
+
f = x * c + g * s
|
|
493
|
+
g = -x * s + g * c
|
|
494
|
+
h = y * s
|
|
495
|
+
y = y * c
|
|
496
|
+
for(j=0; j<columns; j++) {
|
|
497
|
+
const row=v[j];
|
|
498
|
+
x = row[iMinus1]
|
|
499
|
+
z = row[i]
|
|
500
|
+
row[iMinus1] = x * c + z * s
|
|
501
|
+
row[i] = -x * s + z * c
|
|
310
502
|
}
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
f = ((x - z) * (x + z) + h * (y / (f + g) - h)) / x
|
|
324
|
-
// next QR transformation
|
|
325
|
-
c = 1.0
|
|
326
|
-
s = 1.0
|
|
327
|
-
for(i=l+1; i<kPlusOne; i++) {
|
|
328
|
-
g = e[i]
|
|
329
|
-
y = q[i]
|
|
330
|
-
h = s * g
|
|
331
|
-
g = c * g
|
|
332
|
-
z = pythag(f, h)
|
|
333
|
-
e[i - 1] = z
|
|
334
|
-
c = f / z
|
|
335
|
-
s = h / z
|
|
336
|
-
f = x * c + g * s
|
|
337
|
-
g = -x * s + g * c
|
|
338
|
-
h = y * s
|
|
339
|
-
y = y * c
|
|
340
|
-
for (j = 0; j < n; j++) {
|
|
341
|
-
x = v[j][i - 1]
|
|
342
|
-
z = v[j][i]
|
|
343
|
-
v[j][i - 1] = x * c + z * s
|
|
344
|
-
v[j][i] = -x * s + z * c
|
|
345
|
-
}
|
|
346
|
-
z = pythag(f, h)
|
|
347
|
-
q[i - 1] = z
|
|
348
|
-
c = f / z
|
|
349
|
-
s = h / z
|
|
350
|
-
f = c * g + s * y
|
|
351
|
-
x = -s * g + c * y
|
|
352
|
-
for (j = 0; j < m; j++) {
|
|
353
|
-
y = u[j][i - 1]
|
|
354
|
-
z = u[j][i]
|
|
355
|
-
u[j][i - 1] = y * c + z * s
|
|
356
|
-
u[j][i] = -y * s + z * c
|
|
357
|
-
}
|
|
503
|
+
z = this.pythag(f, h)
|
|
504
|
+
q[iMinus1] = z
|
|
505
|
+
c = f / z
|
|
506
|
+
s = h / z
|
|
507
|
+
f = c * g + s * y
|
|
508
|
+
x = -s * g + c * y
|
|
509
|
+
for(j=0; j<rows; j++) {
|
|
510
|
+
const row=u[j];
|
|
511
|
+
y = row[iMinus1]
|
|
512
|
+
z = row[i]
|
|
513
|
+
row[iMinus1] = y * c + z * s
|
|
514
|
+
row[i] = -y * s + z * c
|
|
358
515
|
}
|
|
359
|
-
e[l] = 0.0
|
|
360
|
-
e[k] = f
|
|
361
|
-
q[k] = x
|
|
362
516
|
}
|
|
517
|
+
e[l] = 0.0
|
|
518
|
+
e[k] = f
|
|
519
|
+
q[k] = x
|
|
363
520
|
}
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
521
|
+
}
|
|
522
|
+
const ql=q.length;
|
|
523
|
+
for(i=0; i<ql; i++) if(q[i]<precision) q[i]=0;
|
|
367
524
|
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
}
|
|
381
|
-
const vl=v.length;
|
|
382
|
-
for(k=0; k<vl; k++) {
|
|
383
|
-
temp = v[k][i];
|
|
384
|
-
v[k][i] = v[k][j];
|
|
385
|
-
v[k][j] = temp;
|
|
386
|
-
}
|
|
387
|
-
i = j
|
|
388
|
-
}
|
|
525
|
+
//sort eigenvalues
|
|
526
|
+
for(i=0; i<columns; i++) {
|
|
527
|
+
for(j=i-1; j>=0; j--) {
|
|
528
|
+
const qj=q[j];
|
|
529
|
+
const qi=q[i]
|
|
530
|
+
if(qj < qi) {
|
|
531
|
+
c = qj
|
|
532
|
+
q[j] = qi
|
|
533
|
+
q[i] = c
|
|
534
|
+
u.allRowsSwap(i,j);
|
|
535
|
+
v.allRowsSwap(i,j);
|
|
536
|
+
i=j
|
|
389
537
|
}
|
|
390
538
|
}
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
539
|
+
}
|
|
540
|
+
return {
|
|
541
|
+
U: u,
|
|
542
|
+
S: q,
|
|
543
|
+
V: v
|
|
396
544
|
}
|
|
397
545
|
}
|
|
398
|
-
|
|
399
|
-
module.exports = PCA;
|
|
546
|
+
PCA.prototype.svd=svd;
|