Gleitenden Mittwlwert

Bennyaa

Lieutenant
Registriert
März 2007
Beiträge
828
Hallo, ich möchte für den ioBroker einen Adapter entwickeln, der den gleitenden Mittelwert ausgibt.
Allerdings nicht mit einer bestimmten Anzahl an Werte, sondern bspw. Über 10min gemittelt.
Hierzu müsste ich dann neu reinkommende Werte in ein Array schieben.(wert und Timestamp)
Es kann also im Extremfall sein, dass sich der Wert nicht ändert und ich nur einen Wert im Array habe, oder dass er sich jede Sekunde ändert und ich bspw. 1000 Einträge habe.
Die Frage ist nun, muss ich das selbst über eine for Schleife ausrechnen, oder gibts schon was von math oder sonstigem?
 
Kruzifix, da hab ich doch glatt Gleitmittel gelesen. Wundern würds mich nicht. Also sorry, weitermachen.
 
  • Gefällt mir
Reaktionen: BeBur und lazsniper
So wie ich es verstehe, reicht es doch einfach die eingehenden Werte zu addieren und zusätzlich über einen Zähler die Anzahl der Werte festzuhalten?
Und dann nach 10 minuten die Summe der addierten Werte durch die Summe des Zählers ergibt dann den Mittelwert.

Falls du es unbedingt vorher in einem Array Speichern willst, dann ist count(array) die Anzahl der Einträge bzw das Equivalent des Zählers von oben.
 
Zuletzt bearbeitet:
Schau doch mal hier https://www.metastock.com/customer/resources/taaz/?p=74

Im einfachsten Fall:

1702818896909.png
 
pvcf schrieb:
So wie ich es verstehe, reicht es doch einfach die eingehenden Werte zu addieren und zusätzlich über einen Zähler die Anzahl der Werte festzuhalten?
Und dann nach 10 minuten die Summe der addierten Werte durch die Summe des Zählers ergibt dann den Mittelwert.

Falls du es unbedingt vorher in einem Array Speichern willst, dann ist count(array) die Anzahl der Einträge bzw das Equivalent des Zählers von oben.
Nein eben nicht.
1. gibt es einen anderen Mittelwert,
Wenn das Array zuvor [0,0,0,0,0,5,5,5,5,5].
2. gibt es wie gesagt keine festen Zyklen, also könnte ein Wert 1s anstehen und ein anderer 10s … dieser dann höher gewuchtet werden muss… und dann muss ich halt auch wissen, wann ich dieses hohe Gewicht wieder raus nehmen muss… also nicht so simple +/- wie man denkt.
Ergänzung ()

CyborgBeta schrieb:
Auch das bezieht sich ja auf einen festen Zyklus…. Den ich nicht habe.
 
Bennyaa schrieb:
Auch das bezieht sich ja auf einen festen Zyklus…
Nein, bezieht es sich nicht. Der die Zeitspanne der Kerzen ist egal...
Ergänzung ()

Bennyaa schrieb:
gibt es wie gesagt keine festen Zyklen, also könnte ein Wert 1s anstehen und ein anderer 10s
Ach so, das meintest du mit "nicht fest"...

Dann sind deine Daten einfach unbrauchbar.

2, 4, 3, ... das kann ja dann alles sein: 2 im Vorjahr, 4 vor 9 Monaten und 3 in den letzten 5 Minuten...
 
Einfach "neu abtasten". Aktuellen ausgelesenen Wert in eine Variable schreiben, und jede Sekunde diese Variable auslesen, und in die Queue schieben.

Dann wird aus einem Wert alle 10 Sekunden 10mal der gleiche Wert pro Sekunde. Passt ungefähr.
 
  • Gefällt mir
Reaktionen: pvcf und CyborgBeta
CyborgBeta schrieb:
Nein, bezieht es sich nicht. Der die Zeitspanne der Kerzen ist egal...
Ergänzung ()


Ach so, das meintest du mit "nicht fest"...

Dann sind deine Daten einfach unbrauchbar.

2, 4, 3, ... das kann ja dann alles sein: 2 im Vorjahr, 4 vor 9 Monaten und 3 in den letzten 5 Minuten...
Doch wenn ich ja was durch n teile, dann müssen die n Teile ja gleich groß sein.
ich muss es dann anders berechnen.
 
