node-red-contrib-prib-functions 0.20.4 → 0.22.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/README.md +82 -68
- package/dataAnalysis/arrayAverage.js +13 -0
- package/dataAnalysis/arraySum.js +14 -0
- package/dataAnalysis/arrayTypesForEach.js +5 -0
- package/dataAnalysis/autocorrelation.js +29 -0
- package/dataAnalysis/dataAnalysis.html +25 -9
- package/dataAnalysis/dataAnalysis.js +66 -43
- package/documentation/DataAnalysisRealtime.JPG +0 -0
- package/documentation/matrix.jpg +0 -0
- package/documentation/monitorSystem.JPG +0 -0
- package/documentation/monitorSystemTest.JPG +0 -0
- package/matrix/matrix.js +93 -8
- package/matrix/matrixNode.html +88 -55
- package/matrix/matrixNode.js +12 -5
- package/monitor/monitorSystem.html +2 -1
- package/monitor/monitorSystem.js +1 -1
- package/package.json +5 -1
- package/test/data/.config.nodes.json +1 -1
- package/test/data/.config.nodes.json.backup +2 -2
- package/test/data/.flow.json.backup +2486 -1814
- package/test/data/flow.json +2486 -1814
- package/test/data/package-lock.json +1 -1
- package/test/dataAnalysisExtensions.js +20 -1
- package/test/transformConfluence.js +1 -1
- package/testing/test.html +5 -1
- package/testing/test.js +79 -54
- package/arima/index.js +0 -18
package/README.md
CHANGED
|
@@ -4,22 +4,22 @@
|
|
|
4
4
|
|
|
5
5
|
* Data Analysis - statistical metrics that has real time option
|
|
6
6
|
* Matrix
|
|
7
|
-
*
|
|
8
|
-
*
|
|
7
|
+
* Transform
|
|
8
|
+
* Test
|
|
9
9
|
* Load Injector
|
|
10
|
-
*
|
|
11
|
-
*
|
|
12
|
-
*
|
|
13
|
-
*
|
|
14
|
-
*
|
|
15
|
-
*
|
|
10
|
+
* Monitor Flow
|
|
11
|
+
* append
|
|
12
|
+
* Spawn Process
|
|
13
|
+
* Host Available
|
|
14
|
+
* node.js os metrics
|
|
15
|
+
* Levenshtein Distance
|
|
16
16
|
|
|
17
17
|
------------------------------------------------------------
|
|
18
18
|
|
|
19
19
|
## Data Analysis
|
|
20
20
|
|
|
21
21
|
Real time metrics which are recalculated on single of data point and posted in msg.result.
|
|
22
|
-
Key and value can be selected from msg.payload.
|
|
22
|
+
Key and value can be selected from msg.payload. Includes lag (seasonal) along with delta(defference).
|
|
23
23
|
Sending message with topic"@stats" places message with all stats on second port.
|
|
24
24
|
If realtime metrics then a third port is shown where the message is sent if it is an outlier
|
|
25
25
|
being outside 3 standard deviations from mean. This can be changed to median and number of deviations.
|
|
@@ -27,10 +27,11 @@ being outside 3 standard deviations from mean. This can be changed to median and
|
|
|
27
27
|

|
|
28
28
|

