NEWS
Rollierender Durchschnittswert 4 Wochen
-
Hallo zusammen,
ich finde für folgendes Problem nicht die Lösung:
Ich habe ein Diagramm (chart-Node) mit den Werten der letzten 4 Wochen. Nun möchte ich von den dargestellten Werten den Durchschnittswert haben. Sprich, der aktuelle Durchschnittswert soll immer nur aus den Werten der letzten 4 Wochen bestehen bzw. gebildet werden. Ältere Werte sollen rausaltern bzw. nicht mehr berücksichtigt werden.
Die Werte kommen übrigens unregelmäßig, z.B. kann es pro Tag mal 3 oder auch 20 Werte geben.
Mir ist schon klar, dass ich auf die Werte der vergangenen 4 Wochen, die mir das Diagramm momentan anzeigt, für die Durchschnittswertermittlung nicht mehr zugreifen kann. Aber für die Zukunft würde ich gern immer den Durchschnittswert der aktuell angezeigten Werte haben (quasi ein rollierender Durchschnittswert).
Ich habe zwar folgenden Nodes gefunden, die in Frage kommen könnten, weiß aber nicht, wie ich die konfigurieren soll, um das zuvor Beschriebene zu erreichen:
- aggregator node
- node-red-contrib-moving-average
Kann mir jemand weiterhelfen?
Danke und Gruß
Merleg -
@merleg sagte in Rollierender Durchschnittswert 4 Wochen:
Na wenn sie unregelmässig kommen, würde ich halt eine Kombi machen. Mit der aggregator Node könntest Du beispielsweise Tagesdurchschnitte ermitteln (egal wieviele Werte kommen) und diese Tagesdurchschnitte in die moving-average node mit 28 Tagen ausgeben lassen. Der moving average übernimmt das Rollieren, die aggregator node ermittelt Tagesdruchschnitte, so dass Du in den 4 wochen immer die gleiche Anzahl an Werten hast.
Du musst Dir nur überlegen, wie Du ggf. einen Neustart überstehen willst. Also würde ich ggf. die Tageswerte noch in einem Array abspeichern - um die moving average node ggf. wieder initialisieren zu können. Im Prinzip ist die moving average Node nicht mehr erforderlich, wenn Du die Tageswerte in einem 28er Array abspeicherst.
-
Wie gesagt die rolling average node brauchst Du nicht. Über den Flowkontext kannst Du das Array mit den 28 Tageswerten abspeichern.
Entweder Du speicherst das in einem Datenpunkt oder falls nicht im iobroker - ggf. im Filekontext. Die Aggregator-Node konfigurierst Du, dass die den Tagesdurchschnitt liefert.
-
@mickym
Danke für die schnelle Antwort! Die Idee ist gut. Ein 28 Tages-Array mit jeweils dem Tagesdurchschnitt ist die Lösung. Ich würde das in einem Function Node mit Javascript versuchen. Und wegen dem Neustart das Array immer im Context abspeichern.Deinen Flow-Vorschlag (danke dafür) muss ich erst noch verstehen, wie er arbeitet. (Bisher habe ich in solchen Fällen immer mit Function Nodes und Javascript gearbeitet.) Der Aggregator-Node ist klar (Tagesdurchschnitt). Der liefert einmal am Tag den Durchschnittswert als msg.payload.
Aber was passiert dann bei den Rules im Change-Node? Die Rules werden ja von oben nach unten abgearbeitet. Und das verstehe ich irgendwie nicht. Außerdem zeigt er in einer Expression einen Fehler "Unknown operator: #":
-
@merleg Wenn Du lieber Code schreiben willst, dann bleibt das Dir überlassen. Ich meide es zu programmieren.
Bei mir kommt der Fehler nicht. Dann hast Du halt was geändert.Auch wenn ich das erneut importiere habe ich keinerlei Fehler:
Vielleicht hast Du irgendwas verändert?
Die payload muss natürlich eine Zahl sein. Der von Dir markierte JSONATA Code lautet:
$append(payload,days)#$i[$i<28]
Falls Du Dich davon verabscheiden willst, Javascript Code zu schreiben und diese geniale JSONATA Bibliothek zu verwenden , dann helfe ich gerne. Ich vermeide die Nutzung von function Nodes, solange nicht unbedingt erforderlich. Es gibt noch einige Fälle, wo man function Nodes braucht - aber ansonsten ist es einfach nur umständlich bzw. widerspricht der Idee von NodeRed der grafischen Programmierung. Die Regeln der Change Nodes kannst Du ggf. in einzelne Change NOdes aufteilen und mit debug Nodes überprüfen.
Innerhalb von function NOdes mit node.warn(...) zu debuggen ist dagegen nur grausam.
Bei einem Flow sollte man möglichst gleich sehen, was er tut ohne sich erst mal durch Code zu kämpfen.
Du kannst schließlich ganze Flows auch gänzlich als Javascript Code in function Nodes schreiben. Das habe ich aber schon oft thematisiert.
Hier zum Beispiel : https://forum.iobroker.net/post/777723
Anhand der function Node siehst Du nichts, was passiert.
-
Wenn Du JSONATA lernen willst, hier kannst Du den Code und die Auswirkungen direkt ausprobieren. Hier mal mit einem Array mit 5 Elementen, in dem Du die payload hinzufügst:
-
Aber natürlich kannst Du das auch mit einer function Node machen - dann kannst auch den Node Kontext nehmen.
var arr = context.get("days") || [] arr.unshift(msg.payload); if (arr.length > 28) arr.pop(); context.set("days",arr); msg.payload = arr.reduce((/** @type {any} */ a, /** @type {any} */ b) => a + b) / arr.length; return msg;
-
Es gibt noch einige Fälle, wo man function Nodes braucht - aber ansonsten ist es einfach nur umständlich bzw. widerspricht der Idee von NodeRed der grafischen Programmierung.
Da gebe ich Dir vollkommen recht!
Bei einem Flow sollte man möglichst gleich sehen, was er tut ohne sich erst mal durch Code zu kämpfen.
Aber auch bei Deinem change-Node habe ich Schwierigkeiten, den Ablauf auf Anhieb zu verstehen. Im Gegensatz zu Deiner Javascript-Lösung, die ist toll. Und dank Google (ich kannte die speziellen Array-Möglichkeiten noch nicht) auch leicht zu verstehen.
Ich habe den Grund gefunden, warum bei mir ein Fehler im JSONATA-Code angezeigt wird: Es liegt an meiner Node-Red Version 0.19.5, die seit 2018 stabil und ohne für mich erkennbarer Fehler auf einem Raspi 3 B+ läuft. Wenn Du im JSONata Exerciser die Version auf 1.6.5 oder kleiner stellst, bekommst Du genau den Fehler, der auch bei mir angezeigt wird. (Unknown operator: "#").
Aber natürlich kannst Du das auch mit einer function Node machen - dann kannst auch den Node Kontext nehmen.
Dein Code gefällt mir sehr gut und ist genau das, was ich gesucht habe. Herzlichen Dank dafür! Jeden neuen Wert fügst Du an 1. Stelle im Array hinzu und wenn es mehr als 28 Elemente werden, löscht Du jeweils das Letzte wieder weg. Somit hat man immer die letzten 28 Werte. Dann addierst Du alle Elemente und teilst sie durch die Anzahl der Elemente. Der Durchschnitt wird dann als msg.payload ausgegeben. Und das Ganze im Kontext gespeichert überlebt dann auch den Reboot. Perfekt!
-
@merleg sagte in Rollierender Durchschnittswert 4 Wochen:
Und das Ganze im Kontext gespeichert überlebt dann auch den Reboot. Perfekt!
Na nur wenn der Kontext im Filekontext gespeichert wird. Im Memory Kontext gehen die Daten verloren. Ansonsten hoffe ich, behilflich gewesen zu sein. Aber Du kannst es ggf. abspeichern und das Array wieder neu initialisieren.