405 Stimmen

Spitzensignalerfassung in Echtzeit-Zeitreihendaten


Aktualisierung: Der bisher beste Algorithmus bisher ist dieser.


Diese Frage erkundet robuste Algorithmen zur Erkennung plötzlicher Peaks in echtzeitfähigen Zeitreihendaten.

Betrachten Sie das folgende Beispiel für Daten:

Plot der Daten

Beispiel dieser Daten ist im Matlab-Format (aber es handelt sich bei dieser Frage nicht um die Sprache, sondern um den Algorithmus):

p = [1 1 1.1 1 0.9 1 1 1.1 1 0.9 1 1.1 1 1 0.9 1 1 1.1 1 1 1 1 1.1 0.9 1 1.1 1 1 0.9, ...
     1 1.1 1 1 1.1 1 0.8 0.9 1 1.2 0.9 1 1 1.1 1.2 1 1.5 1 3 2 5 3 2 1 1 1 0.9 1 1, ... 
     3 2.6 4 3 3.2 2 1 1 0.8 4 4 2 2.5 1 1 1];

Es ist deutlich zu erkennen, dass es drei große Peaks und einige kleine Peaks gibt. Diese Datensatz ist ein spezifisches Beispiel aus der Klasse von Zeitreihendatensätzen, um die es in der Frage geht. Diese Datenklasse hat zwei allgemeine Eigenschaften:

  1. Es gibt grundlegendes Rauschen mit einem allgemeinen Mittelwert
  2. Es gibt große 'Peaks' oder 'höhere Datenpunkte', die signifikant vom Rauschen abweichen.

Angenommen wird auch folgendes:

  • Die Breite der Peaks kann nicht im Voraus bestimmt werden
  • Die Höhe der Peaks weicht signifikant von den anderen Werten ab
  • Der Algorithmus aktualisiert sich in Echtzeit (also mit jedem neuen Datenpunkt)

In einem solchen Szenario muss ein Grenzwert erstellt werden, der Signale auslöst. Allerdings kann der Grenzwert nicht statisch sein und muss in Echtzeit mithilfe eines Algorithmus bestimmt werden.


Meine Frage: Welcher Algorithmus ist gut geeignet, um solche Schwellenwerte in Echtzeit zu berechnen? Gibt es spezifische Algorithmen für solche Situationen? Was sind die bekanntesten Algorithmen?


Robuste Algorithmen oder nützliche Einsichten werden sehr geschätzt. (kann in jeder Sprache antworten: es geht um den Algorithmus)

3voto

Marc Punkte 64

Ich denke, dass die Python-Antwort von delica einen Fehler hat. Ich kann seinen Beitrag nicht kommentieren, da ich nicht die Reputation dazu habe und die Bearbeitungswarteschlange voll ist, sodass ich wahrscheinlich nicht der Erste bin, der es bemerkt hat.

avgFilter[lag - 1] und stdFilter[lag - 1] werden im Init gesetzt und dann erneut gesetzt, wenn lag == i, anstatt den Wert [lag] zu ändern. Das führt dazu, dass das erste Signal immer 1 ist.

Hier ist der Code mit der kleinen Korrektur:

import numpy as np

class Echtzeit-Spitzenfindung():
    def __init__(self, array, lag, threshold, influence):
        self.y = list(array)
        self.length = len(self.y)
        self.lag = lag
        self.threshold = threshold
        self.influence = influence
        self.signals = [0] * len(self.y)
        self.filteredY = np.array(self.y).tolist()
        self.avgFilter = [0] * len(self.y)
        self.stdFilter = [0] * len(self.y)
        self.avgFilter[self.lag - 1] = np.mean(self.y[0:self.lag]).tolist()
        self.stdFilter[self.lag - 1] = np.std(self.y[0:self.lag]).tolist()

    def thresholding_algo(self, new_value):
        self.y.append(new_value)
        i = len(self.y) - 1
        self.length = len(self.y)
        if i < self.lag:
            return 0
        elif i == self.lag:
            self.signals = [0] * len(self.y)
            self.filteredY = np.array(self.y).tolist()
            self.avgFilter = [0] * len(self.y)
            self.stdFilter = [0] * len(self.y)
            self.avgFilter[self.lag] = np.mean(self.y[0:self.lag]).tolist()
            self.stdFilter[self.lag] = np.std(self.y[0:self.lag]).tolist()
            return 0

        self.signals += [0]
        self.filteredY += [0]
        self.avgFilter += [0]
        self.stdFilter += [0]

        if abs(self.y[i] - self.avgFilter[i - 1]) > self.threshold * self.stdFilter[i - 1]:
            if self.y[i] > self.avgFilter[i - 1]:
                self.signals[i] = 1
            else:
                self.signals[i] = -1

            self.filteredY[i] = self.influence * self.y[i] + (1 - self.influence) * self.filteredY[i - 1]
            self.avgFilter[i] = np.mean(self.filteredY[(i - self.lag):i])
            self.stdFilter[i] = np.std(self.filteredY[(i - self.lag):i])
        else:
            self.signals[i] = 0
            self.filteredY[i] = self.y[i]
            self.avgFilter[i] = np.mean(self.filteredY[(i - self.lag):i])
            self.stdFilter[i] = np.std(self.filteredY[(i - self.lag):i])

        return self.signals[i]

3voto

Jason C Punkte 35780

C++ (Qt) Demo Port, Interaktive Parameter

Ich habe die Demo-Anwendung für diesen Algorithmus nach C++ (Qt) portiert.