es kann sein, dass ich nur 1-2 Werte habe, oder ebnen auch viele. Und ich möchte es eben nicht zyklisch auslesen, das war ja gerade die Frage…. Aber anscheinend gibt es nichts fertiges von Math. Ok
 
Bennyaa schrieb:
Und ich möchte es eben nicht zyklisch auslesen
musst du aber... ich schließe mich dem alle 1 Sekunden an. Dann ist es völlig egal, ob du nur einen Wert in 10 Minuten bekommst oder 100. Der daraus errechnete Mittelwert wird dann immer stimmen.

Unabhängig von der Berechnung: die von dir beschriebene Datenlage und das Ziel einen Mitterlwert daraus zu bilden: bist du Sicher dass der Mittelwert daraus wirklich irgendwas bringt? So wie ich das sehe, ist das Messintervall von 10minuten dann offensichtlich nicht brauchbar, eventuell solltest du dann DIESEN Mittelwert auch nochmal separat mit den letzten 20x10 Minuten werten Korrellieren, und dann, abhängig vom Verwendungszweck, bei zu großer Abweichung dann eine extra Info Ausgeben (Alarm, Wert atkuell stark fallend / Steigend).
 
Zuletzt bearbeitet:
Ok, werde es wohl eh abtasten, solang ich keine Änderung erhalte, wird der Wert ja noch gleich sein.
Aber ich muss ihn notgedrungen in ein Array speichern, da ich ja wissen muss, wann ich was raus „pop“en muss.

Jetzt ist noch die Frage, ob ich einfach das was raus fliegt Abzüge und das was reinkommt hinzuzähle…gewuchtet natürlich.
Oder immer komplett das Array durchlaufe… performancemäßig sollte ersteres besser sein, denke ich.
 
Zuletzt bearbeitet:
Bennyaa schrieb:
es kann sein, dass ich nur 1-2 Werte habe, oder ebnen auch viele. Und ich möchte es eben nicht zyklisch auslesen, das war ja gerade die Frage…. Aber anscheinend gibt es nichts fertiges von Math. Ok

Also, wenn die Timestamps für die Werte bekannt sind:

Java:
import java.util.Arrays;
import java.util.DoubleSummaryStatistics;

public class MA {
    public static long minimumAbsoluteDifference(final long[] timestamps) {
        long min = Long.MAX_VALUE;
        for (int i = 1; i < timestamps.length; i++) {
            min = Math.min(min, Math.abs(timestamps[i] - timestamps[i - 1]));
        }
        return min;
    }

    public static DoubleSummaryStatistics getFittedStatistics(final long[] timestamps, final double[] values) {
        if (timestamps.length != values.length) {
            throw new IllegalArgumentException("timestamps.length != values.length");
        }
        {
            long[] tempTimestamps = Arrays.copyOf(timestamps, timestamps.length);
            Arrays.sort(tempTimestamps);
            if (!Arrays.equals(timestamps, tempTimestamps)) {
                throw new IllegalArgumentException("timestamps != sorted timestamps");
            }
        }

        long minimum = minimumAbsoluteDifference(timestamps);
        int len = (int) Math.ceil((timestamps[timestamps.length - 1] - timestamps[0]) / (double) minimum);
        double[] newValues = new double[len];
        for (int i = 0; i < len; i++) {
            long timestamp = timestamps[0] + i * minimum;
            int j = 0;
            while (j < timestamps.length && timestamps[j] < timestamp) {
                j++;
            }
            newValues[i] = values[j];
        }
        System.out.println("newValues = " + Arrays.toString(newValues));
        return Arrays.stream(newValues)
                .summaryStatistics();
    }

    public static void main(final String[] args) {
        long[] timestamps = {
                1500, 1510, 1520, 1600, 1602, 1777, 1885, 2000, 2010
        };
        double[] prices = {
                1.51, 1.62, 1.43, 1.34, 1.85, 1.76, 1.97, 1.18, 1.19
        };
        DoubleSummaryStatistics statistics = getFittedStatistics(timestamps, prices);
        System.out.println(statistics);
        // -> DoubleSummaryStatistics{count=255, sum=404,910000, min=1,180000, average=1,587882, max=1,970000}
    }
}
 
