node-red-contrib-prib-functions 0.23.2 → 0.26.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.
Files changed (58) hide show
  1. package/.github/copilot-instructions.md +36 -0
  2. package/README.md +153 -140
  3. package/columnar/columnar.html +258 -0
  4. package/columnar/columnar.js +1055 -0
  5. package/columnar/icons/columnar.svg +38 -0
  6. package/fileSystem/filesystem.html +299 -0
  7. package/fileSystem/filesystem.js +170 -0
  8. package/gitlab/gitlab.html +191 -0
  9. package/gitlab/gitlab.js +248 -0
  10. package/gitlab/icons/gitlab.svg +17 -0
  11. package/lib/AlphaBeta.js +32 -0
  12. package/lib/GraphDB.js +40 -9
  13. package/lib/MinMax.js +17 -0
  14. package/lib/Tree.js +64 -0
  15. package/lib/objectExtensions.js +28 -5
  16. package/lib/timeDimension.js +36 -0
  17. package/lib/typedInput.js +18 -2
  18. package/logisticRegression/icons/logisticregression.svg +22 -0
  19. package/logisticRegression/logisticRegression.html +136 -0
  20. package/logisticRegression/logisticRegression.js +83 -0
  21. package/package.json +21 -9
  22. package/test/02-graphdb.js +46 -0
  23. package/test/columnar.js +509 -0
  24. package/test/data/.config.nodes.json +114 -70
  25. package/test/data/.config.nodes.json.backup +104 -71
  26. package/test/data/.config.runtime.json +2 -1
  27. package/test/data/.config.runtime.json.backup +2 -1
  28. package/test/data/.config.users.json +3 -2
  29. package/test/data/.config.users.json.backup +3 -2
  30. package/test/data/.flow.json.backup +1545 -369
  31. package/test/data/flow.json +1457 -270
  32. package/test/data/package-lock.json +11 -11
  33. package/test/data/shares/.config.nodes.json +611 -0
  34. package/test/data/shares/.config.nodes.json.backup +589 -0
  35. package/test/data/shares/.config.runtime.json +5 -0
  36. package/test/data/shares/.config.runtime.json.backup +4 -0
  37. package/test/data/shares/.config.users.json +33 -0
  38. package/test/data/shares/.config.users.json.backup +33 -0
  39. package/test/data/shares/.flow.json.backup +230 -0
  40. package/test/data/shares/.flow_cred.json.backup +3 -0
  41. package/test/data/shares/flow.json +267 -0
  42. package/test/data/shares/flow_cred.json +3 -0
  43. package/test/data/shares/package.json +6 -0
  44. package/test/data/shares/settings.js +544 -0
  45. package/test/dataAnalysisExtensions.js +93 -93
  46. package/test/logisticRegression.js +379 -0
  47. package/test/transform.js +11 -11
  48. package/test/transformConfluence.js +4 -2
  49. package/test/transformNumPy.js +3 -1
  50. package/test/transformXLSX.js +4 -2
  51. package/test/transformXML.js +4 -2
  52. package/test-runner.js +400 -0
  53. package/test.parq +0 -0
  54. package/test_select.js +37 -0
  55. package/testing/test.js +8 -7
  56. package/transform/transform.html +23 -2
  57. package/transform/transform.js +239 -283
  58. package/transform/xlsx2.js +74 -0
