Thursday, 9 November 2017

Gewichtsbewegungsdurchschnitt


Ich versuche, den gleitenden Durchschnitt eines Signals zu berechnen. Der Signalwert (a double) wird zu beliebigen Zeiten aktualisiert. Ich suche nach einem effizienten Weg, um seinen zeitgewichteten Durchschnitt über ein Zeitfenster in Echtzeit zu berechnen. Ich könnte es selbst tun, aber es ist schwieriger als ich dachte Die meisten der Ressourcen, die ich über das Internet gefunden habe, berechnen den gleitenden Durchschnitt des periodischen Signals, aber meine Updates zufällig. Kennt jemand gute Ressourcen dafür Der Trick ist der folgende: Sie erhalten Updates zufällig über void update (int time, float value). Allerdings müssen Sie auch auch verfolgen, wenn ein Update aus dem Zeitfenster fällt, also setzen Sie einen Alarm, der zur Zeit N aufruft, der das vorherige Update entfernt, von der jemals wieder in der Berechnung berücksichtigt wird. Wenn dies in Echtzeit geschieht, können Sie das Betriebssystem anfordern, um einen Anruf zu einer Methode zu machen void dropoffoldestupdate (int time) zum Zeitpunkt N aufgerufen werden. Wenn dies eine Simulation ist, können Sie keine Hilfe vom Betriebssystem erhalten und Sie müssen Mach es manuell. In einer Simulation nennst du Methoden mit der Zeit, die als Argument geliefert wird (was nicht mit der Realzeit korreliert). Eine vernünftige Annahme ist jedoch, dass die Anrufe so garantiert werden, dass die Zeitargumente zunehmen. In diesem Fall müssen Sie eine sortierte Liste von Alarmzeitwerten pflegen und für jeden Aktualisierungs - und Leseaufruf überprüfen Sie, ob das Zeitargument größer ist als der Kopf der Alarmliste. Während es größer ist, machst du die Alarmverarbeitung (Drop off die älteste Aktualisierung), entfernen Sie den Kopf und überprüfen Sie noch einmal, bis alle Alarme vor der angegebenen Zeit verarbeitet werden. Dann rufe das Update auf. Ich habe so weit davon ausgegangen, dass es offensichtlich ist, was Sie für die eigentliche Berechnung tun würden, aber ich werde nur im Fall erarbeiten. Ich nehme an, Sie haben eine Methode float lesen (int Zeit), die Sie verwenden, um die Werte zu lesen. Ziel ist es, diesen Anruf so effizient wie möglich zu machen. So berechnen Sie den gleitenden Durchschnitt nicht jedes Mal, wenn die Lesemethode aufgerufen wird. Stattdessen berechnen Sie den Wert ab dem letzten Update oder dem letzten Alarm und optimieren diesen Wert um ein paar Gleitkommaoperationen, um den Ablauf der Zeit seit dem letzten Update zu berücksichtigen. (Z. B. eine konstante Anzahl von Operationen, außer bei der Verarbeitung einer Liste von aufgestauten Alarmen). Hoffentlich ist das klar - das sollte ein ganz einfacher Algorithmus und sehr effizient sein. Weitere Optimierung. Eines der verbleibenden Probleme ist, wenn eine große Anzahl von Updates innerhalb des Zeitfensters passiert, dann gibt es eine lange Zeit, für die es weder Lesungen noch Updates gibt und dann ein Lesen oder Update kommt. In diesem Fall ist der obige Algorithmus ineffizient in der inkrementellen Aktualisierung des Wertes für jeden der Updates, die abfällt. Dies ist nicht notwendig, weil wir uns nur um die letzte Aktualisierung über das Zeitfenster kümmern, also wenn es einen Weg gibt, um alle älteren Updates effizient abzusetzen, würde es helfen. Um dies zu tun, können wir den Algorithmus ändern, um eine binäre Suche nach Updates durchzuführen, um das aktuellste Update vor dem Zeitfenster zu finden. Wenn es relativ wenige Updates gibt, die gelöscht werden müssen, dann kann man den Wert für jedes gelöschte Update inkrementell aktualisieren. Aber wenn es viele Updates gibt, die gelöscht werden müssen, dann kann man den Wert von neuem neu berechnen, nachdem er die alten Updates abgelegt hat. Anhang zur Inkrementalberechnung: Ich sollte klären, was ich mit der inkrementellen Berechnungen oben im Satz tue, um diesen Wert durch ein paar Gleitkommaoperationen zu zwingen, um den Ablauf der Zeit seit dem letzten Update zu berücksichtigen. Initiale nicht-inkrementale Berechnungen: dann iterate über relevanteupdates in der Reihenfolge der zunehmenden Zeit: moveaverage (sum lastupdate timesincelastupdate) windowlength. Nun, wenn genau ein Update aus dem Fenster fällt, aber keine neuen Updates ankommen, stelle die Summe wie folgt ein: (Notiz ist es, dass der Zeitstempel geändert wurde, um den Anfang des letzten Fensters zu starten). Und wenn genau ein Update in das Fenster eintritt, aber keine neuen Updates abfallen, stelle die Summe als: Wie sollte es offensichtlich sein, das ist eine grobe Skizze, aber hoffentlich zeigt es, wie man den Durchschnitt so beibehalten kann, dass es O (1) Operationen pro Update ist Auf amortisierter Basis. Aber bemerken Sie weitere Optimierung im vorherigen Absatz. Beachten Sie auch Stabilitätsprobleme, die in einer älteren Antwort angedeutet werden, was bedeutet, dass sich Gleitkomma-Fehler über eine große Anzahl solcher inkrementeller Operationen ansammeln können, so dass es eine Abweichung von dem Ergebnis der vollständigen Berechnung gibt, die für die Anwendung signifikant ist. Wenn eine Annäherung in Ordnung ist und theres eine minimale Zeit zwischen den Proben, könnte man Super-Sampling versuchen. Haben Sie ein Array, das gleichmäßig beabstandete Zeitintervalle repräsentiert, die kürzer als das Minimum sind, und bei jedem Zeitspanne das letzte erhaltene Sample speichern. Je kürzer das Intervall ist, desto näher wird der Durchschnitt auf den wahren Wert. Der Zeitraum sollte nicht größer als die Hälfte des Minimums sein oder es besteht die Möglichkeit, eine Probe zu fehlen. Antwortete Dec 15 11 um 18:12 antwortete Dec 15 11 um 22:38 Vielen Dank für die Antwort. Eine Verbesserung, die es nötig wäre, um den Wert des Gesamtdurchschnitts tatsächlich zu notieren, so dass wir die ganze Zeit nicht schleifen. Auch kann es ein kleiner Punkt sein, aber wäre es nicht effizienter, einen Deque oder eine Liste zu verwenden, um den Wert zu speichern, da wir davon ausgehen, dass das Update in der richtigen Reihenfolge kommen wird. Einfügung wäre schneller als in der Karte. Ndash Arthur Dec 16 11 at 8:55 Ja, du kannst den Wert der Summe zwischenspeichern. Subtrahieren Sie die Werte der Samples, die Sie löschen, fügen Sie die Werte der Proben hinzu, die Sie einfügen. Auch, ja, ein dequeltpairltSample, Dategtgt könnte effizienter sein. Ich wählte Karte für Lesbarkeit, und die Leichtigkeit der Aufruf Karte :: upperbound. Wie immer, schreiben Sie korrekten Code zuerst, dann Profil und messen Sie inkrementelle Änderungen. Ndash Rob Dez 16 11 um 15:00 Hinweis: Anscheinend ist dies nicht der Weg, um dies zu nähern. Verlassen Sie es hier als Referenz auf das, was mit diesem Ansatz falsch ist. Überprüfe die Kommentare. AKTUALISIERT - basierend auf Olis Kommentar. Nicht sicher über die Instabilität, über die er spricht. Verwenden Sie eine sortierte Karte der Ankunftszeiten gegen Werte. Bei der Ankunft eines Wertes fügen Sie die Ankunftszeit zu der sortierten Karte zusammen mit seinem Wert hinzu und aktualisieren Sie den gleitenden Durchschnitt. Warnung das ist Pseudocode: Dort. Nicht voll ausgefunden, aber du bekommst die Idee. Dinge zu beachten. Wie ich schon sagte, ist der Pseudocode. Du musst eine passende Karte auswählen. Dont entfernen Sie die Paare, wie Sie durch, wie Sie den Iterator ungültig machen und müssen wieder anfangen. Siehe Olis Kommentar unten auch. Beantwortet Dec 15 11 at 12:22 Das funktioniert nicht: Es wird nicht berücksichtigt, welcher Anteil der Fensterlänge jeder Wert existiert. Auch dieser Ansatz des Hinzufügens und dann Subtrahieren ist nur stabil für ganzzahlige Typen, nicht Floats. Ndash Oliver Charlesworth Dec 15 11 at 12:29 OliCharlesworth - Entschuldigung Ich vermisse einige wichtige Punkte in der Beschreibung (doppelt und zeitlich gewichtet). Ich werde aktualisieren Vielen Dank. Ndash Dennis Dec 15 11 at 12:33 Die Zeitgewichtung ist ein weiteres Problem. Aber das ist nicht das, worüber ich rede. Ich bezog mich auf die Tatsache, dass, wenn ein neuer Wert zuerst das Zeitfenster betritt, sein Beitrag zum Durchschnitt minimal ist. Der Beitrag steigt weiter an, bis ein neuer Wert eintritt. Ndash Oliver Charlesworth Dec 15 11 at 12: 35 Ich habe im Wesentlichen eine Reihe von Werten wie folgt: Das obige Array ist vereinfacht, Im sammeln 1 Wert pro Millisekunde in meinem realen Code und ich muss die Ausgabe auf einem Algorithmus verarbeiten, den ich geschrieben habe, um die zu finden Nächster Gipfel vor einem Zeitpunkt. Meine Logik scheitert, weil in meinem Beispiel oben, 0.36 ist die reale Spitze, aber mein Algorithmus würde nach hinten schauen und sehen die letzte Zahl 0,25 als die Spitze, als theres eine Abnahme auf 0,24 vor ihm. Das Ziel ist, diese Werte zu nehmen und einen Algorithmus an sie anzuwenden, der sie ein bisschen glättet, so dass ich mehr lineare Werte habe. (Dh: Id wie meine Ergebnisse zu curvy, nicht Jaggedy) Ive wurde gesagt, um einen exponentiellen gleitenden durchschnittlichen Filter auf meine Werte anzuwenden. Wie kann ich das tun? Es ist wirklich schwer für mich, mathematische Gleichungen zu lesen, ich mache viel besser mit Code. Wie verarbeite ich Werte in meinem Array, indem du eine exponentielle gleitende durchschnittliche Berechnung anwende, um sie herauszufordern, um den 8. Februar 12 um 20:27 zu bitten, um einen exponentiellen gleitenden Durchschnitt zu berechnen. Du musst einen Zustand halten und du brauchst einen Tuning-Parameter. Dies fordert eine kleine Klasse (vorausgesetzt, du bist mit Java 5 oder höher): Instantiieren Sie mit dem Zerfallsparameter, den Sie wollen (kann die Abstimmung zwischen 0 und 1) und dann mit durchschnittlichen () zu filtern. Beim Lesen einer Seite auf einige mathematische Wiederholung, alles, was Sie wirklich wissen müssen, wenn es in Code ist, dass Mathematiker gerne Indizes in Arrays und Sequenzen mit Indizes schreiben. (Sie haben auch ein paar andere Notationen, was nicht hilft.) Allerdings ist die EMA ziemlich einfach, da man sich nur an einen alten Wert erinnern muss, keine komplizierten Zustand Arrays erforderlich. Antwortete Feb 8 12 um 20:42 TKKocheran: Ziemlich viel. Isn39t es schön, wenn es einfach sein kann (wenn man mit einer neuen Sequenz beginnt, bekomme du einen neuen Mittelwert.) Beachten Sie, dass die ersten paar Begriffe in der gemittelten Sequenz ein bisschen wegen der Brenneffekte umgehen werden, aber man bekommt diese mit anderen gleitenden Durchschnitten auch. Allerdings ist ein guter Vorteil, dass man die gleitende durchschnittliche Logik in den Mittelalter einpacken und experimentieren kann, ohne den Rest deines Programms zu stark zu stören. Ndash Donal Fellows Feb 9 12 at 0:06 Ich habe eine harte Zeit, Ihre Fragen zu verstehen, aber ich werde versuchen, trotzdem zu antworten. 1) Wenn dein Algorithmus 0,25 statt 0,36 gefunden hat, dann ist es falsch Es ist falsch, weil es eine monotone Zunahme oder Abnahme annimmt (das geht immer nach oben oder immer nach unten). Es sei denn, du durchschnittst alle deine Daten, deine Datenpunkte - wie du sie präsentierst - sind nichtlinear. Wenn Sie wirklich wollen, um den maximalen Wert zwischen zwei Punkten in der Zeit zu finden, dann schneiden Sie Ihr Array von tmin zu tmax und finden Sie die max dieser Subarray. 2) Nun ist das Konzept der gleitenden Durchschnitte sehr einfach: Stellen Sie sich vor, dass ich die folgende Liste habe: 1.4, 1.5, 1.4, 1.5, 1.5. Ich kann es glatt machen, indem ich den Durchschnitt von zwei Zahlen: 1.45, 1.45, 1.45, 1.5. Beachten Sie, dass die erste Zahl ist der Durchschnitt von 1,5 und 1,4 (zweite und erste Zahlen) die zweite (neue Liste) ist der Durchschnitt von 1,4 und 1,5 (dritte und zweite alte Liste) die dritte (neue Liste) der Durchschnitt von 1,5 und 1,4 (Vierte und dritte) und so weiter. Ich hätte es mal drei oder vier machen können, oder n. Beachten Sie, wie die Daten viel glatter sind. Ein guter Weg, um gleitende Durchschnitte bei der Arbeit zu sehen, ist, zu Google Finance zu gehen, eine Aktie auszuwählen (Tesla Motors ziemlich flüchtig (TSLA) auszuprobieren) und klicken Sie auf die Technik am unteren Rand des Diagramms. Wählen Sie Moving Average mit einem bestimmten Zeitraum und Exponential gleitenden Durchschnitt, um ihre Unterschiede zu vergleichen. Exponentieller gleitender Durchschnitt ist nur eine weitere Ausarbeitung von diesem, aber gewichtet die älteren Daten weniger als die neuen Daten ist dies ein Weg, um die Glättung nach hinten voranzutreiben. Bitte lesen Sie den Wikipedia-Eintrag. Also, das ist mehr ein Kommentar als eine Antwort, aber die kleine Kommentar-Box war nur zu winzig. Viel Glück. Wenn du Schwierigkeiten mit der Mathematik hast, könntest du mit einem einfachen gleitenden Durchschnitt anstatt exponentiell gehen. Also die Ausgabe, die du bekommst, wäre die letzten x Begriffe geteilt durch x. Ungetesteter Pseudocode: Beachten Sie, dass Sie die Start - und Endteile der Daten behandeln müssen, da Sie deutlich die letzten 5 Begriffe haben, wenn Sie auf Ihrem 2. Datenpunkt sind. Auch gibt es effizientere Möglichkeiten, diesen gleitenden Durchschnitt zu berechnen (Summsumme - älteste neueste), aber das ist es, das Konzept zu bekommen, was passiert. Antwortete am 8. Februar 12 um 20: 41Ist es möglich, einen gleitenden Durchschnitt in C ohne die Notwendigkeit für ein Fenster von Proben Ive gefunden, dass ich ein bisschen optimieren kann, indem Sie eine Fenstergröße, die eine Macht von zwei, um Bit-Verschiebung zu ermöglichen Anstatt zu teilen, aber nicht brauchen einen Puffer wäre schön. Gibt es eine Möglichkeit, ein neues gleitendes durchschnittliches Ergebnis nur als eine Funktion des alten Ergebnisses auszudrücken und das neue Sample Definieren Sie ein Beispiel gleitender Durchschnitt, über ein Fenster von 4 Samples: Add new sample e: Ein gleitender Durchschnitt kann rekursiv implementiert werden , Aber für eine genaue Berechnung des gleitenden Durchschnitts müssen Sie sich an die älteste Eingabe Probe in der Summe (dh die a in Ihrem Beispiel) erinnern. Für eine Länge N gleitenden Durchschnitt berechnen Sie: wobei yn das Ausgangssignal ist und xn das Eingangssignal ist. Gl. (1) kann rekursiv geschrieben werden, also musst du dich immer an die Probe xn-N erinnern, um zu berechnen (2). Wie von Conrad Turner hervorgehoben, können Sie stattdessen ein (unendlich langes) exponentielles Fenster verwenden, mit dem Sie die Ausgabe nur aus der Vergangenheit und dem aktuellen Eingang berechnen können. Dies ist jedoch kein Standard (ungewichtet) gleitender Durchschnitt, sondern exponentiell Gewichteter gleitender Durchschnitt, wo Proben in der Vergangenheit ein kleineres Gewicht bekommen, aber (zumindest in der Theorie) vergisst du niemals etwas (die Gewichte werden in der Vergangenheit immer kleiner und kleiner). Ich habe einen gleitenden Durchschnitt ohne Einzelposten-Speicher für ein GPS-Tracking-Programm, das ich geschrieben habe. Ich fange mit 1 Probe an und teile mit 1, um die aktuelle avg zu bekommen. Ich füge dann eine Probe hinzu und teile mit 2 auf die aktuelle avg. Das geht weiter, bis ich die Länge des Durchschnitts erreicht habe. Jedes Mal danach füge ich die neue Probe hinzu, bekomme den Durchschnitt und beseitige diesen Durchschnitt von der Summe. Ich bin kein Mathematiker, aber das schien ein guter Weg, es zu tun. Ich dachte, es würde den Magen eines echten Mathe-Kerls drehen, aber es stellt sich heraus, dass es eine der akzeptierten Möglichkeiten ist, es zu tun. Und es geht gut Denken Sie daran, dass je höher Ihre Länge desto langsamer ist es, was Sie folgen wollen. Das mag die meiste Zeit nicht ausmachen, aber wenn man den Satelliten folgt, wenn man langsam ist, könnte der Weg weit von der aktuellen Position entfernt sein und es wird schlecht aussehen. Du hättest eine Lücke zwischen dem Sat und den hinteren Punkten. Ich wählte eine Länge von 15 aktualisiert 6 mal pro Minute, um ausreichende Glättung zu bekommen und nicht zu weit von der tatsächlichen Sat-Position mit den geglätteten Pfad-Punkten zu bekommen. Antwortete 16. November 16 um 23:03 initialize total 0, count0 (jedes Mal, wenn du einen neuen Wert sehe, dann eine Eingabe (scanf), man add totalnewValue, ein Inkrement (count), ein divide average (totalcount) Dies wäre ein gleitender Durchschnitt über Alle Eingänge Um den Durchschnitt über nur die letzten 4 Eingänge zu berechnen, würde es 4 Eingangsvariablen erfordern, vielleicht jede Eingabe in einen älteren Eingabevariablen kopieren und dann den neuen gleitenden Durchschnitt berechnen, als Summe der 4 Eingangsvariablen, geteilt durch 4 (rechte Verschiebung 2 wäre Gut, wenn alle Eingänge waren positiv, um die durchschnittliche Berechnung beantwortet Feb 3 15 um 4:06 Das wird tatsächlich berechnen den Gesamtdurchschnitt und NICHT der gleitende Durchschnitt. Wie Zähler wird größer die Auswirkungen einer neuen Eingabe Probe wird verschwindend klein ndash Hilmar Feb 3 15 um 13:53 Ihre Antwort 2017 Stack Exchange, Inc

No comments:

Post a Comment