node-red-contrib-prib-functions 0.18.0 → 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 +22 -19
- 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 +546 -0
- package/dataAnalysis/svd.js +239 -0
- package/documentation/loadInjector.png +0 -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 +50 -50
- package/matrix/matrixNode.html +144 -154
- package/matrix/matrixNode.js +26 -9
- 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 +31 -8
- package/test/00-objectExtensions.js +94 -0
- package/test/01-base.js +88 -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 +3433 -0
- package/test/data/float32vector10.npy +0 -0
- package/test/data/flow.json +3433 -0
- package/test/data/int2matrix2x3.npy +0 -0
- package/test/data/package-lock.json +158 -0
- package/test/data/package.json +11 -0
- package/test/data/settings.js +544 -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/matrix/02base.js +36 -0
- package/test/transformNumPy.js +132 -0
- package/testing/data/countries.csv +250 -0
- package/testing/hostAvailable.html +0 -2
- package/testing/load-injector.html +76 -21
- package/testing/load-injector.js +35 -54
- package/testing/test.js +1 -0
- package/transform/NumPy.js +303 -0
- package/transform/transform.html +73 -19
- package/transform/transform.js +144 -8
- package/documentation/LoadInjector.JPG +0 -0
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
function generatedMatrixFunction(arg1) {
|
|
2
|
+
if(typeof arg1 !=="object") throw Error("no definitional JSON found");
|
|
3
|
+
Object.assign(this,{
|
|
4
|
+
args:[],
|
|
5
|
+
dimensionOrder:["row","column"],
|
|
6
|
+
returnValue:"this",
|
|
7
|
+
debug:false
|
|
8
|
+
},arg1);
|
|
9
|
+
const extendedArgs=[
|
|
10
|
+
this.dimensionOrder[0]+"StartOffset=0",
|
|
11
|
+
this.dimensionOrder[0]+"EndOffset",
|
|
12
|
+
this.dimensionOrder[1]+"StartOffset=0",
|
|
13
|
+
this.dimensionOrder[1]+"EndOffset",
|
|
14
|
+
"returnValue="+this.returnValue,
|
|
15
|
+
"matrixEndOffset=matrix.length-1",
|
|
16
|
+
"rowOffset",
|
|
17
|
+
"columnOffset",
|
|
18
|
+
"rowVectorOffset",
|
|
19
|
+
"columnVectorOffset",
|
|
20
|
+
"elementOffset",
|
|
21
|
+
"element",
|
|
22
|
+
"getElementArray=()=>matrix[rowOffset][columnOffset]",
|
|
23
|
+
"getElementVector=()=>matrix[elementOffset]",
|
|
24
|
+
"setElementArray=(v)=>matrix[rowOffset][columnOffset]=v",
|
|
25
|
+
"setElementVector=(v)=>matrix[elementOffset]=v",
|
|
26
|
+
"getElement=getElementVector",
|
|
27
|
+
"setElement=setElementVector",
|
|
28
|
+
"getMatrix=()=>Object.create(Object.getPrototypeOf(matrix))",
|
|
29
|
+
"arrayFunctions={"+
|
|
30
|
+
"forEachRowColumn:(call)=>{for(rowOffset=0;rowOffset<=rowEndOffset;rowOffset++) call()},"+
|
|
31
|
+
"forEachColumnRow:(call)=>{for(columnOffset=0;columnOffset<=columnEndOffset;columnOffset++) call()}"+
|
|
32
|
+
"}",
|
|
33
|
+
"vectorFunctions={}"
|
|
34
|
+
];
|
|
35
|
+
const functionCode=
|
|
36
|
+
"("+["matrix","rows",
|
|
37
|
+
"columns"
|
|
38
|
+
].concat(this.args,extendedArgs).join()+
|
|
39
|
+
")=>{\n"+
|
|
40
|
+
" const isArray=(matrix!==null&&matrix instanceof Array)\n"+
|
|
41
|
+
" if(isArray){\n"+
|
|
42
|
+
" rows=matrix.length\n"+
|
|
43
|
+
" columns=matrix[0].length\n"+
|
|
44
|
+
" getElement=getElementArray\n"+
|
|
45
|
+
" setElement=setElementArray\n"+
|
|
46
|
+
" }\n"+
|
|
47
|
+
" if(rows<=0) throw Error('rows < 1')\n"+
|
|
48
|
+
" if(columns<=0) throw Error('rows < 1')\n"+
|
|
49
|
+
" if(rowEndOffset==null) rowEndOffset=rows-1\n"+
|
|
50
|
+
" if(columnEndOffset==null) columnEndOffset=columns-1\n"+
|
|
51
|
+
(this.debug?"console.log({matrix:matrix,matrixEndOffset:matrixEndOffset,rowEndOffset:rowEndOffset,columnEndOffset:columnEndOffset,rows:rows,columns:columns})\n":"")+
|
|
52
|
+
" if(isArray){\n"+
|
|
53
|
+
" for("+this.dimensionOrder[0]+"Offset="+this.dimensionOrder[0]+"StartOffset;"+this.dimensionOrder[0]+"Offset<="+this.dimensionOrder[0]+"EndOffset;"+this.dimensionOrder[0]+"Offset++){\n"+
|
|
54
|
+
" const "+this.dimensionOrder[0]+"=matrix["+this.dimensionOrder[0]+"Offset]\n"+
|
|
55
|
+
(this.debug?"console.log({"+this.dimensionOrder[0]+"Offset:"+this.dimensionOrder[0]+"Offset,})\n":"")+"\n"+
|
|
56
|
+
" for("+this.dimensionOrder[1]+"Offset="+this.dimensionOrder[1]+"StartOffset;"+this.dimensionOrder[1]+"Offset<="+this.dimensionOrder[1]+"EndOffset;"+this.dimensionOrder[1]+"Offset++){\n"+
|
|
57
|
+
" const element=matrix["+this.dimensionOrder[1]+"Offset]\n"+
|
|
58
|
+
(this.debug?"console.log({"+this.dimensionOrder[1]+"Offset:"+this.dimensionOrder[1]+"Offset,element:element})\n":"")+
|
|
59
|
+
this.code+"\n"+
|
|
60
|
+
" }\n"+
|
|
61
|
+
" }\n"+
|
|
62
|
+
" } else {\n"+
|
|
63
|
+
" const innerSize="+(this.dimensionOrder[0]=="row"?"columns":"1")+"\n"+
|
|
64
|
+
" const outerSize="+(this.dimensionOrder[0]=="row"?"1":"columns")+"\n"+
|
|
65
|
+
" for("+this.dimensionOrder[0]+"Offset="+this.dimensionOrder[0]+"StartOffset;"+this.dimensionOrder[0]+"Offset<="+this.dimensionOrder[0]+"EndOffset;"+this.dimensionOrder[0]+"Offset++){\n"+
|
|
66
|
+
" const "+this.dimensionOrder[0]+"VectorOffset="+this.dimensionOrder[0]+"Offset*innerSize\n"+
|
|
67
|
+
(debug?"console.log({"+this.dimensionOrder[0]+"Offset:"+this.dimensionOrder[0]+"Offset,})\n":"")+
|
|
68
|
+
" for("+this.dimensionOrder[1]+"Offset="+this.dimensionOrder[1]+"StartOffset;"+this.dimensionOrder[1]+"Offset<="+this.dimensionOrder[1]+"EndOffset;"+this.dimensionOrder[1]+"Offset++){\n"+
|
|
69
|
+
" "+this.dimensionOrder[1]+"VectorOffset="+this.dimensionOrder[1]+"Offset*outerSize\n"+
|
|
70
|
+
" elementOffset=rowVectorOffset +columnOffset\n"+
|
|
71
|
+
" element=matrix[elementOffset];\n"+
|
|
72
|
+
(debug?"console.log({"+this.dimensionOrder[1]+"Offset:"+this.dimensionOrder[1]+"Offset,rowVectorOffset:rowVectorOffset,columnVectorOffset:columnVectorOffset,elementOffset:elementOffset,element:element})\n":"")+
|
|
73
|
+
" "+this.code+"\n"+
|
|
74
|
+
" }\n"+
|
|
75
|
+
" }\n"+
|
|
76
|
+
" }\n"+
|
|
77
|
+
(this.returnValue==undefined?" return this":" return returnValue")+
|
|
78
|
+
"\n}";
|
|
79
|
+
try{
|
|
80
|
+
const evaluatedCode=eval(functionCode);
|
|
81
|
+
return evaluatedCode;
|
|
82
|
+
} catch(ex) {
|
|
83
|
+
console.error("built function: \n"+functionCode)
|
|
84
|
+
console.error("error: "+ex.message)
|
|
85
|
+
console.error(ex.stack)
|
|
86
|
+
throw Error("code failed")
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
module.exports = generatedMatrixFunction;
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
function generatedVectorFunction(arg1) {
|
|
2
|
+
if(typeof arg1 !=="object") throw Error("no definitional JSON found");
|
|
3
|
+
Object.assign(this,{
|
|
4
|
+
args:[],
|
|
5
|
+
debug:false,
|
|
6
|
+
returnValue:"this"
|
|
7
|
+
},arg1);
|
|
8
|
+
const extendedArgs=["startOffset=0","endOffset=vector.length-1,returnValue="+this.returnValue]
|
|
9
|
+
const functionCode=
|
|
10
|
+
"("+["vector"].concat(this.args,extendedArgs).join()+")=>{"+
|
|
11
|
+
"for(let index=startOffset;index<=endOffset;index++){\n"+
|
|
12
|
+
this.code+";\n"+
|
|
13
|
+
"}\n"+
|
|
14
|
+
"return returnValue\n}";
|
|
15
|
+
try{
|
|
16
|
+
if(debug==true) console.log(functionCode);
|
|
17
|
+
const evaluatedCode=eval(functionCode);
|
|
18
|
+
return evaluatedCode;
|
|
19
|
+
} catch(ex) {
|
|
20
|
+
console.error("built function: "+functionCode)
|
|
21
|
+
console.error("error: "+ex.message)
|
|
22
|
+
throw Error("code failed")
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
module.exports = generatedVectorFunction;
|
|
@@ -0,0 +1,546 @@
|
|
|
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
|
|
12
|
+
}
|
|
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);
|
|
19
|
+
}
|
|
20
|
+
PCA.prototype.getDeviationScores=function(deviation) {
|
|
21
|
+
const transposed=this.transpose(deviation)
|
|
22
|
+
return this.multiply(transposed, deviation);
|
|
23
|
+
}
|
|
24
|
+
PCA.prototype.getVarianceCovariance=function(devSumOfSquares, sample) {
|
|
25
|
+
return this.scale(devSumOfSquares, 1/devSumOfSquares.length);
|
|
26
|
+
}
|
|
27
|
+
PCA.prototype.getVarianceCovarianceSample=function(devSumOfSquares) {
|
|
28
|
+
const factor=devSumOfSquares.length-1
|
|
29
|
+
return this.scale(devSumOfSquares, 1/factor);
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
PCA.prototype.getAdjustedData=function(data, ...vectorObjs){ // reduced after removing some dimensions
|
|
33
|
+
const vectors = vectorObjs.map((v)=>v.vector);
|
|
34
|
+
const matrixMinusMean = this.getDeviationMatrix(data);
|
|
35
|
+
const adjustedData = this.multiply(vectors, this.transpose(matrixMinusMean));
|
|
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
|
|
38
|
+
return {
|
|
39
|
+
adjustedData: adjustedData,
|
|
40
|
+
formattedAdjustedData:formatData(adjustedData, 2),
|
|
41
|
+
avgData: avgData,
|
|
42
|
+
selectedVectors: vectors
|
|
43
|
+
};
|
|
44
|
+
}
|
|
45
|
+
// Get original data set from reduced data set (decompress)
|
|
46
|
+
PCA.prototype.getOriginalData=function(adjustedData, vectors, avgData) {
|
|
47
|
+
const originalWithoutMean = this.transpose(multiply(transpose(vectors), adjustedData));
|
|
48
|
+
const originalWithMean = this.subtract(originalWithoutMean, avgData);
|
|
49
|
+
return {
|
|
50
|
+
originalData: originalWithMean,
|
|
51
|
+
formattedOriginalData: this.formatData(originalWithMean, 2)
|
|
52
|
+
}
|
|
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();
|
|
57
|
+
return (explained / total);
|
|
58
|
+
}
|
|
59
|
+
PCA.prototype.getEigenVectors=function(data) {
|
|
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
|
+
});
|
|
72
|
+
}
|
|
73
|
+
PCA.prototype.getTopResult=function(data){
|
|
74
|
+
const eigenVectors = this.getEigenVectors(data);
|
|
75
|
+
const sorted = eigenVectors.sort((a, b)=>b.eigenvalue-a.eigenvalue);
|
|
76
|
+
const selected = sorted[0].vector;
|
|
77
|
+
return this.getAdjustedData(data, selected);
|
|
78
|
+
}
|
|
79
|
+
PCA.prototype.formatData=function(data, precision) {
|
|
80
|
+
const factor= Math.pow(10, precision || 2);
|
|
81
|
+
return data.map((d,i)=>d.map((n)=>Math.round(n * factor) / factor))
|
|
82
|
+
}
|
|
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])
|
|
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
|
+
|
|
101
|
+
PCA.prototype.multiply=function(a, b){
|
|
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);
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
return result
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
PCA.prototype.subtract=function(a,b) {
|
|
118
|
+
if(!(a.length === b.length && a[0].length === b[0].length)) throw Error('Both A and B should have the same dimensions');
|
|
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
|
+
}
|
|
126
|
+
}
|
|
127
|
+
return result
|
|
128
|
+
}
|
|
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;
|
|
153
|
+
}
|
|
154
|
+
return result;
|
|
155
|
+
}
|
|
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;
|
|
171
|
+
}
|
|
172
|
+
return result;
|
|
173
|
+
}
|
|
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
|
|
202
|
+
}
|
|
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
|
|
212
|
+
}
|
|
213
|
+
PCA.prototype.scale=function(matrix,factor){return this.map(matrix,c=>c*factor)};
|
|
214
|
+
|
|
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
|
+
}
|
|
220
|
+
|
|
221
|
+
PCA.prototype.getMatrix=getMatrix
|
|
222
|
+
|
|
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]
|
|
352
|
+
}
|
|
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
|
|
385
|
+
}
|
|
386
|
+
}
|
|
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
|
+
}
|
|
397
|
+
|
|
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
|
|
413
|
+
}
|
|
414
|
+
}
|
|
415
|
+
for(let ri=i;ri<=rowOffsetEnd;ri++) u[ri][i]/=g;
|
|
416
|
+
}
|
|
417
|
+
uRow[i]++;
|
|
418
|
+
}
|
|
419
|
+
|
|
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
|
|
429
|
+
}
|
|
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)
|
|
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)
|
|
454
|
+
}
|
|
455
|
+
}
|
|
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;
|
|
463
|
+
}
|
|
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
|
|
502
|
+
}
|
|
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
|
|
515
|
+
}
|
|
516
|
+
}
|
|
517
|
+
e[l] = 0.0
|
|
518
|
+
e[k] = f
|
|
519
|
+
q[k] = x
|
|
520
|
+
}
|
|
521
|
+
}
|
|
522
|
+
const ql=q.length;
|
|
523
|
+
for(i=0; i<ql; i++) if(q[i]<precision) q[i]=0;
|
|
524
|
+
|
|
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
|
|
537
|
+
}
|
|
538
|
+
}
|
|
539
|
+
}
|
|
540
|
+
return {
|
|
541
|
+
U: u,
|
|
542
|
+
S: q,
|
|
543
|
+
V: v
|
|
544
|
+
}
|
|
545
|
+
}
|
|
546
|
+
PCA.prototype.svd=svd;
|