@@ -0,0 +1,379 @@
1
+ const should = require('should');
2
+ const LogisticRegression = require('logisticegression');
3
+
4
+ describe('Logistic Regression', function() {
5
+
6
+ it('should create a LogisticRegression model instance', function() {
7
+ const model = new LogisticRegression({
8
+ learningRate: 0.1,
9
+ iterations: 100
10
+ });
11
+ (model instanceof LogisticRegression).should.be.true();
12
+ model.learningRate.should.equal(0.1);
13
+ model.iterations.should.equal(100);
14
+ });
15
+
16
+ it('should fit a model with training data', function() {
17
+ const model = new LogisticRegression({
18
+ learningRate: 0.1,
19
+ iterations: 100
20
+ });
21
+ const X = [[0, 0], [0, 1], [1, 0], [1, 1]];
22
+ const y = [0, 1, 1, 1];
23
+
24
+ const result = model.fit(X, y);
25
+ (result instanceof LogisticRegression).should.be.true();
26
+ (model.weights !== null).should.be.true();
27
+ model.weights.length.should.equal(3); // 2 features + 1 intercept
28
+ });
29
+
30
+ it('should predict class labels', function() {
31
+ const model = new LogisticRegression({
32
+ learningRate: 0.1,
33
+ iterations: 100
34
+ });
35
+ const X = [[0, 0], [0, 1], [1, 0], [1, 1]];
36
+ const y = [0, 1, 1, 1];
37
+
38
+ model.fit(X, y);
39
+ const predictions = model.predict([[0, 0], [1, 1], [0, 1]]);
40
+
41
+ Array.isArray(predictions).should.be.true();
42
+ predictions.length.should.equal(3);
43
+ predictions.forEach(pred => {
44
+ [0, 1].should.containEql(pred);
45
+ });
46
+ });
47
+
48
+ it('should predict probabilities', function() {
49
+ const model = new LogisticRegression({
50
+ learningRate: 0.1,
51
+ iterations: 100
52
+ });
53
+ const X = [[0, 0], [0, 1], [1, 0], [1, 1]];
54
+ const y = [0, 1, 1, 1];
55
+
56
+ model.fit(X, y);
57
+ const probs = model.predictProba([[0, 0], [1, 1], [0.5, 0.5]]);
58
+
59
+ Array.isArray(probs).should.be.true();
60
+ probs.length.should.equal(3);
61
+ probs.forEach(prob => {
62
+ (typeof prob === 'number').should.be.true();
63
+ prob.should.be.above(-0.01);
64
+ prob.should.be.below(1.01);
65
+ });
66
+ });
67
+
68
+ it('should compute decision function (logits)', function() {
69
+ const model = new LogisticRegression({
70
+ learningRate: 0.1,
71
+ iterations: 100
72
+ });
73
+ const X = [[0, 0], [0, 1], [1, 0], [1, 1]];
74
+ const y = [0, 1, 1, 1];
75
+
76
+ model.fit(X, y);
77
+ const decisions = model.decisionFunction([[0, 0], [1, 1]]);
78
+
79
+ Array.isArray(decisions).should.be.true();
80
+ decisions.length.should.equal(2);
81
+ decisions.forEach(d => {
82
+ (typeof d === 'number').should.be.true();
83
+ });
84
+ });
85
+
86
+ it('should throw error when fitting without data', function() {
87
+ const model = new LogisticRegression();
88
+ (() => {
89
+ model.fit([], []);
90
+ }).should.throw(/non-empty/);
91
+ });
92
+
93
+ it('should throw error when X and y have different lengths', function() {
94
+ const model = new LogisticRegression();
95
+ (() => {
96
+ model.fit([[0, 0], [1, 1]], [0, 1, 1]);
97
+ }).should.throw(/same number/);
98
+ });
99
+
100
+ it('should throw error when predicting before fitting', function() {
101
+ const model = new LogisticRegression();
102
+ (() => {
103
+ model.predict([[0, 0]]);
104
+ }).should.throw(/not fitted/);
105
+ });
106
+
107
+ it('should support L2 regularization', function() {
108
+ const model = new LogisticRegression({
109
+ learningRate: 0.1,
110
+ iterations: 100,
111
+ l2: 0.01
112
+ });
113
+ const X = [[0, 0], [0, 1], [1, 0], [1, 1]];
114
+ const y = [0, 1, 1, 1];
115
+
116
+ model.fit(X, y);
117
+ const predictions = model.predict([[0.5, 0.5]]);
118
+
119
+ Array.isArray(predictions).should.be.true();
120
+ predictions.length.should.equal(1);
121
+ });
122
+
123
+ it('should use custom threshold for prediction', function() {
124
+ const model = new LogisticRegression({
125
+ learningRate: 0.1,
126
+ iterations: 100
127
+ });
128
+ const X = [[0, 0], [0, 1], [1, 0], [1, 1]];
129
+ const y = [0, 1, 1, 1];
130
+
131
+ model.fit(X, y);
132
+ const predictions = model.predict([[0.5, 0.5]], 0.7);
133
+
134
+ Array.isArray(predictions).should.be.true();
135
+ [0, 1].should.containEql(predictions[0]);
136
+ });
137
+ });
138
+
139
+ describe('Logistic Regression Node', function() {
140
+ const helper = require("node-red-node-test-helper");
141
+ const logisticRegression = require("../logisticRegression/logisticRegression.js");
142
+ helper.init(require.resolve('node-red'));
143
+
144
+ const helperNodeOutput = {id: "helperNodeOutput", type: "helper"};
145
+
146
+ const base = {
147
+ id: "n1",
148
+ type: "logisticRegression",
149
+ modelName: "testModel",
150
+ action: "fit",
151
+ learningRate: 0.1,
152
+ iterations: 2000,
153
+ fitIntercept: true,
154
+ l2: 0.0,
155
+ tolerance: 1e-7,
156
+ verbose: false,
157
+ threshold: 0.5,
158
+ wires: [[helperNodeOutput.id]]
159
+ };
160
+
161
+ function getNode(node) {
162
+ const n = helper.getNode(node.id);
163
+ n.should.not.be.undefined();
164
+ return n;
165
+ }
166
+
167
+ afterEach(function(done) {
168
+ helper.unload().then(function() {
169
+ done();
170
+ }).catch(done);
171
+ });
172
+
173
+ it('should fit a model with training data', function(done) {
174
+ const newBase = Object.assign({}, base, {action: "fit"});
175
+ const flow = [helperNodeOutput, newBase];
176
+
177
+ helper.load(logisticRegression, flow, function() {
178
+ try {
179
+ const outputNode = getNode(helperNodeOutput);
180
+ const n1 = getNode(newBase);
181
+ n1.should.have.property("action", "fit");
182
+
183
+ outputNode.on("input", function(msg) {
184
+ try {
185
+ msg.should.have.property("result");
186
+ msg.result.should.equal("Model fitted and stored as: testModel");
187
+ msg.should.not.have.property("error");
188
+ } catch (ex) {
189
+ return done(ex);
190
+ }
191
+ done();
192
+ });
193
+
194
+ // Send training data
195
+ const trainingData = {
196
+ payload: {
197
+ X: [[0, 0], [0, 1], [1, 0], [1, 1]],
198
+ y: [0, 1, 1, 1]
199
+ }
200
+ };
201
+ n1.receive(trainingData);
202
+ } catch (ex) {
203
+ done(ex);
204
+ }
205
+ });
206
+ });
207
+
208
+ it('should predict class labels after fitting', function(done) {
209
+ const fitBase = Object.assign({}, base, {action: "fit"});
210
+ const predictBase = Object.assign({}, base, {id: "n2", action: "predict"});
211
+ const flow = [helperNodeOutput, fitBase, predictBase];
212
+
213
+ helper.load(logisticRegression, flow, function() {
214
+ try {
215
+ const outputNode = getNode(helperNodeOutput);
216
+ const fitNode = getNode(fitBase);
217
+ const predictNode = getNode(predictBase);
218
+
219
+ let fitDone = false;
220
+
221
+ outputNode.on("input", function(msg) {
222
+ try {
223
+ if (!fitDone) {
224
+ // First message is from fit
225
+ msg.result.should.equal("Model fitted and stored as: testModel");
226
+ fitDone = true;
227
+
228
+ // Send prediction data to same node
229
+ setTimeout(() => {
230
+ const predictionData = {
231
+ payload: [[0, 0], [1, 1], [0, 1]]
232
+ };
233
+ predictNode.receive(predictionData);
234
+ }, 100);
235
+ } else {
236
+ // Second message is prediction result
237
+ msg.should.have.property("result");
238
+ Array.isArray(msg.result).should.be.true();
239
+ msg.result.length.should.equal(3);
240
+ msg.result.forEach(pred => {
241
+ [0, 1].should.containEql(pred);
242
+ });
243
+ done();
244
+ }
245
+ } catch (ex) {
246
+ done(ex);
247
+ }
248
+ });
249
+
250
+ // First fit the model
251
+ const trainingData = {
252
+ payload: {
253
+ X: [[0, 0], [0, 1], [1, 0], [1, 1]],
254
+ y: [0, 1, 1, 1]
255
+ }
256
+ };
257
+ fitNode.receive(trainingData);
258
+ } catch (ex) {
259
+ done(ex);
260
+ }
261
+ });
262
+ });
263
+
264
+ it('should predict probabilities after fitting', function(done) {
265
+ const fitBase = Object.assign({}, base, {action: "fit"});
266
+ const probaBase = Object.assign({}, base, {id: "n3", action: "predictProba"});
267
+ const flow = [helperNodeOutput, fitBase, probaBase];
268
+
269
+ helper.load(logisticRegression, flow, function() {
270
+ try {
271
+ const outputNode = getNode(helperNodeOutput);
272
+ const fitNode = getNode(fitBase);
273
+ const probaNode = getNode(probaBase);
274
+
275
+ let fitDone = false;
276
+
277
+ outputNode.on("input", function(msg) {
278
+ try {
279
+ if (!fitDone) {
280
+ // First message is from fit
281
+ msg.result.should.equal("Model fitted and stored as: testModel");
282
+ fitDone = true;
283
+
284
+ // Send prediction data
285
+ setTimeout(() => {
286
+ const predictionData = {
287
+ payload: [[0, 0], [1, 1], [0.5, 0.5]]
288
+ };
289
+ probaNode.receive(predictionData);
290
+ }, 100);
291
+ } else {
292
+ // Second message is probability result
293
+ msg.should.have.property("result");
294
+ Array.isArray(msg.result).should.be.true();
295
+ msg.result.length.should.equal(3);
296
+ msg.result.forEach(prob => {
297
+ (typeof prob === 'number').should.be.true();
298
+ prob.should.be.above(-0.01);
299
+ prob.should.be.below(1.01);
300
+ });
301
+ done();
302
+ }
303
+ } catch (ex) {
304
+ done(ex);
305
+ }
306
+ });
307
+
308
+ // Fit the model
309
+ const trainingData = {
310
+ payload: {
311
+ X: [[0, 0], [0, 1], [1, 0], [1, 1]],
312
+ y: [0, 1, 1, 1]
313
+ }
314
+ };
315
+ fitNode.receive(trainingData);
316
+ } catch (ex) {
317
+ done(ex);
318
+ }
319
+ });
320
+ });
321
+
322
+ it('should handle fit error when missing X and y', function(done) {
323
+ const newBase = Object.assign({}, base, {action: "fit"});
324
+ const flow = [helperNodeOutput, newBase];
325
+
326
+ helper.load(logisticRegression, flow, function() {
327
+ try {
328
+ const outputNode = getNode(helperNodeOutput);
329
+ const n1 = getNode(newBase);
330
+
331
+ outputNode.on("input", function(msg) {
332
+ try {
333
+ msg.should.have.property("error");
334
+ done();
335
+ } catch (ex) {
336
+ done(ex);
337
+ }
338
+ });
339
+
340
+ // Send invalid data
341
+ const invalidData = {
342
+ payload: {}
343
+ };
344
+ n1.receive(invalidData);
345
+ } catch (ex) {
346
+ done(ex);
347
+ }
348
+ });
349
+ });
350
+
351
+ it('should handle predict error when model not fitted', function(done) {
352
+ const newBase = Object.assign({}, base, {action: "predict"});
353
+ const flow = [helperNodeOutput, newBase];
354
+
355
+ helper.load(logisticRegression, flow, function() {
356
+ try {
357
+ const outputNode = getNode(helperNodeOutput);
358
+ const n1 = getNode(newBase);
359
+
360
+ outputNode.on("input", function(msg) {
361
+ try {
362
+ msg.should.have.property("error");
363
+ done();
364
+ } catch (ex) {
365
+ done(ex);
366
+ }
367
+ });
368
+
369
+ // Try to predict without fitting
370
+ const predictionData = {
371
+ payload: [[0, 0]]
372
+ };
373
+ n1.receive(predictionData);
374
+ } catch (ex) {
375
+ done(ex);
376
+ }
377
+ });
378
+ });
379
+ });
package/test/transform.js CHANGED
@@ -1,4 +1,4 @@
1
- const should = require("should");
1
+ const assert = require("assert");
2
2
  const helper = require("node-red-node-test-helper");
