node-red-contrib-prib-functions 0.16.0 → 0.18.0
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/.vscode/extensions.json +6 -0
- package/README.md +44 -36
- package/dataAnalysis/arrayLast.js +8 -0
- package/dataAnalysis/arrayPairs.js +15 -0
- package/dataAnalysis/dataAnalysis.html +18 -0
- package/dataAnalysis/dataAnalysis.js +21 -9
- package/dataAnalysis/euclideanDistance.js +54 -0
- package/dataAnalysis/forNestedEach.js +24 -0
- package/matrix/icons/icons8-matrix-desktop-80.png +0 -0
- package/matrix/matrix.js +614 -0
- package/matrix/matrixNode.html +224 -0
- package/matrix/matrixNode.js +145 -0
- package/package.json +24 -6
- package/test/dataAnalysis.js +1 -1
- package/test/euclideanDistance.js +99 -0
- package/test/matrix/01base.js +338 -0
- package/test/matrix/10flowMatrices.js +68 -0
- package/testing/load-injector.html +2 -2
- package/testing/load-injector.js +9 -4
package/README.md
CHANGED
|
@@ -3,6 +3,7 @@
|
|
|
3
3
|
[Node-Red][1] nodes for various functions:
|
|
4
4
|
|
|
5
5
|
* Data Analysis - statistical metrics that has real time option
|
|
6
|
+
* Matrix
|
|
6
7
|
* Transform
|
|
7
8
|
* Test
|
|
8
9
|
* Load Injector
|
|
@@ -50,6 +51,11 @@ Array metrics:
|
|
|
50
51
|
* Normalise
|
|
51
52
|
* Standardization (Z-score Normalization)
|
|
52
53
|
|
|
54
|
+
Array data
|
|
55
|
+
* distances
|
|
56
|
+
* minimum distance(s) between points
|
|
57
|
+
* maximum distance(s) between points
|
|
58
|
+
|
|
53
59
|

|
|
54
60
|
|
|
55
61
|
example:
|
|
@@ -61,6 +67,36 @@ example:
|
|
|
61
67
|
|
|
62
68
|

|
|
63
69
|
|
|
70
|
+
|
|
71
|
+
------------------------------------------------------------
|
|
72
|
+
|
|
73
|
+
## Matrix
|
|
74
|
+
|
|
75
|
+
Define a matrix and perform various functions
|
|
76
|
+
|
|
77
|
+
* Define / Define Empty / Create / Create Like/ clone"
|
|
78
|
+
* Add / Add Row to Row / Add to Cell / Add Row / Subtract Cell
|
|
79
|
+
* Multiple / Multiple Cell / Divide Cell / Divide Row
|
|
80
|
+
* Transpose
|
|
81
|
+
* Adjoint
|
|
82
|
+
* Cofactor
|
|
83
|
+
* Complement Minor
|
|
84
|
+
* Identity
|
|
85
|
+
* Inverse
|
|
86
|
+
* Determinant
|
|
87
|
+
* Backward Substitution
|
|
88
|
+
* Forward Elimination
|
|
89
|
+
* Gaussian Elimination
|
|
90
|
+
* Reduced Row EchelonForm
|
|
91
|
+
* Row Echelon Form
|
|
92
|
+
* Nearly Equals / Is Square / Get Cell
|
|
93
|
+
* Sum Row
|
|
94
|
+
* Swap Rows
|
|
95
|
+
* To Array Object
|
|
96
|
+
|
|
97
|
+
|
|
98
|
+

|
|
99
|
+
|
|
64
100
|
------------------------------------------------------------
|
|
65
101
|
|
|
66
102
|
## Transform
|
|
@@ -115,7 +151,7 @@ With xlsx object one can use the function in [xlsx][7] against the object in fun
|
|
|
115
151
|
|
|
116
152
|
Example AVRO with schema
|
|
117
153
|
|
|
118
|
-

