Anyone any idea how to create a 7 day rolling average from an array of objects, to plot in a node-RED chart?
The data arrives as an array of objects, comprising of a value, together with a epoch timestamp.
There is just one object per day, and currently there is about 120 objects (4 months worth of data).
If you want just latest 7 days and the data is already sorted, you could (in a function node) use the array slice function followed by array reduce function.
No, I want to plot the rolling 7 days average, starting from the earliest datapoint to the latest datapoint, so the chart data will absorb the anomalies.
Example; the daily UK Coronavirus briefing, where accurate data is not received over a weekend, but the missing data is then added to Monday's data, resulting in a false representation. To avoid this, they plot the chart with a 7 day rolling average, which smoothes the chart.
PS - It's not for plotting Coronavirus deaths!
Yes, I've looked at that, but I haven't managed to handle an array of timestamped data using the node.
If I strip out the data from the timestamp, and feed it as consecutive data points, I'm sure it would work, but then I've lost the timestamps, and therefore difficult to chart.
I’m not sure if I understand your needs correctly, but have you thought about supplying your data points to a function node with a reducer in it to calculate the rolling average?
The reduce function gives access to current value, index in the array, as well as the full input array. Specifically the index and full input array sounds useful to me. That way you could build your output object but keep the timestamp (current object), while appending the calculated result to the reducer array (initial value/accumulator).
I have assumed that for the first 6 samples what is wanted is the average of the samples so far.
There are probably more compact ways of doing it but I imagine this is one of the most efficient techniques as keeps a rolling total rather than adding up seven values each time.
// Expects payload to be an array of objects {x: xvalue, y: yvalue}
// Performs a rolling average of y values over n samples and returns the result
// in payload.
const n = 7 // no of samples to average over
let total = 0
let count = 0 // number of samples accumulated
let answer = []
for (i = 0; i < msg.payload.length; i++) {
total += msg.payload[i].y
if (i >= n) {
total -= msg.payload[i-n].y
count = n
} else {
count = i+1
}
answer[i] = {x: msg.payload[i].x, y: total/count}
}
msg.payload = answer
return msg;
Very clever @Colin, it's working fine on the live data, and it's also easy to switch the chart between original data and the 7 day rolling average by passing in the value of n via msg.ra with the data feed.
const n = msg.ra || 7;
Thank you
Edit// @afelix, @dceejay I did try to use both suggestions, but found reduce() difficult to get right, and was just looking at the join node option when Colin's posted. - thanks both.