|
|
29
29
|
|
|
30
|
-
|
|
31
30
|
A set of data analysis functions that can be run over an array of data
|
|
32
31
|
|
|
33
32
|
Single value metrics:
|
|
33
|
+
* autocorrelation
|
|
34
|
+
* Autocovariance
|
|
34
35
|
* Average/Mean
|
|
35
36
|
* Maximum
|
|
36
37
|
* Median
|
|
@@ -78,7 +79,7 @@ example:
|
|
|
78
79
|
|
|
79
80
|
Define a matrix and perform various functions
|
|
80
81
|
|
|
81
|
-
* Define / Define Empty / Create / Create Like/ clone
|
|
82
|
+
* Define / Define Empty / Create / Create Like/ clone
|
|
82
83
|
* Add / Add Row to Row / Add to Cell / Add Row / Subtract Cell
|
|
83
84
|
* Multiple / Multiple Cell / Divide Cell / Divide Row
|
|
84
85
|
* Transpose
|
|
@@ -110,48 +111,48 @@ Messages generates a message for each row or record.
|
|
|
110
111
|
|
|
111
112
|
Transformations:
|
|
112
113
|
|
|
113
|
-
*
|
|
114
|
-
*
|
|
115
|
-
*
|
|
116
|
-
*
|
|
117
|
-
*
|
|
118
|
-
*
|
|
114
|
+
* Array to CSV
|
|
115
|
+
* Array to HTML
|
|
116
|
+
* Array to ISO8385
|
|
117
|
+
* Array to Messages
|
|
118
|
+
* Array to xlsx / xlsx object (excel uses [xlsx][7])
|
|
119
|
+
* AVRO to JSON (uses [avsc][6])
|
|
119
120
|
* Buffer to comprossed
|
|
120
|
-
*
|
|
121
|
+
* Confluence to JSON
|
|
121
122
|
* Compressed to Buffer
|
|
122
123
|
* Compressed to String
|
|
123
124
|
* COmpressed to JSON
|
|
124
|
-
*
|
|
125
|
-
*
|
|
126
|
-
*
|
|
127
|
-
*
|
|
128
|
-
*
|
|
129
|
-
*
|
|
130
|
-
*
|
|
131
|
-
*
|
|
132
|
-
*
|
|
133
|
-
*
|
|
134
|
-
*
|
|
135
|
-
*
|
|
136
|
-
*
|
|
137
|
-
*
|
|
138
|
-
*
|
|
139
|
-
*
|
|
140
|
-
*
|
|
141
|
-
*
|
|
142
|
-
*
|
|
143
|
-
*
|
|
144
|
-
*
|
|
145
|
-
*
|
|
146
|
-
*
|
|
147
|
-
*
|
|
148
|
-
*
|
|
149
|
-
*
|
|
150
|
-
*
|
|
151
|
-
*
|
|
152
|
-
*
|
|
153
|
-
*
|
|
154
|
-
*
|
|
125
|
+
* CSV to Array
|
|
126
|
+
* CSV to HTML
|
|
127
|
+
* CSV to Messages
|
|
128
|
+
* CSVWithHeader to Array
|
|
129
|
+
* CSVWithHeader to HTML
|
|
130
|
+
* CSVWithHeader to JSON
|
|
131
|
+
* ISO8385 to Array
|
|
132
|
+
* ISO8385 to JSON
|
|
133
|
+
* JSON to Array
|
|
134
|
+
* JSON to Confluence
|
|
135
|
+
* JSON to CSV
|
|
136
|
+
* JSON to AVRO (uses [avsc][6])
|
|
137
|
+
* JSON to ISO8385
|
|
138
|
+
* JSON to Messages
|
|
139
|
+
* JSON to String
|
|
140
|
+
* JSON to xlsx / xlsx object (excel uses [xlsx][7])
|
|
141
|
+
* JSON to XML (uses [fast-xml-parser][4])
|
|
142
|
+
* String to JSON
|
|
143
|
+
* path to Basename
|
|
144
|
+
* path to Dirname
|
|
145
|
+
* path to Extname
|
|
146
|
+
* path to Format
|
|
147
|
+
* path to Is Absolute
|
|
148
|
+
* path to Join
|
|
149
|
+
* path to Parse
|
|
150
|
+
* path to Normalize
|
|
151
|
+
* path to Resolve
|
|
152
|
+
* snappy compress (uses [snappy][5], must install separately)
|
|
153
|
+
* snappy uncompress (uses [snappy][5], must install separately)
|
|
154
|
+
* xlsx / xlsx object to array/JSON (excel uses [xlsx][7])
|
|
155
|
+
* XML to JSON (uses [fast-xml-parser][4])
|
|
155
156
|
|
|
156
157
|
Note, snappy needs to be installed separately as can have issues with auto install as build binaries.
|
|
157
158
|
|
|
@@ -219,7 +220,6 @@ Test example:
|
|
|
219
220
|
|
|
220
221
|