|
|
119
155
|
|
|
120
156
|
For Confluence schema contains a list of schemas in form {"<schema id>",<schema>}
|
|
121
157
|
|
|
@@ -154,7 +190,7 @@ Test example:
|
|
|
154
190
|
|
|
155
191
|
The levenshtein distance between two character strings.
|
|
156
192
|
|
|
157
|
-

|
|
158
194
|
|
|
159
195
|
------------------------------------------------------------
|
|
160
196
|
|
|
@@ -242,6 +278,12 @@ Test/example flow in test/generalTest.json
|
|
|
242
278
|
|
|
243
279
|
# Version
|
|
244
280
|
|
|
281
|
+
0.18.0 Add matrix node
|
|
282
|
+
|
|
283
|
+
0.17.0 Add finished wire to load injector
|
|
284
|
+
|
|
285
|
+
0.16.10 data analysis add eulcidean distance functions. Add array pairs
|
|
286
|
+
|
|
245
287
|
0.16.0 fix data analysis variance and stddev, add sample, add tests
|
|
246
288
|
|
|
247
289
|
0.14.2 fix general test flows. Add icon for data analysis
|
|
@@ -258,40 +300,6 @@ Test/example flow in test/generalTest.json
|
|
|
258
300
|
|
|
259
301
|
0.11.0 Transform for AVRO and snappy. Add JSON to CSV
|
|
260
302
|
|
|
261
|
-
0.10.2 Transform validate for array source, bug fixes on transform and add improvements to array to messages. Added node for levenshtein distance.
|
|
262
|
-
|
|
263
|
-
0.10.1 Real time weighted moving average, levenshtein Distance, for test allow testing of "infinity","-infinity" and "NaN" in JSON.
|
|
264
|
-
|
|
265
|
-
0.10.0 Many fixes to transform. Array and csv to various forms work. Added test to validate.
|
|
266
|
-
Improved test to allow for escape to put special characters into a string.
|
|
267
|
-
|
|
268
|
-
0.9.6 Enhance transform with csv ignore lead or trailing lines.
|
|
269
|
-
Add Array and CSV to Messages. Add in topic override
|
|
270
|
-
|
|
271
|
-
0.9.5 Enhance transform with path and setting source and target.
|
|
272
|
-
Outlier allowed to set number of deviations if median and reset or set stats
|
|
273
|
-
Add outlier detection and Pearson R realtime metrics.
|
|
274
|
-
|
|
275
|
-
0.8.1 Add realtime metrics to data analysis.
|
|
276
|
-
|
|
277
|
-
0.7.1
|
|
278
|
-
* fix json to table html and minor code improvements.
|
|
279
|
-
turn off debug mode on spawn process.
|
|
280
|
-
clear down timer on close for host available
|
|
281
|
-
* add Host Available
|
|
282
|
-
|
|
283
|
-
0.6.0
|
|
284
|
-
* add Spawn Process
|
|
285
|
-
* improve experimental transform with json to table html
|
|
286
|
-
|
|
287
|
-
0.5.0
|
|
288
|
-
* test node add select property tested for result
|
|
289
|
-
* dataAnalysis add property analysed
|
|
290
|
-
* add experimental transform
|
|
291
|
-
|
|
292
|
-
0.4.0 Add test, monitor flow, data analysis
|
|
293
|
-
|
|
294
|
-
0.0.1 base
|
|
295
303
|
|
|
296
304
|
# Author
|
|
297
305
|
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
if(Array.prototype.pairs==null)
|
|
2
|
+
Array.prototype.pairs=function (func) {
|
|
3
|
+
const l=this.length,li=l-1;
|
|
4
|
+
if(func) {
|
|
5
|
+
for (let i=0; i<li ; i++) {
|
|
6
|
+
for (let j =i+1; j<l; j++) {
|
|
7
|
+
func(this[i], this[j],i,j,this);
|
|
8
|
+
}
|
|
9
|
+
}
|
|
10
|
+
} else {
|
|
11
|
+
const r=[];
|
|
12
|
+
this.pairs(func===null? (p1,p2,i1,i2)=>r.push([p1,p2,i1,i2]) : (p1,p2)=>r.push([p1,p2]) )
|
|
13
|
+
return r;
|
|
14
|
+
}
|
|
15
|
+
}
|
|
@@ -6,6 +6,7 @@
|
|
|
6
6
|
defaults: {
|
|
7
7
|
name: {value:"",required:false},
|
|
8
8
|
action: {value:"avg",required:true},
|
|
9
|
+
columns:{value:"",required:false},
|
|
9
10
|
outputs: {value:(["realtime","realtimePredict"].includes(this.action)?3:2),required:true},
|
|
10
11
|
outliersBase: {value:"avg",required:false},
|
|
11
12
|
outliersStdDevs: {value:"3",required:false},
|
|
@@ -44,6 +45,11 @@
|
|
|
44
45
|
}
|
|
45
46
|
});
|
|
46
47
|
$("#node-input-action").change(function() {
|
|
48
|
+
if(["distances","distancesMax","distancesMin"].includes( $(this).val() )) {
|
|
49
|
+
$(".form-row-http-in-columns").show();
|
|
50
|
+
} else {
|
|
51
|
+
$(".form-row-http-in-columns").hide();
|
|
52
|
+
}
|
|
47
53
|
if(["movingAvgSimple","movingAvgWeighted","movingAvgExponential","realtime","realtimePredict"].includes( $(this).val() )) {
|
|
48
54
|
$(".form-row-http-in-term").show();
|
|
49
55
|
} else {
|
|
@@ -142,6 +148,9 @@
|
|
|
142
148
|
<option value="corelationship">Corelationship</option>
|
|
143
149
|
<option value="deltas">Deltas</option>
|
|
144
150
|
<option value="deltaNormalised">Deltas Normalised</option>
|
|
151
|
+
<option value="distances">Euclidean Distances</option>
|
|
152
|
+
<option value="distancesMax">Euclidean Distances Max</option>
|
|
153
|
+
<option value="distancesMin">Euclidean Distances Min</option>
|
|
145
154
|
<option value="pearsonR">Linear Correlation Coefficient</option>
|
|
146
155
|
<option value="max">Maximum</option>
|
|
147
156
|
<option value="avg">Mean</option>
|
|
@@ -166,6 +175,11 @@
|
|
|
166
175
|
</select>
|
|
167
176
|
</div>
|
|
168
177
|
|
|
178
|
+
<div class="form-row form-row-http-in-columns hide">
|
|
179
|
+
<label for="node-input-columns"><i class="icon-bookmark"></i> Columns</label>
|
|
180
|
+
<input type="text" id="node-input-columns" placeholder="columns">
|
|
181
|
+
</div>
|
|
182
|
+
|
|
169
183
|
<div class="form-row form-row-http-in-term hide">
|
|
170
184
|
<label for="node-input-term"><i class="icon-bookmark"></i> Term</label>
|
|
171
185
|
<input type="number" id="node-input-term" placeholder="term" min="2" max="100" step="1">
|
|
@@ -231,4 +245,8 @@ If real-time stats then a message can send directive instruction in topic:
|
|
|
231
245
|
<li>3 standard deviations of the mean (or median), around 99.7%
|
|
232
246
|
</ul>
|
|
233
247
|
</p>
|
|
248
|
+
<p>
|
|
249
|
+
Distance functions take in an array of points in an array and calculates the euclidean distance.
|
|
250
|
+
e.g. max input [ [1,1],[2,2],[3,3],[4,5]]), has output [{distance:5,points:[[1,1],[4,5]],index:[0,3]}]
|
|
251
|
+
</p>
|
|
234
252
|
</script>
|
|
@@ -1,13 +1,9 @@
|
|
|
1
1
|
const logger = new (require("node-red-contrib-logger"))("Data Analysis");
|
|
2
2
|
logger.sendInfo("Copyright 2020 Jaroslav Peter Prib");
|
|
3
3
|
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
},
|
|
8
|
-
writable: true,
|
|
9
|
-
configurable: true
|
|
10
|
-
});
|
|
4
|
+
require("./arrayLast");
|
|
5
|
+
const ed=require("./euclideanDistance.js");
|
|
6
|
+
require("./forNestedEach");
|
|
11
7
|
|
|
12
8
|
function crossNormalisedDeltas() {
|
|
13
9
|
const dataPoints=this.dataPoint;
|
|
@@ -120,8 +116,18 @@ function setDataPoint(value,term,node,dp) {
|
|
|
120
116
|
dp.weightedMovingAvg=(dp.weightedMovingAvg*2/count)/(count+1);
|
|
121
117
|
dp.exponentialWeightedMoving.forEach(c=>c.sample(value));
|
|
122
118
|
}
|
|
119
|
+
function getColumns(node) {
|
|
120
|
+
if(node.columns) {
|
|
121
|
+
if(node.columns instanceof Array) return node.columns
|
|
122
|
+
return eval("["+node.columns+"]");
|
|
123
|
+
}
|
|
124
|
+
}
|
|
123
125
|
functions={
|
|
124
126
|
avg:(d)=>functions.sum(d)/d.length,
|
|
127
|
+
arrayMax:(d)=>{ // not tested
|
|
128
|
+
let max=[],indices
|
|
129
|
+
a.forNestedEach((e,f,l)=>{const i=l[l.length-1];if(max[l]<e) {max=e,indices=l}})
|
|
130
|
+
},
|
|
125
131
|
covariance: (d)=>{
|
|
126
132
|
const means=[],covars=[],dl=d.length,dlminus1=dl-1,N=d[0].length;
|
|
127
133
|
d.forEach((e,i)=>{
|
|
@@ -141,11 +147,16 @@ functions={
|
|
|
141
147
|
corelationship:(d)=>{
|
|
142
148
|
const covars=functions.covariance(d);
|
|
143
149
|
const stdDev=d.map(c=>functions.stdDev(c));
|
|
144
|
-
|
|
145
|
-
return 1//covars.map((a)=>a.map((c,i)=>c==null?null:c/(stdDev[i+1]*stdDev[i])));
|
|
150
|
+
return covars.map((a)=>a.map((c,i)=>c==null?null:c/(stdDev[i+1]*stdDev[i])));
|
|
146
151
|
},
|
|
147
152
|
deltas :(d)=>d.map( (c,i)=>c-(d[i-1]||0) ),
|
|
148
153
|
deltaNormalised :(d)=>d.map( (c,i)=>(c-(d[i-1]||0)) / (d[i-1]||0) ),
|
|
154
|
+
distances: (d,term,node)=>{
|
|
155
|
+
if(node.columns) return ed.distances(d,getColumns(node))
|
|
156
|
+
return ed.distances(d);
|
|
157
|
+
},
|
|
158
|
+
distancesMin: (d,term,node)=>ed.minDistances(d,getColumns(node)),
|
|
159
|
+
distancesMax: (d,term,node)=>ed.maxDistances(d,getColumns(node)),
|
|
149
160
|
max: (d)=> Math.max(...d),
|
|
150
161
|
median:(d)=>{
|
|
151
162
|
const i=Math.floor(d.length/2);
|
|
@@ -384,6 +395,7 @@ module.exports = function (RED) {
|
|
|
384
395
|
break;
|
|
385
396
|
}
|
|
386
397
|
} catch(ex) {
|
|
398
|
+
if(logger.active) logger.send({label:"error input",action:node.action,message:ex.message,stack:ex.stack});
|
|
387
399
|
msg.error=ex.message;
|
|
388
400
|
if(node.maxErrorDisplay) {
|
|
389
401
|
--node.maxErrorDisplay;
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
require("./arrayPairs");
|
|
2
|
+
function distance(point1,point2) {
|
|
3
|
+
try{
|
|
4
|
+
if(point1==null | point2==null | point2.length==0 ) return NaN;
|
|
5
|
+
if(point1.length!==point2.length) throw Error("argument 1 array size not equal argument 2 array size") ;
|
|
6
|
+
const sum=point1.reduce((s,c,i)=>{
|
|
7
|
+
const d=c-point2[i];
|
|
8
|
+
return s+d*d;
|
|
9
|
+
},0);
|
|
10
|
+
return Math.sqrt(sum);
|
|
11
|
+
} catch(ex){
|
|
12
|
+
if(!(point1 instanceof Array)) throw Error("argument 1 not array") ;
|
|
13
|
+
if(!(point2 instanceof Array)) throw Error("argument 2 not array") ;
|
|
14
|
+
throw ex;
|
|
15
|
+
}
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
function distanceColumns(point1,point2,columns=[]) {
|
|
19
|
+
const sum=columns.reduce((s,c)=>{
|
|
20
|
+
const d=point1[c]-point2[c];
|
|
21
|
+
return s+d*d;
|
|
22
|
+
},0);
|
|
23
|
+
return Math.sqrt(sum)
|
|
24
|
+
}
|
|
25
|
+
function arrayPairs(points,f=distance,args) {
|
|
26
|
+
if(!(points instanceof Array)) throw Error("argument 1 not array") ;
|
|
27
|
+
const r=[];
|
|
28
|
+
points.pairs((p1,p2,i1,i2)=>{
|
|
29
|
+
r.push([ f(p1,p2,args), i1, i2])
|
|
30
|
+
});
|
|
31
|
+
return r;
|
|
32
|
+
}
|
|
33
|
+
function arrayPairsSet(points,f=distance,args,sf=(a,b)=>a>b,maxSetSize) {
|
|
34
|
+
let m,r=[],pi1,pi2;
|
|
35
|
+
points.pairs((p1,p2,i1,i2)=>{
|
|
36
|
+
const d=f(p1,p2,args);
|
|
37
|
+
if(m===d) {
|
|
38
|
+
r.push({points:[p1,p2],index:[i1,i2],distance:d});
|
|
39
|
+
if(maxSetSize && maxSetSize>r.length) r.shift
|
|
40
|
+
} else if(m==null | sf(m,d)==true) {
|
|
41
|
+
m=d;
|
|
42
|
+
r=[{points:[p1,p2],index:[i1,i2],distance:d}];
|
|
43
|
+
}
|
|
44
|
+
});
|
|
45
|
+
return r;
|
|
46
|
+
}
|
|
47
|
+
module.exports={
|
|
48
|
+
distance:distance,
|
|
49
|
+
distanceColumns:distanceColumns,
|
|
50
|
+
distances:arrayPairs,
|
|
51
|
+
distancesColumns:(a,columns)=>arrayPairs(a,distanceColumns,columns),
|
|
52
|
+
minDistances:(a,columns)=>arrayPairsSet(a, columns?distanceColumns:distance, columns, (a,b)=>a>b),
|
|
53
|
+
maxDistances:(a,columns)=>arrayPairsSet(a, columns?distanceColumns:distance, columns, (a,b)=>a<b)
|
|
54
|
+
};
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
if(Array.prototype.forNestedEach==null)
|
|
2
|
+
Array.prototype.forNestedEach=function (func,filter,level=[]) {
|
|
3
|
+
const l=level.length;
|
|
4
|
+
if(filter.hasOwnProperty(l)) {
|
|
5
|
+
const e=filter[l];
|
|
6
|
+
const callFilter=(v)=>!e.includes(v);
|
|
7
|
+
this.forEach((element, index, array) => {
|
|
8
|
+
if(callFilter(index)) return;
|
|
9
|
+
if(element instanceof Array) {
|
|
10
|
+
element.forNestedEach(func,filter,level.push(index))
|
|
11
|
+
} else {
|
|
12
|
+
func(element,index,array,level)
|
|
13
|
+
}
|
|
14
|
+
} )
|
|
15
|
+
return
|
|
16
|
+
}
|
|
17
|
+
this.forEach((element, index, array) => {
|
|
18
|
+
if(element instanceof Array) {
|
|
19
|
+
element.forNestedEach(func,filter,level.push(index))
|
|
20
|
+
} else {
|
|
21
|
+
func(element,index,array,level)
|
|
22
|
+
}
|
|
23
|
+
} )
|
|
24
|
+
}
|
|
Binary file
|