node-red-contrib-prib-functions 0.21.0 → 0.23.2
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 +91 -35
- 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 +3 -1
- package/dataAnalysis/dataAnalysis.js +3 -0
- package/documentation/matrix.jpg +0 -0
- package/lib/common.js +128 -0
- package/lib/objectExtensions.js +190 -80
- package/lib/typedInput.js +77 -0
- package/matrix/matrixNode.html +2 -1
- package/package.json +14 -11
- package/test/00-objectExtensions.js +192 -1
- package/test/data/.config.nodes.json +3 -3
- package/test/data/.config.nodes.json.backup +3 -3
- package/test/data/.config.users.json +3 -2
- package/test/data/.config.users.json.backup +3 -2
- package/test/data/.flow.json.backup +3306 -427
- package/test/data/flow.json +3292 -411
- package/test/data/package-lock.json +4 -4
- package/test/dataAnalysisExtensions.js +20 -0
- package/testing/test.html +4 -0
- package/testing/test.js +59 -26
- package/transform/transform.html +163 -19
- package/transform/transform.js +106 -55
- package/visual/shapes/base..js +1 -0
- package/visual/visual.js +0 -0
- package/visual/visualNode.js +45 -0
package/README.md
CHANGED
|
@@ -30,6 +30,8 @@ being outside 3 standard deviations from mean. This can be changed to median and
|
|
|
30
30
|
A set of data analysis functions that can be run over an array of data
|
|
31
31
|
|
|
32
32
|
Single value metrics:
|
|
33
|
+
* autocorrelation
|
|
34
|
+
* Autocovariance
|
|
33
35
|
* Average/Mean
|
|
34
36
|
* Maximum
|
|
35
37
|
* Median
|
|
@@ -109,53 +111,104 @@ Messages generates a message for each row or record.
|
|
|
109
111
|
|
|
110
112
|
Transformations:
|
|
111
113
|
|
|
112
|
-
* Array to
|
|
113
|
-
*
|
|
114
|
-
*
|
|
115
|
-
*
|
|
116
|
-
*
|
|
114
|
+
* Array to
|
|
115
|
+
* CSV
|
|
116
|
+
* HTML
|
|
117
|
+
* ISO8385
|
|
118
|
+
* Messages
|
|
119
|
+
* xlsx / xlsx object (excel uses [xlsx][7])
|
|
117
120
|
* AVRO to JSON (uses [avsc][6])
|
|
118
|
-
* Buffer to
|
|
121
|
+
* Buffer to compressed
|
|
119
122
|
* Confluence to JSON
|
|
120
|
-
* Compressed to
|
|
121
|
-
*
|
|
122
|
-
*
|
|
123
|
-
*
|
|
124
|
-
* CSV to
|
|
125
|
-
*
|
|
126
|
-
*
|
|
127
|
-
*
|
|
128
|
-
* CSVWithHeader to
|
|
123
|
+
* Compressed to
|
|
124
|
+
* Buffer
|
|
125
|
+
* String
|
|
126
|
+
* JSON
|
|
127
|
+
* CSV to
|
|
128
|
+
* Array
|
|
129
|
+
* HTML
|
|
130
|
+
* Messages
|
|
131
|
+
* CSVWithHeader to
|
|
132
|
+
* Array
|
|
133
|
+
* HTML
|
|
134
|
+
* JSON
|
|
135
|
+
* Date to
|
|
136
|
+
* is between
|
|
137
|
+
* ISO string
|
|
138
|
+
* locale string
|
|
139
|
+
* range limit
|
|
129
140
|
* ISO8385 to Array
|
|
130
141
|
* ISO8385 to JSON
|
|
131
|
-
* JSON to
|
|
132
|
-
*
|
|
133
|
-
*
|
|
134
|
-
*
|
|
135
|
-
*
|
|
136
|
-
*
|
|
137
|
-
*
|
|
138
|
-
*
|
|
139
|
-
*
|
|
140
|
-
*
|
|
141
|
-
*
|
|
142
|
-
*
|
|
143
|
-
*
|
|
144
|
-
* path
|
|
145
|
-
*
|
|
146
|
-
*
|
|
147
|
-
*
|
|
148
|
-
*
|
|
149
|
-
*
|
|
142
|
+
* JSON to
|
|
143
|
+
* Array
|
|
144
|
+
* Confluence
|
|
145
|
+
* CSV
|
|
146
|
+
* AVRO (uses [avsc][6])
|
|
147
|
+
* ISO8385
|
|
148
|
+
* Messages
|
|
149
|
+
* String
|
|
150
|
+
* xlsx / xlsx object (excel uses [xlsx][7])
|
|
151
|
+
* XML (uses [fast-xml-parser][4])
|
|
152
|
+
* Number
|
|
153
|
+
* is between
|
|
154
|
+
* range limit
|
|
155
|
+
* path
|
|
156
|
+
* Basename
|
|
157
|
+
* Dirname
|
|
158
|
+
* Extname
|
|
159
|
+
* Format
|
|
160
|
+
* Is Absolute
|
|
161
|
+
* Join
|
|
162
|
+
* Parse
|
|
163
|
+
* Normalize
|
|
164
|
+
* Resolve
|
|
150
165
|
* snappy compress (uses [snappy][5], must install separately)
|
|
151
166
|
* snappy uncompress (uses [snappy][5], must install separately)
|
|
167
|
+
* String to
|
|
168
|
+
* Append
|
|
169
|
+
* Array By Delimiter
|
|
170
|
+
* At
|
|
171
|
+
* Camelize
|
|
172
|
+
* Capitalize
|
|
173
|
+
* Compressed
|
|
174
|
+
* Char At
|
|
175
|
+
* Char Code At"
|
|
176
|
+
* Code Point At
|
|
177
|
+
* Concat
|
|
178
|
+
* Date
|
|
179
|
+
* Delimiter On Case
|
|
180
|
+
* _ to space
|
|
181
|
+
* _ to space Capitilize
|
|
182
|
+
* Drop square bracket prefix
|
|
183
|
+
* Ends With
|
|
184
|
+
* Float
|
|
185
|
+
* Get Word
|
|
186
|
+
* Integer
|
|
187
|
+
* is Between
|
|
188
|
+
* Lower Case
|
|
189
|
+
* Number
|
|
190
|
+
* Prepend
|
|
191
|
+
* JSON
|
|
192
|
+
* Range Limit
|
|
193
|
+
* Split
|
|
194
|
+
* Starts With
|
|
195
|
+
* Timestamp
|
|
196
|
+
* Title
|
|
197
|
+
* Title Grammatical
|
|
198
|
+
* Trim
|
|
199
|
+
* Trim End
|
|
200
|
+
* Trim Start
|
|
201
|
+
* Upper Case
|
|
202
|
+
* Xml String Encode
|
|
203
|
+
|
|
204
|
+
|
|
152
205
|
* xlsx / xlsx object to array/JSON (excel uses [xlsx][7])
|
|
153
206
|
* XML to JSON (uses [fast-xml-parser][4])
|
|
154
207
|
|
|
155
208
|
Note, snappy needs to be installed separately as can have issues with auto install as build binaries.
|
|
156
209
|
|
|
157
210
|
With xlsx object one can use the function in [xlsx][7] against the object in functions node.
|
|
158
|
-
|
|
211
|
+
"
|
|
159
212
|
Example AVRO with schema
|
|
160
213
|
|
|
161
214
|