3
3
  const transformNode = require("../transform/transform.js");
4
4
 
@@ -30,16 +30,16 @@ describe('transform', function() {
30
30
  } ];
31
31
  helper.load(transformNode, flow, function() {
32
32
  const n1 = helper.getNode("n1");
33
- n1.should.have.property('name', 'transform name');
34
- n1.should.have.property('actionSource', "Array");
35
- n1.should.have.property('actionTarget',"Messages");
36
- n1.should.have.property('sourceProperty',"msg.payload");
37
- n1.should.have.property('targetProperty',"msg.payload");
38
- n1.should.have.property('topicProperty',"'test topic'");
39
- n1.should.have.property('maxMessages', 1000);
40
- n1.should.have.property('skipLeading', 0);
41
- n1.should.have.property('skipTrailing', 0);
42
- n1.should.have.property('delimiter', ",");
33
+ assert.strictEqual(n1.name, 'transform name');
34
+ assert.strictEqual(n1.actionSource, "Array");
35
+ assert.strictEqual(n1.actionTarget, "Messages");
36
+ assert.strictEqual(n1.sourceProperty, "msg.payload");
37
+ assert.strictEqual(n1.targetProperty, "msg.payload");
38
+ assert.strictEqual(n1.topicProperty, "'test topic'");
39
+ assert.strictEqual(n1.maxMessages, 1000);
40
+ assert.strictEqual(n1.skipLeading, 0);
41
+ assert.strictEqual(n1.skipTrailing, 0);
42
+ assert.strictEqual(n1.delimiter, ",");
43
43
  done();
44
44
  });