|
|
221
222
|
|
|
222
|
-
|
|
223
223
|
------------------------------------------------------------
|
|
224
224
|
|
|
225
225
|
## Monitor Flow
|
|
@@ -235,34 +235,29 @@ Test example:
|
|
|
235
235
|
|
|
236
236
|
------------------------------------------------------------
|
|
237
237
|
|
|
238
|
-
##
|
|
238
|
+
## Host Available
|
|
239
239
|
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
Takes in messages that starts a process with ability to add environment values.
|
|
244
|
-
Message can be sent to kill the process.
|
|
240
|
+
Test if host is available sending msg to up or down port so action can be taken.
|
|
241
|
+
Message only sent on state change or if message is sent which doesn't have topic refreshHostAvailable.
|
|
242
|
+
This topic forces a check rather than time check which can be set.
|
|
245
243
|
|
|
246
|
-

|
|
247
245
|
|
|
248
246
|
Test example:
|
|
249
247
|
|
|
250
|
-

|
|
252
249
|
|
|
253
250
|
------------------------------------------------------------
|
|
254
251
|
|
|
255
|
-
##
|
|
252
|
+
## Monitor System
|
|
256
253
|
|
|
257
|
-
|
|
258
|
-
Message only sent on state change or if message is sent which doesn't have topic refreshHostAvailable.
|
|
259
|
-
This topic forces a check rather than time check which can be set.
|
|
254
|
+
System monitoring metrics
|
|
260
255
|
|
|
261
|
-

|
|
262
257
|
|
|
263
258
|
Test example:
|
|
264
259
|
|
|
265
|
-

|
|
266
261
|
|
|
267
262
|
------------------------------------------------------------
|
|
268
263
|
|
|
@@ -278,6 +273,22 @@ Test example:
|
|
|
278
273
|
|
|
279
274
|
------------------------------------------------------------
|
|
280
275
|
|
|
276
|
+
## Spawn Process
|
|
277
|
+
|
|
278
|
+
Spawn process as per node.js manual with ability to set working directory, environment variables
|
|
279
|
+
and argument passed to process. STDOUT and STDERR are sent as individual messages.
|
|
280
|
+
RC port is sent a message on closure.
|
|
281
|
+
Takes in messages that starts a process with ability to add environment values.
|
|
282
|
+
Message can be sent to kill the process.
|
|
283
|
+
|
|
284
|
+

|
|
285
|
+
|
|
286
|
+
Test example:
|
|
287
|
+
|
|
288
|
+