Bennyaa schrieb:
Oder immer komplett das Array durchlaufe… performancemäßig sollte ersteres besser sein, denke ich.

Wie groß soll das Array denn sein ? Und soll das ganze auf einem Arduino oder dergleichen laufen ? Mit einem Mittel über 100 Werte jede Sekunde hätte nicht einmal ein C64 ein Performanceproblem.

Das Array am besten bei Programmstart vorher mit einem "Ungültig-Platzhalter" wie 0 oder -1 füllen, der dann bei der Mittelung ausgelassen wird.
 
Das Intervall ist schon ok… nur ob ein Wert neu ankommt hängt vom heimautomatisierungssystem ab. Es ist nicht speziell für eine Anwendung und es können x beliebige Instanzen erzeugt werden.
 
Musst du dir denn überhaupt Gedanken über die Performance machen? Besonders rechenintensiv klingt das für mich nicht. Ob du dann 600 Einträge (10 min * 1/s) aufaddierst, oder ob du nur zu einem Wert zwei Werte addierst/subtrahierst könnte dann egal sein. Bei konstanter Abtastrate wäre dabei auch keine Gewichtung nötig.

Der andere, exaktere Ansatz wäre genauso möglich und auch nicht übermäßig kompliziert. Also die anliegende Zeit eines Werts aus den Zeitstempeln bestimmen und entsprechend gewichten. Führt halt tendenziell zu mehr Rechenaufwand, dafür geringeren Speicherbedarf. (Zumindest, solange der Wert sich durchschnittlich weniger häufig als die Abtastrate ändert.) Ich verstehe ehrlich gesagt nicht, warum der Ansatz hier als nicht möglich verschrien wird.

Und zuletzt, welche Sprache eigentlich? Ist das über ioBroker (sagt mir nichts) ersichtlich? Module namens Math gibt es in mehreren Programmiersprachen.
 
  • Gefällt mir
Reaktionen: CyborgBeta
Ich seh gerad, die Berechnung in der Funktion getFittedStatistics im Beitrag #13 geht auch einfacher, wenn einfach nur der Durchschnitt gesucht ist... denn dann muss nicht extra ein neues Array mit allen Werten aufgebaut werden.

Es genügt dann, wie von @simpsonsfan beschrieben, die einzelnen Summanden einfach zu gewichten und zu addieren.

Dadurch spart man Speicherplatz und Rechenzeit ein.
 
Genau um sowas ging es mir. Ich machte mir nur Gedanken, ob es dann irgendwann „weg läuft“ durch rundungsfehler
 
Java:
import java.util.Arrays;

public class MA {

    public static double getAverage(final long[] timestamps, final double[] values) {
        if (timestamps.length != values.length) {
            throw new IllegalArgumentException("timestamps.length != values.length");
        }
        {
            long[] tempTimestamps = Arrays.copyOf(timestamps, timestamps.length);
            Arrays.sort(tempTimestamps);
            if (!Arrays.equals(timestamps, tempTimestamps)) {
                throw new IllegalArgumentException("timestamps != sorted timestamps");
            }
        }

        double sum = values[0];
        long timeSum = 0;
        for (int i = 1; i < timestamps.length; i++) {
            long timeDiff = timestamps[i] - timestamps[i - 1];
            sum += values[i] * timeDiff;
            timeSum += timeDiff;
        }

        return sum / timeSum;
    }

    public static void main(final String[] args) {
        long[] timestamps = {
                1500, 1510, 1520, 1600, 1602, 1777, 1885, 2000, 2010
        };
        double[] prices = {
                1.51, 1.62, 1.43, 1.34, 1.85, 1.76, 1.97, 1.18, 1.19
        };
        System.out.println(getAverage(timestamps, prices));
        // -> DoubleSummaryStatistics{count=255, sum=404,910000, min=1,180000, average=1,587882, max=1,970000}
    }
}

Bennyaa schrieb:
Ich machte mir nur Gedanken, ob es dann irgendwann „weg läuft“ durch rundungsfehler

Rundungsfehler hast du bei so etwas immer. Es geht nur darum, die möglichst gering zu halten.
 
Ja, dachte nur beim kompletten Arraydurchlauf eben nicht…. Aber mal versuchen.
 
Zurück
Oben