45
45
  });
@@ -1,4 +1,4 @@
1
- //const should = require("should");
1
+ const assert = require("assert");
2
2
  const helper = require("node-red-node-test-helper");
3
3
  const transformNode = require("../transform/transform.js");
4
4
  const Buffer=require('buffer').Buffer;
@@ -7,7 +7,9 @@ helper.init(require.resolve('node-red'));
7
7
 
8
8
  function getAndTestNodeProperties(o) {
9
9
  const n = helper.getNode(o.id);
10
- for(let p in o) n.should.have.property(p, o[p]);
10
+ for (let p in o) {
11
+ assert.strictEqual(n[p], o[p], `property ${p} mismatch`);
12
+ }
11
13
  return n;
12
14
  }
13
15
 
@@ -46,7 +46,9 @@ const JSON2npyNode={
46
46
  };
47
47
  function getAndTestNodeProperties(o) {
48
48
  const n = helper.getNode(o.id);
49
- for(let p in o) n.should.have.property(p, o[p]);
49
+ for (let p in o) {
50
+ assert.strictEqual(n[p], o[p], `property ${p} mismatch`);
51
+ }
50
52
  return n;
51
53
  }
52
54
  function testFlow(done,node,data,result) {
@@ -1,4 +1,4 @@
1
- const should = require("should");
1
+ const assert = require("assert");
2
2
  const helper = require("node-red-node-test-helper");
3
3
  const transformNode = require("../transform/transform.js");
4
4
  const Buffer=require('buffer').Buffer;
@@ -7,7 +7,9 @@ helper.init(require.resolve('node-red'));
7
7
 
8
8
  function getAndTestNodeProperties(o) {
9
9
  const n = helper.getNode(o.id);
10
- for(let p in o) n.should.have.property(p, o[p]);
10
+ for (let p in o) {
11
+ assert.strictEqual(n[p], o[p], `property ${p} mismatch`);
12
+ }
11
13
  return n;
12
14
  }
13
15
 
@@ -1,4 +1,4 @@
1
- const should = require("should");
1
+ const assert = require("assert");
2
2
  const helper = require("node-red-node-test-helper");
3
3
  const transformNode = require("../transform/transform.js");
4
4
 
@@ -6,7 +6,9 @@ helper.init(require.resolve('node-red'));
6
6
 
7
7
  function getAndTestNodeProperties(o) {
8
8
  const n = helper.getNode(o.id);
9
- for(let p in o) n.should.have.property(p, o[p]);
9
+ for (let p in o) {
10
+ assert.strictEqual(n[p], o[p], `property ${p} mismatch`);
11
+ }
10
12
  return n;
11
13
  }
12
14