|
|
289
|
+
|
|
290
|
+
------------------------------------------------------------
|
|
291
|
+
|
|
281
292
|
# Install
|
|
282
293
|
|
|
283
294
|
Run the following command in the root directory of your Node-RED install
|
|
@@ -292,15 +303,18 @@ Test/example flow in test/generalTest.json
|
|
|
292
303
|
|
|
293
304
|
# Version
|
|
294
305
|
|
|
306
|
+
0.22.0 Add autocovariance + autocorealationship to real time data analystics, improves test
|
|
307
|
+
|
|
308
|
+
0.21.0 Add lag/seasonal to real time data analystics
|
|
309
|
+
|
|
310
|
+
0.20.3 Add difference + monitor system
|
|
311
|
+
|
|
295
312
|
0.19.0 Improve load injector, fix bug in test comparing buffers, add compression tranforms
|
|
296
313
|
|
|
297
314
|
0.18.0 Add matrix node
|
|
298
315
|
|
|
299
316
|
0.17.0 Add finished wire to load injector
|
|
300
317
|
|
|
301
|
-
0.16.0 fix data analysis variance and stddev, add sample, add tests
|
|
302
|
-
|
|
303
|
-
|
|
304
318
|
|
|
305
319
|
# Author
|
|
306
320
|
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
const arrayTypesForEach=require("./arrayTypesForEach.js")
|
|
2
|
+
require("./arraySum.js")
|
|
3
|
+
|
|
4
|
+
arrayTypesForEach(object=>{
|
|
5
|
+
if(object.prototype.average==null)
|
|
6
|
+
Object.defineProperty(object.prototype, "average", {
|
|
7
|
+
value(i=0,end=this.length-1) {
|
|
8
|
+
return this.sum(i,end)/this.length;
|
|
9
|
+
},
|
|
10
|
+
writable: true,
|
|
11
|
+
configurable: true
|
|
12
|
+
});
|
|
13
|
+
})
|
package/dataAnalysis/arraySum.js
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
/*
|
|
1
2
|
[Array,Float32Array,Float64Array].forEach(object=>{
|
|
2
3
|
if(object.prototype.sum==null)
|
|
3
4
|
Object.defineProperty(object.prototype, "sum", {
|
|
@@ -8,4 +9,17 @@
|
|
|
8
9
|
writable: true,
|
|
9
10
|
configurable: true
|
|
10
11
|
});
|
|
12
|
+
})
|
|
13
|
+
*/
|
|
14
|
+
const arrayTypesForEach=require("./arrayTypesForEach.js")
|
|
15
|
+
arrayTypesForEach(object=>{
|
|
16
|
+
if(object.prototype.sum==null)
|
|
17
|
+
Object.defineProperty(object.prototype, "sum", {
|
|
18
|
+
value(i=0,end=this.length-1,sum=0) {
|
|
19
|
+
for(;i<=end;i++) sum+=this[i];
|
|
20
|
+
return sum;
|
|
21
|
+
},
|
|
22
|
+
writable: true,
|
|
23
|
+
configurable: true
|
|
24
|
+
});
|
|
11
25
|
})
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
const typedArrays= {Array:Array,Int8Array:Int8Array,Uint8Array:Uint8Array,Uint8ClampedArray:Uint8ClampedArray,Int16Array:Int16Array,Uint16Array:Uint16Array,
|
|
2
|
+
Int32Array:Int32Array,Uint32Array:Uint32Array,Float32Array:Float32Array,
|
|
3
|
+
Float64Array:Float64Array,BigInt64Array:BigInt64Array,BigUint64Array:BigUint64Array}
|
|
4
|
+
|
|
5
|
+
module.exports=(call)=>Object.keys(typedArrays).map(t=>typedArrays[t]).forEach(type=>call(type))
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
|
|
2
|
+
require("./arrayAverage.js");
|
|
3
|
+
const arrayTypesForEach=require("./arrayTypesForEach.js")
|
|
4
|
+
arrayTypesForEach(object=>{
|
|
5
|
+
if(object.prototype.autocovariance==null)
|
|
6
|
+
Object.defineProperty(object.prototype, "autocovariance", {
|
|
7
|
+
value(lag,avg=this.average()) {
|
|
8
|
+
let autoCov = 0
|
|
9
|
+
const vectorSize=this.length
|
|
10
|
+
const vectorSizeLagged=vectorSize-lag
|
|
11
|
+
for(let i=0; i<vectorSizeLagged; i++) {
|
|
12
|
+
autoCov += ((this[i+lag])-avg)*(this[i]-avg)
|
|
13
|
+
}
|
|
14
|
+
return (1/(vectorSize-1))*autoCov
|
|
15
|
+
},
|
|
16
|
+
writable: true,
|
|
17
|
+
configurable: true
|
|
18
|
+
})
|
|
19
|
+
})
|
|
20
|
+
arrayTypesForEach(object=>{
|
|
21
|
+
if(object.prototype.autocorrelation==null)
|
|
22
|
+
Object.defineProperty(object.prototype, "autocorrelation", {
|
|
23
|
+
value(lag,avg=this.average()) {
|
|
24
|
+
return this.autocovariance(lag, avg) / this.autocovariance(0, avg)
|
|
25
|
+
},
|
|
26
|
+
writable: true,
|
|
27
|
+
configurable: true
|
|
28
|
+
})
|
|
29
|
+
})
|
|
@@ -11,7 +11,7 @@
|
|
|
11
11
|
outputs: {value:(["realtime","realtimePredict"].includes(this.action)?3:2),required:true},
|
|
12
12
|
outliersBase: {value:"avg",required:false},
|
|
13
13
|
outliersStdDevs: {value:"3",required:false},
|
|
14
|
-
term: {value:
|
|
14
|
+
term: {value:10,required:false},
|
|
15
15
|
keyProperty: {value:"msg.topic",required:false},
|
|
16
16
|
dataProperty: {value:"msg.payload",required:false},
|
|
17
17
|
dataProperties: {value:["msg.payload[0]","msg.payload[1]"],required:false}
|
|
@@ -28,6 +28,7 @@
|
|
|
28
28
|
},
|
|
29
29
|
oneditprepare: function() {
|
|
30
30
|
const node=this;
|
|
31
|
+
node.lag??=1
|
|
31
32
|
$("#node-input-keyProperty").change(function() {
|
|
32
33
|
if( [null,""].includes(node.keyProperty) ) {
|
|
33
34
|
$(this).val("msg.topic");
|
|
@@ -46,7 +47,7 @@
|
|
|
46
47
|
}
|
|
47
48
|
});
|
|
48
49
|
$("#node-input-action").change(function() {
|
|
49
|
-
if(["differenceSeasonal","differenceSeasonalSecondOrder"].includes( $(this).val() )) {
|
|
50
|
+
if(["autocovariance","autocorrelation","differenceSeasonal","differenceSeasonalSecondOrder","realtime","realtimePredict"].includes( $(this).val() )) {
|
|
50
51
|
$(".form-row-http-in-lag").show();
|
|
51
52
|
} else {
|
|
52
53
|
$(".form-row-http-in-lag").hide();
|
|
@@ -69,10 +70,17 @@
|
|
|
69
70
|
if(node.term>1) $(this).val(1);
|
|
70
71
|
});
|
|
71
72
|
} else {
|
|
73
|
+
$("#node-input-lag").attr({min:1,max:1000,step:1});
|
|
74
|
+
$("#node-input-lag").change(function() {
|
|
75
|
+
const v=$(this).val();
|
|
76
|
+
if(v<1) $(this).val(1);
|
|
77
|
+
if(v>1000) $(this).val(1000);
|
|
78
|
+
});
|
|
72
79
|
$("#node-input-term").attr({min:1,max:100,step:1});
|
|
73
80
|
$("#node-input-term").change(function() {
|
|
74
|
-
|
|
75
|
-
if(
|
|
81
|
+
const v=$(this).val()
|
|
82
|
+
if(v<1) $(this).val(1);
|
|
83
|
+
if(v>100) $(this).val(100);
|
|
76
84
|
});
|
|
77
85
|
}
|
|
78
86
|
if(["realtime","realtimePredict"].includes( $(this).val() )) {
|
|
@@ -94,8 +102,8 @@
|
|
|
94
102
|
$(".node-input-dataProperties-container-row").hide();
|
|
95
103
|
$(".form-row-http-in-dataProperty").show();
|
|
96
104
|
}
|
|
97
|
-
$("#node-input-term").change();
|
|
98
105
|
$("#node-input-lag").change();
|
|
106
|
+
$("#node-input-term").change();
|
|
99
107
|
});
|
|
100
108
|
|
|
101
109
|
node.dataProperties.forEach((r)=>addDataProperty(r));
|
|
@@ -150,6 +158,8 @@
|
|
|
150
158
|
<div class="form-row">
|
|
151
159
|
<label for="node-input-action"><i class="fa fa-list-ul"></i> Method</label>
|
|
152
160
|
<select id="node-input-action" placeholder="action">
|
|
161
|
+
<option value="autocovariance">Autocovariance</option>
|
|
162
|
+
<option value="autocorrelation">Autocorrelation</option>
|
|
153
163
|
<option value="avg">Average</option>
|
|
154
164
|
<option value="covariance">Covariance</option>
|
|
155
165
|
<option value="corelationship">Corelationship</option>
|
|
@@ -248,12 +258,18 @@ Data is not persisted so metrics start from zero sample set on node recycle.
|
|
|
248
258
|
<p>
|
|
249
259
|
If real-time stats then a message can send directive instruction in topic:
|
|
250
260
|
<dl>
|
|
251
|
-
<dt>"@predict <key>"
|
|
252
|
-
<dt>"@stats"
|
|
253
|
-
<dt>"@stats reset"
|
|
254
|
-
<dt>"@stats set"
|
|
261
|
+
<dt>"@predict <key>"</dt><dd></dd>Send send predictions to second port for selected key.
|
|
262
|
+
<dt>"@stats"</dt><dd></dd>send all stored metrics and retained datapoints to second port.
|
|
263
|
+
<dt>"@stats reset"</dt><dd>Reset all stats and "@stats reset <key>" will reset a particular data point.</dd>
|
|
264
|
+
<dt>"@stats set"</dt><dd>Set stats with ith msg.payload and "@stats set <a data point>" will set a particular data point with msg.payload.</dd>
|
|
255
265
|
</dl>
|
|
256
266
|
</p>
|
|
267
|
+
<dl>
|
|
268
|
+
<dt>Lag</dt><dd>If greater that 1 generates seasonal difference with degree lag</dd>
|
|
269
|
+
<dt>Term</dt><dd>The depth of moving Average</dd>
|
|
270
|
+
</dl>
|
|
271
|
+
<p>
|
|
272
|
+
</p>
|
|
257
273
|
<p>
|
|
258
274
|
Outliers are not within:
|
|
259
275
|
<ul>
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
const logger = new (require("node-red-contrib-logger"))("Data Analysis");
|
|
2
2
|
logger.sendInfo("Copyright 2020 Jaroslav Peter Prib");
|
|
3
3
|
|
|
4
|
+
require("./autocorrelation.js");
|
|
4
5
|
require("./arrayLast");
|
|
5
6
|
require("./arrayDifference")
|
|
6
7
|
require("./arrayDifferenceSeasonal")
|
|
@@ -69,10 +70,10 @@ EMA.prototype.sample=function(value) {
|
|
|
69
70
|
return this;
|
|
70
71
|
}
|
|
71
72
|
|
|
72
|
-
function setDataPoint(value,term,node,
|
|
73
|
-
if(logger.active) logger.send({label:"setDataPoint",value:value,term,
|
|
74
|
-
if(!
|
|
75
|
-
Object.assign(
|
|
73
|
+
function setDataPoint(value,term,node,dataPoint) {
|
|
74
|
+
if(logger.active) logger.send({label:"setDataPoint",value:value,term,dataPoint});
|
|
75
|
+
if(!dataPoint.values) {
|
|
76
|
+
Object.assign(dataPoint,{
|
|
76
77
|
values:[],
|
|
77
78
|
avg:0,
|
|
78
79
|
count:0,
|
|
@@ -83,43 +84,44 @@ function setDataPoint(value,term,node,dp) {
|
|
|
83
84
|
sum:0,
|
|
84
85
|
sumSquared:0,
|
|
85
86
|
sumCubed:0,
|
|
86
|
-
term:term,
|
|
87
|
+
term:term??node.term,
|
|
87
88
|
weightedMovingSum:0,
|
|
88
89
|
exponentialWeightedMoving:[new EMA(0.25),new EMA(0.5),new EMA(0.75)]
|
|
89
90
|
});
|
|
90
|
-
}
|
|
91
|
-
;
|
|
92
|
-
const count=++dp.count,values=dp.values;
|
|
91
|
+
};
|
|
92
|
+
const count=++dataPoint.count,values=dataPoint.values;
|
|
93
93
|
values.push(value);
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
94
|
+
const movingTerm=Math.min(values.length,dataPoint.term)
|
|
95
|
+
dataPoint.isMaxSize=(values.length>dataPoint.maxSize);
|
|
96
|
+
dataPoint.removedMovingValue=(dataPoint.isMaxSize?values[values.length-dataPoint.term]:0);
|
|
97
|
+
dataPoint.removedValue=(dataPoint.isMaxSize?values.shift():0);
|
|
98
|
+
const removedMovingValue=dataPoint.removedMovingValue;
|
|
99
|
+
dataPoint.max=Math.max(dataPoint.max||value,value);
|
|
100
|
+
dataPoint.min=Math.min(dataPoint.min||value,value);
|
|
101
|
+
dataPoint.range=dataPoint.max-dataPoint.min;
|
|
102
|
+
dataPoint.sum+=value;
|
|
103
|
+
dataPoint.sumSquared+=Math.pow(value,2);
|
|
104
|
+
dataPoint.sumCubed+=Math.pow(value,3);
|
|
105
|
+
dataPoint.movingSum+=value-removedMovingValue;
|
|
106
|
+
dataPoint.movingSumSquared+=Math.pow(value,2)-Math.pow(removedMovingValue,2);
|
|
107
|
+
dataPoint.movingSumCubed+=Math.pow(value,3)-Math.pow(removedMovingValue,3);
|
|
108
|
+
dataPoint.avg=dataPoint.sum/count;
|
|
109
|
+
const avg=dataPoint.avg;
|
|
110
|
+
dataPoint.normalised=dataPoint.range ? (value-avg)/dataPoint.range : 0;
|
|
111
|
+
dataPoint.movingAvg=dataPoint.movingSum/movingTerm;
|
|
112
|
+
dataPoint.variance=dataPoint.sumSquared/count - Math.pow(avg,2);
|
|
113
|
+
dataPoint.stdDev=Math.sqrt(dataPoint.variance);
|
|
114
|
+
dataPoint.movingVariance=dataPoint.movingSumSquared/movingTerm - Math.pow(dataPoint.movingAvg,2);
|
|
115
|
+
dataPoint.movingStdDev=Math.sqrt(dataPoint.movingVariance);
|
|
116
|
+
dataPoint.median=functions.median(values);
|
|
117
|
+
dataPoint.standardized=( (value-avg)/dataPoint.stdDev )||0;
|
|
118
|
+
dataPoint.movingStandardized=( (value-dataPoint.movingAvg)/dataPoint.movingStdDev )||0;
|
|
119
|
+
dataPoint.skewness=(dataPoint.sumCubed-3*avg*dataPoint.variance-Math.pow(avg,3))/dataPoint.variance*dataPoint.stdDev;
|
|
120
|
+
dataPoint.movingSkewness=(dataPoint.movingSumCubed-3*dataPoint.movingAvg*dataPoint.movingVariance-Math.pow(dataPoint.movingAvg,3))/dataPoint.movingVariance*dataPoint.stdDev;
|
|
121
|
+
dataPoint.outlier=node.outliersFunction(node,dataPoint,value);
|
|
122
|
+
dataPoint.weightedMovingSum+=count*value;
|
|
123
|
+
dataPoint.weightedMovingAvg=(dataPoint.weightedMovingAvg*2/count)/(count+1);
|
|
124
|
+
dataPoint.exponentialWeightedMoving.forEach(c=>c.sample(value));
|
|
123
125
|
}
|
|
124
126
|
function getColumns(node) {
|
|
125
127
|
if(node.columns) {
|
|
@@ -128,6 +130,8 @@ function getColumns(node) {
|
|
|
128
130
|
}
|
|
129
131
|
}
|
|
130
132
|
functions={
|
|
133
|
+
autocovariance:(d,term,node)=>d.autocovariance(node.lag),
|
|
134
|
+
autocorrelation:(d,term,node)=>d.autocorrelation(node.lag),
|
|
131
135
|
avg:(d)=>functions.sum(d)/d.length,
|
|
132
136
|
arrayMax:(d)=>{ // not tested
|
|
133
137
|
let max=[],indices
|
|
@@ -223,9 +227,9 @@ functions={
|
|
|
223
227
|
}
|
|
224
228
|
}
|
|
225
229
|
node.samples++;
|
|
226
|
-
for(let
|
|
227
|
-
v=d[i];
|
|
228
|
-
dp=node.dataPoints[i];
|
|
230
|
+
for(let i=0; i<node.dataProperties.length; i++) {
|
|
231
|
+
const v=d[i];
|
|
232
|
+
const dp=node.dataPoints[i];
|
|
229
233
|
dp.sum+=v;
|
|
230
234
|
dp.sumSquared+=v*v;
|
|
231
235
|
for(let j=i+1; j<node.dataProperties.length; j++) {
|
|
@@ -280,10 +284,23 @@ functions={
|
|
|
280
284
|
}
|
|
281
285
|
setDataPoint(d.value,term,node,dp);
|
|
282
286
|
if(dp.delta) {
|
|
283
|
-
|
|
287
|
+
if(dp.values.length>1)
|
|
288
|
+
setDataPoint(d.value-dp.values[dp.values.length-2],term,node,dp.delta);
|
|
284
289
|
} else {
|
|
285
290
|
dp.delta={};
|
|
286
291
|
}
|
|
292
|
+
if(node.lag>1) {
|
|
293
|
+
const vectorSize=dp.values.length
|
|
294
|
+
if(dp.lag) {
|
|
295
|
+
if(node.lag<=vectorSize){
|
|
296
|
+
setDataPoint(d.value-dp.values[vectorSize-node.lag],term,node,dp.lag)
|
|
297
|
+
const values=dp.lag.values
|
|
298
|
+
if(values.length>1) setDataPoint(values[values.length-1]-values[values.length-2],term,node,dp.lag.delta)
|
|
299
|
+
}
|
|
300
|
+
} else {
|
|
301
|
+
dp.lag={delta:{}}
|
|
302
|
+
}
|
|
303
|
+
}
|
|
287
304
|
return dp;
|
|
288
305
|
},
|
|
289
306
|
sampleVariance:(d)=>{
|
|
@@ -310,11 +327,15 @@ module.exports = function (RED) {
|
|
|
310
327
|
function dataAnalysisNode(n) {
|
|
311
328
|
RED.nodes.createNode(this, n);
|
|
312
329
|
const node=Object.assign(this,
|
|
313
|
-
{outliersStdDevs:3,crossNormalisedDeltas:crossNormalisedDeltas.bind(this)},
|
|
330
|
+
{outliersStdDevs:3,crossNormalisedDeltas:crossNormalisedDeltas.bind(this),lag:1,term:3},
|
|
314
331
|
n,
|
|
315
332
|
{maxErrorDisplay:10,dataPoint:{}}
|
|
316
333
|
);
|
|
317
334
|
try{
|
|
335
|
+
node.lag=Number(node.lag)
|
|
336
|
+
if(Number(node.term)==NaN)throw Error("term not a number, value:"+JSON.stringify(node.term))
|
|
337
|
+
node.term=Number(node.term)
|
|
338
|
+
node.maxSize=Math.max(node.term,node.lag)
|
|
318
339
|
if(functions.hasOwnProperty(node.action)) {
|
|
319
340
|
node.actionfunction=functions[node.action];
|
|
320
341
|
} else {
|
|
@@ -349,6 +370,7 @@ module.exports = function (RED) {
|
|
|
349
370
|
node.getData=eval(node.getDatafunction);
|
|
350
371
|
node.status({fill:"green",shape:"ring",text:"Ready"});
|
|
351
372
|
} catch(ex) {
|
|
373
|
+
if(logger.active) logger.send({label:"initialise error",action:node.action,message:ex.message,stack:ex.stack});
|
|
352
374
|
logger.send({label:"initialise error",node:n});
|
|
353
375
|
node.error(ex);
|
|
354
376
|
node.status({fill:"red",shape:"ring",text:"Invalid setup "+ex.message});
|
|
@@ -388,6 +410,7 @@ module.exports = function (RED) {
|
|
|
388
410
|
throw Error("unknown");
|
|
389
411
|
}
|
|
390
412
|
} catch(ex) {
|
|
413
|
+
if(logger.active) logger.send({label:"error input",action:node.action,message:ex.message,stack:ex.stack});
|
|
391
414
|
node.error(msg.topic+" failed "+ex.message);
|
|
392
415
|
}
|
|
393
416
|
return;
|
|
@@ -404,10 +427,10 @@ module.exports = function (RED) {
|
|
|
404
427
|
break;
|
|
405
428
|
}
|
|
406
429
|
} catch(ex) {
|
|
407
|
-
if(logger.active) logger.send({label:"error input",action:node.action,message:ex.message,stack:ex.stack});
|
|
408
430
|
msg.error=ex.message;
|
|
409
431
|
if(node.maxErrorDisplay) {
|
|
410
432
|
--node.maxErrorDisplay;
|
|
433
|
+
logger.send({label:"error input",action:node.action,message:ex.message,stack:ex.stack});
|
|
411
434
|
if(node.action=="realtime") {
|
|
412
435
|
node.error(node.action+" error: "+ex.message);
|
|
413
436
|
} else {
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|