Ausreißer eliminieren

mit matplotlib, NumPy, pandas, SciPy, SymPy und weiteren mathematischen Programmbibliotheken.
Antworten
logo
User
Beiträge: 9
Registriert: Freitag 25. September 2015, 19:54

Hallo!

Ich habe eine Messkurve, welche über einzelne, scharfe Peaks (Ausreißer) verfügt. Nun suche ich einen Weg um nur diese Ausreißer aus meinen Daten zu löschen. Mein Numpy-Array hat ca. 10^6 Zeilen und 3 Spalten. Nun habe ich mir überlegt, mit einer Schleife die einzelnen Werte durchzugehen und die Abweichung des Wertes in der Zeile n+1 zu dem in Zeile n zu berechnen. Übersteigt die Abweichung einen gewissen Wert, wird Zeile n+1 gelöscht.

In einem kleinen Vorabtest hat das auch funktioniert, nur mit den realen Daten nicht. Ich muss die Grenze so groß wählen, dass alle Werte erhalten bleiben. Aus irgendeinem Grund hängt sich mein Python auch auf, wenn mehrere Zeilen nacheinander gelöscht werden. Hier mal mein Code und die Bitte um Hilfe :)

Code: Alles auswählen

i=0
while i < len(A)-1:
        if abs(1-(A[i,1]/A[(i+1),1])) > 0.2:                                                                     
                    A = np.delete(A,(i+1),axis=0)
                    continue
        i += 1
Ich sollte noch erwähnen, dass meine Messdaten Stufen beinhalten, welche aber nicht unstetig sind. Es kann aber dennoch sei, dass eine Stufe die Grenze (und damit das Löschen) auslöst. Da alle Werte nach der Stufe kleiner sind, bleibt auch die Grenze aktiv und es wird ab diesem Punkt alles gelöscht...

Danke für eure Hilfe!

Ps: Entschuldigt bitte, dass ich es nicht genauer darlegen kann. Die Messdaten kann ich nicht veröffentlichen und ein Beispiel, welches das Problem reproduziert, konnte ich nicht basteln. Wenn also etwas unklar ist, bitte fragen :)
BlackJack

@logo: Was immer Du machst: Versuche keine Python-Schleife zu verwenden und wiederholt Zeilen aus einem grossen Array löschen ist auch keine gute Idee weil dafür immer das Array umkopiert wird. Erstelle zum Beispiel ein Array mit Wahrheitswerten und verwende das als Index in A (ungetestet):

Code: Alles auswählen

A = A[np.abs(1 - (A[:-1,1] / A[1:,1])) <= 0.2]
Benutzeravatar
MagBen
User
Beiträge: 799
Registriert: Freitag 6. Juni 2014, 05:56
Wohnort: Bremen
Kontaktdaten:

Besser so

Code: Alles auswählen

import numpy as np
invalidElements = np.zeros((A.shape[0],), dtype=np.bool)
invalidElements[1:] = np.abs(1-(A[:-1,1]/A[1:,1])) > 0.2
A = A[np.logical_not(invalidElements)]
A[:-1,1]/A[1:,1]) ist um eins kürzer als A.shape[0]
Damit wird der Algorithmus allerdings verändert, so werden nur einzelne Ausreißer gefunden, der ursprüngliche Algorithmus findet wegen dem continue auch zwei Ausreißer hintereinander.
a fool with a tool is still a fool, www.magben.de, YouTube
logo
User
Beiträge: 9
Registriert: Freitag 25. September 2015, 19:54

Danke euch beiden!

Mir war schon klar, dass mein Code weit von elegant entfernt ist ;) Hauptsache das Ergebnis stimmt. Ich werde mir eure Vorschläge aber ansehen und versuchen diese zu implementieren.

Aber mein Hauptproblem habe ich wohl nicht so recht auf den Punkt gebracht: Der Algorithmus von mir funktioniert nicht so wirklich, da ich kein sinnvolle Grenze finde (die 0.2 waren einfach ein Beispiel). Entweder ich muss die Grenze so grob wählen, dass alle Werte erhalten bleiben, auch die Ausreißer, oder der Code fängt nach einmaligem Auslösen der Grenze an, alle folgenden Zeilen zu löschen.

Die Kurve sieht etwa so aus:

Code: Alles auswählen

______
      \
       \ _____________
                      \_______
Daher meine Frage, ob es da eine allgemeine Herangehensweise gibt?
Sirius3
User
Beiträge: 17747
Registriert: Sonntag 21. Oktober 2012, 17:20

@logo: Du hast also gewollte Stufen und ungewollte Spitzen. BlackJacks Methode löscht da immer den ersten Wert einer Stufe, weil er immer mit dem vorherigen Wert vergleicht, Du löscht alles, weil Du mit dem nicht gelöschten Vorgängerwert vergleichst. Bei Deinem Fall könnte ein Median-Filter gute Ergebnisse liefern, oder ein Wiener Filter

Code: Alles auswählen

from scipy.signal import medfilt, wiener
A=medfilt(A)
A=wiener(A)
Benutzeravatar
MagBen
User
Beiträge: 799
Registriert: Freitag 6. Juni 2014, 05:56
Wohnort: Bremen
Kontaktdaten:

Ich würde die Daten in mehrere Bereiche aufteilen

Code: Alles auswählen

 1      |  2   |  3               |  4  |   5
______  |      |                  |     |  
        |  \   |                  |     |  
        |   \  |   _____________  |     |  
        |      |                  |  \  |  _______
und dann durch jeden Bereich eine Regressionsgerade legen und damit wie hier beschrieben
stackoverflow.com/questions/10231206/can-scipy-stats-identify-and-mask-obvious-outliers
die Ausreißer eliminieren.

Die Aufteilung in mehrere Bereiche kannst Du mit Hilfe von numpy.diff machen, wobei da die Ausreißer natürlich auch stören.
a fool with a tool is still a fool, www.magben.de, YouTube
logo
User
Beiträge: 9
Registriert: Freitag 25. September 2015, 19:54

Sirius3 hat geschrieben:... Du löscht alles, weil Du mit dem nicht gelöschten Vorgängerwert vergleichst....
Das ist mir gestern Abend dann auch aufgefallen :)

@ magben:

Habe ich mir auch überlegt, bin mir aber noch nicht so richtig sicher.


Danke euch allen, ich werde die Methoden mal durchtesten!
logo
User
Beiträge: 9
Registriert: Freitag 25. September 2015, 19:54

Nur als kurze Rückmeldung:

Ich habe den Code von magben verwendet und es funktioniert soweit. Der "Nachteil", dass zwei aufeinanderfolgende Ausreißer nicht gefunden werden hat sich als Vorteil herausgestellt. Da die Stufen dann keine "Dauerlöschung" verursachen :)

Zwar werden bei dieser Methode jeweils zwei Punkte gelöscht und nicht nur der Ausreißer, was aber bei meiner Datenmenge nicht ins Gewicht fällt. Schöner Nebeneffekt: Macht man die Grenze sehr klein, werden die Daten auch ausgedünnt, ohne die Kurve zu verfälschen.

Vielen Dank!
Antworten