Time based - sliding window - simple stats

Not tonight, I have to be up early in the morning. In the meantime wikipedia may be helpful, Low-pass filter - Wikipedia, if you can try and get the gist of what it is talking about without getting bogged down in the maths. The function above approximates to a Resistor Capacitor filter as shown in the diagrams there.

1 Like

Just for completeness, here is a subflow which input data and outputs:

{"count":3,
"min":0,
"max":9,
"sum":9,
"median":0,
"avg_qty":3,
"avg_time":4.5}

it use a sliding windows.
You can adjust the time threshold in the subflow node properties.

[{"id":"364e384bacbf752f","type":"subflow","name":"timeWeightedAverage","info":"","category":"","in":[{"x":120,"y":280,"wires":[{"id":"440e4364ed92206b"}]}],"out":[{"x":760,"y":280,"wires":[{"id":"64c35f5cfe2c4613","port":0}]}],"env":[{"name":"threshold_s","type":"num","value":"60","ui":{"icon":"font-awesome/fa-clock-o","type":"input","opts":{"types":["num"]}}}],"meta":{"author":"aikitori@github.com","license":"MIT"},"color":"#3FADB5","icon":"font-awesome/fa-clock-o"},{"id":"440e4364ed92206b","type":"function","z":"364e384bacbf752f","name":"store","func":"var sliding_windows_size = 10\n\n\n// Limit array just by size\nfunction limit_array_size (arr,size) {\n        if (arr.length > size) {\n            arr.shift()\n        }\n        return arr\n}\n\n// Limit array by age\nfunction removeOlderThan(timestamps, data, threshold) {\n    const currentTime = Date.now();\n    for (let i = timestamps.length - 1; i >= 0; i--) {\n        if (currentTime - timestamps[i] > threshold) {\n            timestamps.splice(i, 1);\n            data.splice(i, 1);\n        }\n    }\n}\n\nvar data = []\nvar timestamps = []\n\ndata = context.get('data') || []\ntimestamps = context.get('timestamps') || []\n\nif (msg.topic == 'dump') {    \n    msg.payload = {}\n    msg.payload[\"data\"] = data\n    msg.payload[\"timestamps\"]= timestamps\n\n    context.set('data', [])\n    context.set('timestamps', [])\n\n} else {\n    let payload = msg.payload\n    let now = Date.now()\n    data.push(payload)\n    timestamps.push(now)\n\n    const threshold_s = env.get(\"threshold_s\");\n    const threshold_ms = threshold_s * 1000\n\n    removeOlderThan(timestamps, data, threshold_ms);\n\n    context.set('data',data)\n    context.set('timestamps',timestamps)\n\n    msg.payload = {}\n    msg.payload[\"data\"] = data\n    msg.payload[\"timestamps\"] = timestamps\n\n}\nreturn msg;\n\n","outputs":1,"timeout":0,"noerr":0,"initialize":"","finalize":"","libs":[],"x":310,"y":280,"wires":[["64c35f5cfe2c4613"]]},{"id":"64c35f5cfe2c4613","type":"function","z":"364e384bacbf752f","name":"calculate stuff","func":"function timeWeightedAverage(data, timestamps) {\n    if (data.length !== timestamps.length) {\n        node.warn(\"array lenght is not equal\");\n    }\n    \n    let total_weighted_sum = 0.0;\n    let total_duration = 0.0;\n    let total_area = 0.0\n    \n    for (let i = 1; i < data.length; i++) {\n        let d_t = timestamps[i] - timestamps[i - 1];\n        let v_1 = data[i -1]\n        let v_2 = data[i]\n        let area = ((v_1+v_2))/2 * d_t\n        total_area += area\n        const duration = timestamps[i] - timestamps[i - 1];\n        total_duration += duration;\n    }\n    \n    const time_weighted_avg = total_area / total_duration;\n    return time_weighted_avg;\n}\n\n\nfunction findMedian(arr) {\n    arr.sort((a, b) => a - b);\n    const middleIndex = Math.floor(arr.length / 2);\n\n    if (arr.length % 2 === 0) {\n        return (arr[middleIndex - 1] + arr[middleIndex]) / 2;\n    } else {\n        return arr[middleIndex];\n    }\n}\n\nlet new_payload = {}\nconst data = msg.payload.data\nconst timestamps = msg.payload.timestamps\n\n\n//https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Math/max\nconst min = Math.min(...data)\nconst max = Math.max(...data)\n\n// https://stackoverflow.com/a/10624256\nconst sum = data.reduce((a, b) => a + b, 0);\n\n\nconst avg_qty = (sum / data.length) || 0;\nlet avg_time = timeWeightedAverage(data, timestamps)\n\n\nnew_payload[\"count\"] = timestamps.length \nnew_payload[\"min\"] = min\nnew_payload[\"max\"] = max\nnew_payload[\"sum\"] = sum\nnew_payload[\"median\"] = findMedian(data)\nnew_payload[\"avg_qty\"] = avg_qty\nnew_payload[\"avg_time\"] = avg_time || avg_qty\n\n\nmsg.payload = new_payload\nreturn msg;","outputs":1,"timeout":0,"noerr":0,"initialize":"","finalize":"","libs":[],"x":540,"y":280,"wires":[[]]},{"id":"62c4c2b01cca3e59","type":"subflow:364e384bacbf752f","z":"8eabc3a82cb1c5d6","name":"","x":620,"y":680,"wires":[["21e13998d15101d8"]]},{"id":"064548962ef80966","type":"inject","z":"8eabc3a82cb1c5d6","name":"","props":[{"p":"payload"}],"repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"","payload":"$floor($random() * 10)\t\t","payloadType":"jsonata","x":390,"y":680,"wires":[["62c4c2b01cca3e59"]]},{"id":"21e13998d15101d8","type":"debug","z":"8eabc3a82cb1c5d6","name":"debug","active":true,"tosidebar":true,"console":false,"tostatus":true,"complete":"payload","targetType":"msg","statusVal":"payload","statusType":"auto","x":810,"y":680,"wires":[]}]

@Colin the RC filter looks also interesting, thank you for the hint!

@kitori - I forgot to come back and say thank you to you and everyone who helped me! Thank you!

This topic was automatically closed 60 days after the last reply. New replies are no longer allowed.