Navigation

    Logo
    • Register
    • Login
    • Search
    • Recent
    • Tags
    • Unread
    • Categories
    • Unreplied
    • Popular
    • GitHub
    • Docu
    • Hilfe
    1. Home
    2. Deutsch
    3. Skripten / Logik
    4. Node-Red
    5. Rollierender Durchschnittswert 4 Wochen

    NEWS

    • Neuer Blog: Fotos und Eindrücke aus Solingen

    • ioBroker@Smart Living Forum Solingen, 14.06. - Agenda added

    • ioBroker goes Matter ... Matter Adapter in Stable

    Rollierender Durchschnittswert 4 Wochen

    This topic has been deleted. Only users with topic management privileges can see it.
    • M
      Merleg last edited by

      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

      mickym 1 Reply Last reply Reply Quote 0
      • mickym
        mickym Most Active @Merleg last edited by mickym

        @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.

        mickym 1 Reply Last reply Reply Quote 0
        • mickym
          mickym Most Active @mickym last edited by

          Wie gesagt die rolling average node brauchst Du nicht. Über den Flowkontext kannst Du das Array mit den 28 Tageswerten abspeichern.

          84eea1f4-f7c3-4087-899d-cd3ab5e3f7fb-image.png

          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.

          [{"id":"facadffb2a1c9b98","type":"change","z":"289f539dcc33814e","name":"Rolling Average 28 ","rules":[{"t":"set","p":"days","pt":"msg","to":"days","tot":"flow"},{"t":"set","p":"days","pt":"msg","to":"$append(payload,days)#$i[$i<28]","tot":"jsonata"},{"t":"set","p":"days","pt":"flow","to":"days","tot":"msg"},{"t":"set","p":"payload","pt":"msg","to":"$average(days)","tot":"jsonata"}],"action":"","property":"","from":"","to":"","reg":false,"x":550,"y":5960,"wires":[["30d5140be5733937"]]},{"id":"5750c041a1659feb","type":"inject","z":"289f539dcc33814e","name":"","props":[{"p":"payload"}],"repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"","payload":"10","payloadType":"num","x":330,"y":6020,"wires":[["facadffb2a1c9b98"]]},{"id":"293c36f42771fe8c","type":"aggregator","z":"289f539dcc33814e","name":"","topic":"","intervalCount":1,"intervalUnits":"d","submitIncompleteInterval":true,"submitPerTopic":false,"aggregationType":"mean","x":330,"y":5960,"wires":[["facadffb2a1c9b98"]]},{"id":"30d5140be5733937","type":"debug","z":"289f539dcc33814e","name":"Rolling Average 28","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"payload","targetType":"msg","statusVal":"","statusType":"auto","x":770,"y":5960,"wires":[]}]
          

          M 1 Reply Last reply Reply Quote 0
          • M
            Merleg @mickym last edited by

            @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: #":

            b6b48292-800b-4e66-a351-771959527aa6-image.png

            mickym 2 Replies Last reply Reply Quote 0
            • mickym
              mickym Most Active @Merleg last edited by mickym

              @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:

              094a8706-8099-473c-a6f3-fb3ae2c2971b-image.png

              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

              6d1e2025-df7d-4277-a477-86c00cd72d6d-image.png

              Anhand der function Node siehst Du nichts, was passiert.

              M 1 Reply Last reply Reply Quote 0
              • mickym
                mickym Most Active @Merleg last edited by

                @merleg

                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:

                https://try.jsonata.org/0SUk0ycrx

                7ab82806-174c-4bdf-beb1-fa4e0d36abc7-image.png

                1 Reply Last reply Reply Quote 0
                • mickym
                  mickym Most Active last edited by mickym

                  Aber natürlich kannst Du das auch mit einer function Node machen - dann kannst auch den Node Kontext nehmen.

                  [{"id":"facadffb2a1c9b98","type":"change","z":"289f539dcc33814e","name":"Rolling Average 28 ","rules":[{"t":"set","p":"days","pt":"msg","to":"days","tot":"flow"},{"t":"set","p":"days","pt":"msg","to":"$append(payload,days)#$i[$i<28]","tot":"jsonata"},{"t":"set","p":"days","pt":"flow","to":"days","tot":"msg"},{"t":"set","p":"payload","pt":"msg","to":"$average(days)","tot":"jsonata"}],"action":"","property":"","from":"","to":"","reg":false,"x":590,"y":5740,"wires":[["30d5140be5733937"]]},{"id":"5750c041a1659feb","type":"inject","z":"289f539dcc33814e","name":"","props":[{"p":"payload"}],"repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"","payload":"15","payloadType":"num","x":370,"y":5800,"wires":[["facadffb2a1c9b98","2c6e88964a11292a"]]},{"id":"293c36f42771fe8c","type":"aggregator","z":"289f539dcc33814e","name":"","topic":"","intervalCount":1,"intervalUnits":"d","submitIncompleteInterval":true,"submitPerTopic":false,"aggregationType":"mean","x":370,"y":5740,"wires":[["facadffb2a1c9b98","2c6e88964a11292a"]]},{"id":"30d5140be5733937","type":"debug","z":"289f539dcc33814e","name":"Rolling Average 28","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"payload","targetType":"msg","statusVal":"","statusType":"auto","x":810,"y":5740,"wires":[]},{"id":"2c6e88964a11292a","type":"function","z":"289f539dcc33814e","name":"Rolling Average 28","func":"var arr = context.get(\"days\") || []\narr.unshift(msg.payload);\nif (arr.length > 28) arr.pop();\ncontext.set(\"days\",arr);\nmsg.payload = arr.reduce((/** @type {any} */ a, /** @type {any} */ b) => a + b) / arr.length;\nreturn msg;","outputs":1,"timeout":0,"noerr":0,"initialize":"","finalize":"","libs":[],"x":590,"y":5800,"wires":[["f7b2a949588fd495"]]},{"id":"f7b2a949588fd495","type":"debug","z":"289f539dcc33814e","name":"Rolling Average 28","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"payload","targetType":"msg","statusVal":"","statusType":"auto","x":810,"y":5800,"wires":[]}]
                  

                  72b5616d-8fad-471c-9ff7-ec145b0c7e7a-image.png

                  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;
                  
                  1 Reply Last reply Reply Quote 0
                  • M
                    Merleg @mickym last edited by

                    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.

                    https://try.jsonata.org/0SUk0ycrx

                    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!

                    mickym 1 Reply Last reply Reply Quote 0
                    • mickym
                      mickym Most Active @Merleg last edited by

                      @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.

                      1 Reply Last reply Reply Quote 0
                      • First post
                        Last post

                      Support us

                      ioBroker
                      Community Adapters
                      Donate

                      447
                      Online

                      31.8k
                      Users

                      79.9k
                      Topics

                      1.3m
                      Posts

                      2
                      9
                      340
                      Loading More Posts
                      • Oldest to Newest
                      • Newest to Oldest
                      • Most Votes
                      Reply
                      • Reply as topic
                      Log in to reply
                      Community
                      Impressum | Datenschutz-Bestimmungen | Nutzungsbedingungen
                      The ioBroker Community 2014-2023
                      logo