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.
@@ -0,0 +1,6 @@
1
+ {
2
+ "recommendations": [
3
+ "bibhasdn.git-easy",
4
+ "github.vscode-pull-request-github"
5
+ ]
6
+ }
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
  ![Data Analysis](documentation/DataAnalysis.JPG "Data Analysis")
54
60
 
55
61
  example:
@@ -61,6 +67,36 @@ example:
61
67
 
62
68
  ![Data Analysis example](documentation/DataAnalysisTest.JPG "Data Analysis example")
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
+ ![Matrix](documentation/matrix.jpg "Matrix")
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
- ![Transform AVRO](documentation/transformArvo.JPG "Transform AVRO example")
154
+ ![Transform AVRO](documentation/transformArvo.jpg "Transform AVRO example")
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
- ![Levenshtein Distance](documentation/levenshteinDistance.JPG "Levenshtein Distance")
193
+ ![Levenshtein Distance](documentation/levenshteinDistance.jpg "Levenshtein Distance")
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,8 @@
1
+ if(Array.prototype.last==null)
2
+ Object.defineProperty(Array.prototype, "last", {
3
+ value() {
4
+ return this[this.length-1];
5
+ },
6
+ writable: true,
7
+ configurable: true
8
+ });
@@ -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
- Object.defineProperty(Array.prototype, "last", {
5
- value() {
6
- return this[this.length-1];
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
- logger.send({label:"test",d:d,covars:covars,stdDev:stdDev});
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
+ }