|
|
@@ -300,6 +353,9 @@ Test/example flow in test/generalTest.json
|
|
|
300
353
|
------------------------------------------------------------
|
|
301
354
|
|
|
302
355
|
# Version
|
|
356
|
+
0.23.0 Removes bug in test, more translation
|
|
357
|
+
|
|
358
|
+
0.22.0 Add autocovariance + autocorealationship to real time data analystics, improves test
|
|
303
359
|
|
|
304
360
|
0.21.0 Add lag/seasonal to real time data analystics
|
|
305
361
|
|
|
@@ -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
|
+
})
|
|
@@ -47,7 +47,7 @@
|
|
|
47
47
|
}
|
|
48
48
|
});
|
|
49
49
|
$("#node-input-action").change(function() {
|
|
50
|
-
if(["differenceSeasonal","differenceSeasonalSecondOrder","realtime","realtimePredict"].includes( $(this).val() )) {
|
|
50
|
+
if(["autocovariance","autocorrelation","differenceSeasonal","differenceSeasonalSecondOrder","realtime","realtimePredict"].includes( $(this).val() )) {
|
|
51
51
|
$(".form-row-http-in-lag").show();
|
|
52
52
|
} else {
|
|
53
53
|
$(".form-row-http-in-lag").hide();
|
|
@@ -158,6 +158,8 @@
|
|
|
158
158
|
<div class="form-row">
|
|
159
159
|
<label for="node-input-action"><i class="fa fa-list-ul"></i> Method</label>
|
|
160
160
|
<select id="node-input-action" placeholder="action">
|
|
161
|
+
<option value="autocovariance">Autocovariance</option>
|
|
162
|
+
<option value="autocorrelation">Autocorrelation</option>
|
|
161
163
|
<option value="avg">Average</option>
|
|
162
164
|
<option value="covariance">Covariance</option>
|
|
163
165
|
<option value="corelationship">Corelationship</option>
|
|
@@ -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")
|
|
@@ -129,6 +130,8 @@ function getColumns(node) {
|
|
|
129
130
|
}
|
|
130
131
|
}
|
|
131
132
|
functions={
|
|
133
|
+
autocovariance:(d,term,node)=>d.autocovariance(node.lag),
|
|
134
|
+
autocorrelation:(d,term,node)=>d.autocorrelation(node.lag),
|
|
132
135
|
avg:(d)=>functions.sum(d)/d.length,
|
|
133
136
|
arrayMax:(d)=>{ // not tested
|
|
134
137
|
let max=[],indices
|
|
Binary file
|
package/lib/common.js
ADDED
|
@@ -0,0 +1,128 @@
|
|
|
1
|
+
const error=(node,message,shortMessage,logger)=>{
|
|
2
|
+
if(logger && logger.active) logger.send({label:"error",node:node.id,error:error,shortMessage});
|
|
3
|
+
node.error(message);
|
|
4
|
+
node.status({fill:"red",shape:"ring",text:shortMessage});
|
|
5
|
+
}
|
|
6
|
+
const evalFunction(id,mapping,logger)=>{
|
|
7
|
+
logger&&logger.sendInfo({label:"evalFunction",id:id,mapping:mapping})
|
|
8
|
+
try{
|
|
9
|
+
return eval(mapping);
|
|
10
|
+
} catch(ex) {
|
|
11
|
+
logger.sendError({label:"evalFunction error",id:id,mapping:mapping,error:ex.message})
|
|
12
|
+
throw Error(id+" "+ex.message);
|
|
13
|
+
}
|
|
14
|
+
}
|
|
15
|
+
const evalInFunction=(node,propertyName)=>{
|
|
16
|
+
try{
|
|
17
|
+
const nodeContext = node.context();
|
|
18
|
+
const flow = nodeContext.flow;
|
|
19
|
+
const global = nodeContext.global;
|
|
20
|
+
const property=node[propertyName];
|
|
21
|
+
if(property==null) throw Error("no value for "+propertyName);
|
|
22
|
+
const propertyType=propertyName+"-type";
|
|
23
|
+
if(! (propertyType in node)) return evalFunction(propertyName,"()=>node."+property)
|
|
24
|
+
switch (node[propertyType]){
|
|
25
|
+
case "num":
|
|
26
|
+
case "json":
|
|
27
|
+
return evalFunction(propertyName,"()=>"+property);
|
|
28
|
+
case "node":
|
|
29
|
+
return evalFunction(propertyName,"()=>nodeContext.get("+property+")");
|
|
30
|
+
case "flow":
|
|
31
|
+
if(flow) throw Error("context store may be memoryonly so flow doesn't work")
|
|
32
|
+
return evalFunction(propertyName,"()=>flow.get("+property+")");
|
|
33
|
+
case "global":
|
|
34
|
+
return evalFunction(propertyName,"()=>global.get("+property+")");
|
|
35
|
+
case "env":
|
|
36
|
+
return evalFunction(propertyName,"()=>env.get("+property+")");
|
|
37
|
+
case "msg":
|
|
38
|
+
return evalFunction(propertyName,"(msg)=>msg."+property);
|
|
39
|
+
default:
|
|
40
|
+
logger.sendInfo({label:"setData unknown type",action:node.action,propertyType:propertyType,type:node[propertyType]});
|
|
41
|
+
throw Error("unknown type "+node[propertyType])
|
|
42
|
+
}
|
|
43
|
+
} catch(ex) {
|
|
44
|
+
logger.sendError({label:"setup",error:ex.message,stack:ex.stack});
|
|
45
|
+
throw Error(property+" "+ex.message);
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
const argsArray=(node,msg)=>{
|
|
50
|
+
const args=[];
|
|
51
|
+
node.argFunction.forEach(callFunction=> {
|
|
52
|
+
const result=callFunction(msg);
|
|
53
|
+
args.push(result);
|
|
54
|
+
});
|
|
55
|
+
return args;
|
|
56
|
+
}
|
|
57
|
+
const setArgsFunction=(node)=>{
|
|
58
|
+
node.argFunction=[];
|
|
59
|
+
node.args.forEach(property=>{
|
|
60
|
+
try{
|
|
61
|
+
node.argFunction.push(evalInFunction(node,property).bind(this));
|
|
62
|
+
} catch(ex) {
|
|
63
|
+
throw Error("args "+property+" "+ex.message)
|
|
64
|
+
}
|
|
65
|
+
})
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
|
|
69
|
+
const setTargetFunction=(node)=>{
|
|
70
|
+
if(node.target) throw Error("target is null")
|
|
71
|
+
if(node.hasOwnProperty("target-type")) {
|
|
72
|
+
node.setData=evalFunction("target","data=>data)")
|
|
73
|
+
return
|
|
74
|
+
}
|
|
75
|
+
const nodeContext = node.context()
|
|
76
|
+
const type=node["target-type"]
|
|
77
|
+
switch(type){
|
|
78
|
+
case "node":
|
|
79
|
+
node.setData=evalFunction("target","data=>nodeContext.set("+node.target+",data)")
|
|
80
|
+
break
|
|
81
|
+
case "flow":
|
|
82
|
+
if(nodeContext.flow) throw Error("context store may be memory only so flow doesn't work")
|
|
83
|
+
node.setData=evalFunction("target","data=>nodeContext.flow.set("+node.target+",data)")
|
|
84
|
+
break
|
|
85
|
+
case "global":
|
|
86
|
+
node.setData=evalFunction("target","data=>nodeContext.global.set("+node.target+",data)")
|
|
87
|
+
break
|
|
88
|
+
case "msg":
|
|
89
|
+
node.setData=evalFunction("target","(data,msg)=>{msg."+node.target+"=data;}")
|
|
90
|
+
break
|
|
91
|
+
default:
|
|
92
|
+
throw Error("setData unknown type "+type)
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
const baseProcess=(msg,call=sourceMatrix[node.action])=>{
|
|
97
|
+
const value=node.getSource(msg);
|
|
98
|
+
if(value==null) throw Error("source data not found");
|
|
99
|
+
const valueObject=(value instanceof Matrix?value:new Matrix(value));
|
|
100
|
+
return call.apply(valueObject,argsArray(node,msg));
|
|
101
|
+
}
|
|
102
|
+
const baseProcessAndSet=(msg)=>{
|
|
103
|
+
const result=baseProcess(msg);
|
|
104
|
+
node.setData.apply(node,[result,msg]);
|
|
105
|
+
}
|
|
106
|
+
function createProcess(msg){
|
|
107
|
+
const sourceMatrix=new Matrix({rowsMax:node.rows,columns:node.columns,dataType:node.dataType});
|
|
108
|
+
if(!(node.initialState in sourceMatrix)) throw Error("Invalid initial state "+node.initialState);
|
|
109
|
+
sourceMatrix[node.initialState]()
|
|
110
|
+
node.setData.apply(node,[sourceMatrix,msg]);
|
|
111
|
+
}
|
|
112
|
+
function defineProcess(msg){
|
|
113
|
+
const sourceMatrix=new Matrix({rows:node.rows,columns:node.columns,dataType:node.dataType});
|
|
114
|
+
node.setData.apply(node,[sourceMatrix,msg]);
|
|
115
|
+
}
|
|
116
|
+
function defineEmptyProcess(msg){
|
|
117
|
+
if(logger.active) logger.sendInfo({label:"define",arg:{rowsMax:node.row,columns:node.column}});
|
|
118
|
+
const sourceMatrix=new Matrix({rowsMax:node.rows,columns:node.columns});
|
|
119
|
+
node.setData.apply(node,[sourceMatrix,msg]);
|
|
120
|
+
}
|
|
121
|
+
function createSize(msg){
|
|
122
|
+
const sourceMatrix=new Matrix({rows:node.size,columns:node.size});
|
|
123
|
+
node.setData.apply(node,[sourceMatrix[node.action](),msg]);
|
|
124
|
+
}
|
|
125
|
+
function createDummy(msg){
|
|
126
|
+
const sourceMatrix=new Matrix(1,1);
|
|
127
|
+
node.setData.apply(node,[sourceMatrix[node.action].apply(sourceMatrix,argsArray(node,msg)),msg]);
|
|
128
|
+
}
|