Der Code kann hier auf GitHub gefunden werden. Ein Windows (64-Bit) Build mit einem Installer ist auf der Releases-Seite verfügbar. Schließlich werde ich etwas Dokumentation und andere Release-Builds hinzufügen.

Man kann keine Punkte zeichnen, aber man kann sie aus Textdateien importieren (Leerzeichen-getrennte Punkte - Zeilenumbrüche zählen als Leerzeichen). Man kann auch die Algorithmus-Parameter anpassen und die Effekte in Echtzeit sehen. Es ist sehr nützlich, um den Algorithmus an einen bestimmten Datensatz anzupassen und zu erkunden, wie die Parameter die Ergebnisse beeinflussen.

Bildbeschreibung hier eingeben


Der obige Screenshot ist etwas veraltet; ich habe seitdem zwei experimentelle Optionen hinzugefügt, die nicht im Originalalgorithmus enthalten sind:

  • Option, den Datensatz rückwärts zu verarbeiten (scheint die Ergebnisse zumindest für Leistungsspektren zu verbessern).
  • Option, einen festen Mindestschwellenwert für einen Peak festzulegen.

Ich habe auch eine klumpige Zoom-/Pan-Leiste in der Mitte des Fensters hinzugefügt, ziehe sie einfach mit der Maus, um zu zoomen und zu schwenken.

Vage Build-Anweisungen:

Es gibt ein Windows-Installationsprogramm (64-Bit) auf der Releases-Seite, aber wenn Sie es aus dem Quellcode erstellen möchten, sind die wesentlichen Schritte:

  • Installiere entweder Qt's Build-Tools und führe dann qmake && make im selben Verzeichnis wie die .pro-Datei aus, oder
  • Installiere Qt Creator, öffne die .pro-Datei, wähle eine Standard-Build-Konfiguration aus und drücke den Build- und/oder Ausführungsbutton (unten links von Creator).

Ich habe nur mit Qt5 getestet. Ich bin zu 91% sicher, dass der Qt Creator-Installer es Ihnen erlauben wird, Qt5 zu installieren, wenn Sie die Komponenten manuell konfigurieren (wenn Sie es tun, möchten Sie auch bestätigen, dass Qt Charts installiert ist). Qt6 könnte ein reibungsloser Build sein oder auch nicht. Eines Tages werde ich Qt4 und Qt6 testen und diese Dokumentation verbessern. Vielleicht.

3voto

hotpaw2 Punkte 69093

Wenn der Grenzwert oder andere Kriterien von zukünftigen Werten abhängen, dann ist die einzige Lösung (ohne eine Zeitmaschine oder anderes Wissen über zukünftige Werte) zu warten, bis ausreichend zukünftige Werte vorhanden sind, um eine Entscheidung zu treffen. Wenn Sie beispielsweise eine Ebene über einem Mittelwert wollen, die beispielsweise 20 Punkte umfasst, müssen Sie warten, bis Sie mindestens 19 Punkte vor einer Spitzenentscheidung haben, sonst könnte der nächste neue Punkt Ihre Schwelle vor 19 Punkte vollständig durcheinanderbringen.

Hinzugefügt: Wenn die statistische Verteilung der Spitzenhöhen schwerfällig sein könnte, anstatt gleichmäßig oder gaußförmig, müssen Sie möglicherweise warten, bis Sie mehrere tausend Spitzen sehen, bevor es unwahrscheinlich wird, dass eine verborgene Pareto-Verteilung eine Spitze erzeugt, die viele Male größer ist als alle bisher gesehenen oder in Ihrem aktuellen Plot haben. Es sei denn, Sie wissen irgendwie im Voraus, dass der nächste Punkt nicht 1e20 sein kann, könnte er erscheinen, was nach Neuskalierung der Y-Dimension Ihres Plots bis zu diesem Punkt flach wäre.

3voto

mrk Punkte 6799

Die Funktion scipy.signal.find_peaks, wie der Name schon sagt, ist hierfür nützlich. Aber es ist wichtig, die Parameter width, threshold, distance und vor allem prominence gut zu verstehen, um eine gute Spitzenextraktion zu erreichen.

Nach meinen Tests und der Dokumentation ist das Konzept der prominence "das nützliche Konzept", um die guten Peaks zu behalten und die verrauschten Peaks zu verwerfen.

Was ist (topographische) Prominenz? Es ist "die minimale Höhe, die erforderlich ist, um vom Gipfel zu einem höheren Gelände abzusteigen", wie hier zu sehen ist:

Die Idee lautet:

Je höher die Prominenz, desto "wichtiger" ist der Gipfel.

3voto

Ocean Airdrop Punkte 2509

Wenn Sie Ihre Daten in einer Datenbanktabelle haben, hier ist eine SQL-Version eines einfachen Z-Score-Algorithmus:

with data_with_zscore as (
    select
        date_time,
        value,
        value / (avg(value) over ()) as pct_of_mean,
        (value - avg(value) over ()) / (stdev(value) over ()) as z_score
    from {{tablename}}  where datetime > '2018-11-26' and datetime < '2018-12-03'
)

-- alles auswählen
select * from data_with_zscore 

-- nur Punkte größer als einen bestimmten Schwellenwert auswählen
select * from data_with_zscore where z_score > abs(2)

CodeJaeger.com

CodeJaeger ist eine Gemeinschaft für Programmierer, die täglich Hilfe erhalten..
Wir haben viele Inhalte, und Sie können auch Ihre eigenen Fragen stellen oder die Fragen anderer Leute lösen.

Powered by:

X