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)

1voto

DieOde Punkte 81

Fsharp Implementierung der Lösung von Brakel, J.P.G. van

#r "nuget: FSharp.Stats"

open FSharp.Stats

/// 
/// 
/// Brakel, J.P.G. van (2014). "Robuster Spitzenentdeckungsalgorithmus unter Verwendung von Z-Scores".
/// Stack Overflow. Verfügbar unter:
/// diesem Link
/// (Version: 2020-11-08).
/// 
/// 
Modul zScorePeakDetection =
    /// 
    /// Berechnen des Durchschnitts einer Liste
    /// 
    let Mean (values: list) = values |> List.average

    /// 
    /// Standardabweichung unter Verwendung der FSharp.Stats NuGet Package-Bibliothek.
    /// 
    let StdDev (values: list) = Seq.stDevPopulation values

    /// 
    /// Spitzenentdeckungsalgorithmus
    /// 
    /// Dies sind die Rohdaten, von denen die Spitzenentdeckung gewünscht ist
    /// Die Probenverzögerung, auf der Beobachtungen basieren sollen. Ein Wert von 5 bedeutet ein gleitendes Fenster von 5 Proben, auf dem der Durchschnitt und die Standardabweichung basieren sollen
    /// Signal, bei dem es so viele Standardabweichungen vom Durchschnitt entfernt sein soll
    /// Zwischen 0 (kein Einfluss) und 1 (voller Einfluss)
    /// Tupel des averageFilter, der Signale und des stdFilter
    let StartAlgo (input: list) (lag: int) (threshold: double) (influence: double) =
        let signals = Array.init input.Length (fun _ -> 0)
        let filteredY = input |> List.toArray
        let (averageFilter: array) = Array.init input.Length (fun _ -> 0.0)
        let (stdFilter: array) = Array.init input.Length (fun _ -> 0.0)

        let initialWindow = filteredY |> Array.take lag |> Array.toList

        averageFilter[lag - 1] <- Mean initialWindow
        stdFilter[lag - 1] <- StdDev initialWindow

        for i = lag to (input.Length - 1) do
            if (abs (input[i] - averageFilter[i - 1]) > threshold * stdFilter[i - 1]) then
                signals[i] <- if (input[i] > averageFilter[i - 1]) then 1 else -1
                filteredY[i] <- influence * input[i] + (1.0 - influence) * filteredY[i - 1]
            else
                signals[i] <- 0
                filteredY[i] <- input[i]

            let slidingWindow =
                filteredY |> Array.skip (i - lag) |> Array.take (lag + 1) |> Array.toList

            averageFilter[i] <- Mean slidingWindow
            stdFilter[i] <- StdDev slidingWindow

        // Finale Ausgabe zurückgeben
        averageFilter, signals, stdFilter

0voto

Zibri Punkte 7918

Angenommen, Ihre Daten stammen von einem Sensor (und der Algorithmus kann daher nichts über die Zukunft wissen),

Ich habe diesen Algorithmus entwickelt, der ziemlich gut mit den Daten funktioniert, die ich in meinem eigenen Projekt bekomme.

Der Algorithmus hat 2 Parameter: Empfindlichkeit und Fenster.

Letztendlich kann eine einzelne Codezeile Ihr Ergebnis liefern:

detected=data.map((a, b, c) => (a > 0) ? c[b] ** 4 * c[b - 1] ** 3 : -0).map((a, b, c) => a > Math.max(...c.slice(2)) / sensitivity).map((a, b, c) => (b > dwindow) && c.slice(b - dwindow, b).indexOf(a) == -1);

Da ich Programmierer bin und kein Mathematiker, kann ich es nicht besser erklären als das. Aber ich bin sicher, dass es jemand kann.

sensitivity = 20;
dwindow = 4;

data = [1., 1., 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., 1., 1., 1.
];
//data = data.concat(data);
//data = data.concat(data);
var data1 = [{
  name: 'original source',
  y: data
}];
Plotly.newPlot('stage1', data1, {
  title: 'Sensor-Daten',
  yaxis: {
    title: 'Signal'
  }
});

filtered = data.map((a, b, c) => (a > 0) ? c[b] ** 4 * c[b - 1] ** 3 : -0);

var data2 = [{
  name: 'gefilterte Quelle',
  y: gefiltert
}];
Plotly.newPlot('stage2', data2, {
  title: 'Gefilterte Datena = a * a³',
  yaxis: {
    title: 'Signal'
  }
});

dwindow = 6;
k = dwindow;
detected = gefiltert.map((a, b, c) => a > Math.max(...c.slice(2)) / sensitivity).map((a, b, c) => (b > k) && c.slice(b - k, b).indexOf(a) == -1)

var data3 = [{
  name: 'erkannte Spitzen',
  y: detected
}];
Plotly.newPlot('stage3', data3, {
  title: 'Fenster 6',
  yaxis: {
    title: 'Signal'
  }
});

dwindow = 10;
k = dwindow;
detected = gefiltert.map((a, b, c) => a > Math.max(...c.slice(2)) / 20).map((a, b, c) => (b > k) && c.slice(b - k, b).indexOf(a) == -1)
var data4 = [{
  name: 'erkannte Spitzen',
  y: detected
}];
Plotly.newPlot('stage4', data4, {
  title: 'Fenster 10',
  yaxis: {
    title: 'Signal'
  }
});

0voto

Zibri Punkte 7918

Alternativ funktioniert auch dieser Algorithmus sehr gut für mich...

sensitivity = 4;
dwindow = 4;
k = dwindow;

data = [1., 1., 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., 1., 1., 1.
];
//data = data.concat(data);
//data = data.concat(data);
var data1 = [{
  name: 'original source',
  y: data
}];
Plotly.newPlot('stage1', data1, {
  title: 'Sensor data',
  yaxis: {
    title: 'signal'
  }
});

filtered = data.map((a,b,c)=>a>=Math.max(...c.slice(b-k,b))?a**3:0);

var data2 = [{
  name: 'filtered source',
  y: filtered
}];
Plotly.newPlot('stage2', data2, {
  title: 'Filterdatena = a³',
  yaxis: {
    title: 'signal'
  }
});

dwindow = 6;
k = dwindow;
detected = filtered.map((a,b,c)=>a>Math.max(...c.slice(2))/sensitivity).map((a,b,c)=>(b>k) && c.slice(b-k,b).indexOf(a)==-1 );

var data3 = [{
  name: 'detected peaks',
  y: detected
}];
Plotly.newPlot('stage3', data3, {
  title: 'Maximum in einem Fenster von 6',
  yaxis: {
    title: 'signal'
  }
});

dwindow = 10;
k = dwindow;
detected = filtered.map((a, b, c) => a > Math.max(...c.slice(2)) / 20).map((a, b, c) => (b > k) && c.slice(b - k, b).indexOf(a) == -1)
var data4 = [{
  name: 'detected peaks',
  y: detected
}];
Plotly.newPlot('stage4', data4, {
  title: 'Maximum in einem Fenster von 10',
  yaxis: {
    title: 'signal'
  }
});

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