node-red-contrib-prib-functions
Version:
Node-RED added node functions.
285 lines (271 loc) • 13.8 kB
HTML
<script type="text/javascript">
/*globals RED */
RED.nodes.registerType('Data Analysis', {
category: 'function',
color: '#fdeea2',
defaults: {
name: {value:"",required:false},
action: {value:"avg",required:true},
columns:{value:"",required:false},
lag: {value:1,required:true},
outputs: {value:(["realtime","realtimePredict"].includes(this.action)?3:2),required:true},
outliersBase: {value:"avg",required:false},
outliersStdDevs: {value:"3",required:false},
term: {value:10,required:false},
keyProperty: {value:"msg.topic",required:false},
dataProperty: {value:"msg.payload",required:false},
dataProperties: {value:["msg.payload[0]","msg.payload[1]"],required:false}
},
inputs:1,
inputLabels: "in",
outputLabels: ["Results","Details","Outliers"],
icon: "graphmagnifier.png",
label: function() {
return this.name||(this.action+" " +this.dataProperty)||this._("Data Analysis");
},
labelStyle: function() {
return "node_label_italic";
},
oneditprepare: function() {
const node=this;
node.lag??=1
$("#node-input-keyProperty").change(function() {
if( [null,""].includes(node.keyProperty) ) {
$(this).val("msg.topic");
}
});
$("#node-input-outliersStdDevs").change(function() {
if( [null,""].includes($(this).val()) || $(this).val()>3 ){
$(this).val(3);
} else if($(this).val()<0){
$(this).val(1);
}
});
$("#node-input-dataProperty").change(function() {
if( [null,""].includes(node.dataProperty) ) {
$(this).val("msg.payload");
}
});
$("#node-input-action").change(function() {
if(["autocovariance","autocorrelation","differenceSeasonal","differenceSeasonalSecondOrder","realtime","realtimePredict"].includes( $(this).val() )) {
$(".form-row-http-in-lag").show();
} else {
$(".form-row-http-in-lag").hide();
}
if(["distances","distancesMax","distancesMin"].includes( $(this).val() )) {
$(".form-row-http-in-columns").show();
} else {
$(".form-row-http-in-columns").hide();
}
if(["movingAvgSimple","movingAvgWeighted","movingAvgExponential","realtime","realtimePredict"].includes( $(this).val() )) {
$(".form-row-http-in-term").show();
} else {
$(".form-row-http-in-term").hide();
}
if(["movingAvgExponential"].includes( $(this).val() )) {
$("#node-input-term").attr({min:0,max:1,step:0.1});
if(node.term<0 || node.term>1) $("#node-input-term").val(0.5);
$("#node-input-term").change(function() {
if(node.term<0) $(this).val(0);
if(node.term>1) $(this).val(1);
});
} else {
$("#node-input-lag").attr({min:1,max:1000,step:1});
$("#node-input-lag").change(function() {
const v=$(this).val();
if(v<1) $(this).val(1);
if(v>1000) $(this).val(1000);
});
$("#node-input-term").attr({min:1,max:100,step:1});
$("#node-input-term").change(function() {
const v=$(this).val()
if(v<1) $(this).val(1);
if(v>100) $(this).val(100);
});
}
if(["realtime","realtimePredict"].includes( $(this).val() )) {
node.outputs=3;
$(".form-row-http-in-keyProperty").show();
$(".form-row-http-in-outliersStdDevs").show();
$(".form-row-http-in-outliersBase").show();
$("#node-input-outliersStdDevs").change();
} else {
node.outputs=2;
$(".form-row-http-in-keyProperty").hide();
$(".form-row-http-in-outliersStdDevs").hide();
$(".form-row-http-in-outliersBase").hide();
}
if(["pearsonR","covariance","corelationship"].includes( $(this).val() )) {
$(".node-input-dataProperties-container-row").show();
$(".form-row-http-in-dataProperty").hide();
} else {
$(".node-input-dataProperties-container-row").hide();
$(".form-row-http-in-dataProperty").show();
}
$("#node-input-lag").change();
$("#node-input-term").change();
});
node.dataProperties.forEach((r)=>addDataProperty(r));
$("#node-input-add-dataProperties").click(function() {
addDataProperty("msg.payload["+($("#node-input-dataProperties-tbody").children().length-1)+"]");
});
function addDataProperty(dataProperty) {
let row=$('<tr/>').appendTo($("#node-input-dataProperties-tbody"));
if($("#node-input-dataProperties-tbody").children().length>3){
let deleteButton = $('<a/>',{href:"#",class:"editor-button editor-button-medium"}).appendTo(row);
$('<i/>',{class:"fa fa-remove"}).appendTo(deleteButton);
deleteButton.click(function() {
$(this).parent().remove();
});
} else {
$('<a/>').appendTo(row);
}
$('<td/>').append($('<input type="text" size="30" style="width:100%; border:0;" />').attr('value', dataProperty)).appendTo(row); // Topic
}
$("#node-input-action").change();
},
oneditsave: function() {
let inputs,node=this;
node.dataProperties=[];
$('#node-input-dataProperties-tbody tr:gt(0)').each(function () {
inputs=$(this).find("input");
node.dataProperties.push(inputs[0].value);
});
},
oneditresize: function() {
},
});
</script>
<script type="text/x-red" data-template-name="Data Analysis">
<div class="form-row">
<label for="node-input-name"><i class="fa fa-tag"></i> Name</label>
<input type="text" id="node-input-name" placeholder="Name">
</div>
<div class="form-row form-row-http-in-keyProperty hide">
<label for="node-input-dataProperty" style="white-space: nowrap"><i class="icon-bookmark"></i> Key Property </label>
<input type="text" id="node-input-keyProperty" placeholder="key">
</div>
<div class="form-row form-row-http-in-dataProperty show">
<label for="node-input-dataProperty" style="white-space: nowrap"><i class="icon-bookmark"></i> Data Property </label>
<input type="text" id="node-input-dataProperty" placeholder="msg.payload">
</div>
<div class="form-row">
<label for="node-input-action"><i class="fa fa-list-ul"></i> Method</label>
<select id="node-input-action" placeholder="action">
<option value="autocovariance">Autocovariance</option>
<option value="autocorrelation">Autocorrelation</option>
<option value="avg">Average</option>
<option value="covariance">Covariance</option>
<option value="corelationship">Corelationship</option>
<option value="deltas">Deltas</option>
<option value="deltaNormalised">Deltas Normalised</option>
<option value="difference">Difference</option>
<option value="differenceSeasonal">Difference Seasonal</option>
<option value="differenceSeasonalSecondOrder">Difference Seasonal Second Order</option>
<option value="differenceSecondOrder">Difference Second Order</option>
<option value="distances">Euclidean Distances</option>
<option value="distancesMax">Euclidean Distances Max</option>
<option value="distancesMin">Euclidean Distances Min</option>
<option value="pearsonR">Linear Correlation Coefficient</option>
<option value="max">Maximum</option>
<option value="avg">Mean</option>
<option value="median">Median</option>
<option value="min">Minimun</option>
<option value="movingAvgSimple">Moving Average Simple (SMA)</option>
<option value="movingAvgCumulative">Moving Average Cumulative (CMA)</option>
<option value="movingAvgWeighted">Moving Average Weighted (WMA)</option>
<option value="movingAvgExponential">Moving Average Exponential (EMA/EWMA)</option>
<option value="normalize">Normalise</option>
<option value="pearsonR">Pearson Product Moment Correlation (PPMC)</option>
<option value="randomise">Randomise</option>
<option value="range">Range</option>
<option value="realtime">RealTime Metrics</option>
<option value="realtimePredict">RealTime Metrics + predicts</option>
<option value="standardize">Standardization (Z-score Normalization)</option>
<option value="stdDev">Standard Deviation</option>
<option value="sampleStdDev">Standard Deviation (Sample)</option>
<option value="skew">Skewness</option>
<option value="sum">Sum</option>
<option value="variance">Variance</option>
<option value="sampleVariance">Variance (Sample)</option>
</select>
</div>
<div class="form-row form-row-http-in-columns hide">
<label for="node-input-columns"><i class="icon-bookmark"></i> Columns</label>
<input type="text" id="node-input-columns" placeholder="columns">
</div>
<div class="form-row form-row-http-in-lag hide">
<label for="node-input-lag"><i class="icon-bookmark"></i> Lag</label>
<input type="number" id="node-input-lag" placeholder="lag" min="1" max="10000" step="1">
</div>
<div class="form-row form-row-http-in-term hide">
<label for="node-input-term"><i class="icon-bookmark"></i> Term</label>
<input type="number" id="node-input-term" placeholder="term" min="2" max="100" step="1">
</div>
<div class="form-row form-row-http-in-outliersBase hide">
<label for="node-input-outliersBase"><i class="fa fa-list-ul"></i> Outlier base</label>
<select id="node-input-outliersBase" placeholder="outliersBase">
<option value="avg">Average</option>
<option value="median">Median</option>
</select>
</div>
<div class="form-row form-row-http-in-outliersStdDevs hide">
<label for="node-input-outliersStdDevs"><i class="icon-bookmark"></i> Outlier, std devs.</label>
<input type="number" id="node-input-outliersStdDevs" placeholder="outliersStdDevs" min="1" max="3" step="1">
</div>
<div class="form-row node-input-dataProperties-container-row hide" style="margin-bottom:0px; width:100%; min-width:520px">
<label style="vertical-align:top;">
<i class="fa fa-list-alt"></i> Topic
<a href="#" class="editor-button editor-button-small" id="node-input-add-dataProperties" style="margin-top: 4px; margin-left: 103px;"><i class="fa fa-plus"></i> <span>Add</span></a>
</label>
<div style="width:100%; display: inline-block; background-color:#f3f3f3; border-top:0px solid; border-radius:0 0 0 0; border-bottom:1px solid #ccc;">
<table>
<tbody id="node-input-dataProperties-tbody" stype="display: block; overflow: auto; max-width:400px; max-height: 400px;">
<tr style="padding-left:4px; border-bottom: 1px solid black; background: lightblue; position: sticky; top: 0;">
<td style="min-width: 10px;">Delete</td>
<td style="min-width: 200px;">Property</td>
</tr>
</tbody>
</table>
</div>
</div>
</script>
<script type="text/x-red" data-help-name="Data Analysis">
<p>
Various functions on array of values expected in msg.payload or selected location. Output msg.result.
</p>
<p>
If realtime metrics it calculates various statistical metrics as it receives key and value in msg.payload property
for a single data point.
Must select key property and value property must be singular value along with term.
The number of keys and term will determine amount of memory consummed.
Data is not persisted so metrics start from zero sample set on node recycle.
</p>
<p>
If real-time stats then a message can send directive instruction in topic:
<dl>
<dt>"@predict <key>"</dt><dd></dd>Send send predictions to second port for selected key.
<dt>"@stats"</dt><dd></dd>send all stored metrics and retained datapoints to second port.
<dt>"@stats reset"</dt><dd>Reset all stats and "@stats reset <key>" will reset a particular data point.</dd>
<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>
</dl>
</p>
<dl>
<dt>Lag</dt><dd>If greater that 1 generates seasonal difference with degree lag</dd>
<dt>Term</dt><dd>The depth of moving Average</dd>
</dl>
<p>
</p>
<p>
Outliers are not within:
<ul>
<li>1 standard deviation of the mean (or median), around 68%
<li>2 standard deviation of the mean (or median), around 95%
<li>3 standard deviations of the mean (or median), around 99.7%
</ul>
</p>
<p>
Distance functions take in an array of points in an array and calculates the euclidean distance.
e.g. max input [ [1,1],[2,2],[3,3],[4,5]]), has output [{distance:5,points:[[1,1],[4,5]],index:[0,3]}]
</p>
</script>