Überschreitungen eines Max-Wertes mit Pandas aus DF auslesen

mit matplotlib, NumPy, pandas, SciPy, SymPy und weiteren mathematischen Programmbibliotheken.
Antworten
xx5198
User
Beiträge: 4
Registriert: Montag 24. Februar 2020, 16:40

Hallo zusammen,

zunächst sage ich als Neuling Hallo :)
Ich bin gerade dabei auf Python umzusteigen und komme was das allgemeine angeht auch ganz gut zurecht.
Nun möchte ich etwas "komplexeres" machen, was aber eigentlich nicht so komplex sein dürfte ;)
Denke mir fehlt gerade einfach die Erfahrung um das richtige Werkzeug zu finden.

Folgendes möchte ich machen:
Gegeben ist ein Datafram mit zwei Spalten t = Zeit und V = Value.
Value ist ein schwankender Wert, der mal größer und mal kleiner ist.
Ich möchte nun einen Max-Wert vorgeben bzw. auswerten wie oft / lange dieser überschritten wurde.
Das sollte dann so aussehen:
Überschreitungsdauer
1 s
4s
0,5 s

Wenn ich das mit einer For-Schleife machen würde, dann würde ich einfach t aufsummieren, so lange V > Max.
Aber wie mache ich das mit Pandas? Eine For-Schleife sollte ja nur die letzte Lösung sein aber irgendwie habe ich was Pandas angeht ein Brett vor dem Kopf :)
Also ich weiß wie ich aus meinen Daten Min, Max usw. bestimme aber wie mache ich eine solche Auswertung?
Zum besseren Verständnis habe ich mal eine Skizze hochgeladen.
Die rote Linie soll mein Max-Wert sein.
In orange habe ich die Stellen gekennzeichnet, bei denen die grüne die rote Linie schneidet.
Die erste Überschreitung dauert also 0,5 s und die zweite 1 s.
Bild

Ok scheinbar funktioniert das mit der Skizze aus meiner Dropbox leider nicht :(

Danke schon mal.

Gruß
Andreas
Benutzeravatar
__blackjack__
User
Beiträge: 13925
Registriert: Samstag 2. Juni 2018, 10:21
Wohnort: 127.0.0.1
Kontaktdaten:

Ansatz könnte sein eine Spalte mit Zeitdauern aus den Zeitpunkten zu erstellen, nach Blöcken zu gruppieren in denen der Maxwert überschritten ist und dort dann die Zeitdauern zu addieren.

Bei solchen Fragen hilft es wenn Beispieldaten mitgeliefert werden in einer Form die es erlaubt sie einfach in Pandas zu übernehmen, damit helfende damit experimentieren können.
“Java is a DSL to transform big Xml documents into long exception stack traces.”
— Scott Bellware
xx5198
User
Beiträge: 4
Registriert: Montag 24. Februar 2020, 16:40

Hallo blackjack,

danke für deine Antwort. Das Thema Gruppieren werde ich mir mal ansehen.
Derweil habe ich ein Beispiel gebastelt:

Code: Alles auswählen

import pandas as pd
import matplotlib.pyplot as plt
import numpy as np

fig = plt.figure(figsize=(6.3, 9))
df = pd.DataFrame.from_dict(dict([('X', [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25]),
('Y', [0.0, 0.84, 0.91, 0.84, 0, -0.84, -0.91, -0.84, 0, 0.84, 0.91, 0.84, 0, -0.84, -0.91, -0.84, 0, 0.84, 0.91, 0.84, 0, -0.84, -0.91, -0.84, 0, 0.84])]))
axes = fig.gca()
df.columns = ["X", "Y"]  # Tabellenkopf / Spalten definieren
df.set_index("X", inplace=True)  # Spalte für Index festlegen
df["Max"] = 0.84
df['Cut'] = np.where(df['Y'] >= df["Max"], 1, 0)
df["Y"].plot(label="Y")
df["Max"].plot(label="Max")
df["Cut"].plot(label="Cut")
axes.set_xlim((0, 25))
plt.legend(loc='best')
plt.tight_layout()
plt.show()
Es ist einfach ein sehr kantiker Sinus, den ich als df einlese.
Das Ganze lasse ich mir mit plt dann als Diagramm ausgeben.
Mein Max habe ich in dem beispiel auf 0,84 gesetzt.
"Cut" habe ich mir gebastelt, um die Überschreitung zu visualisieren.
Wo ich jetzt auf dem Schlauch stehe ist wie ich mit Pandas auswerte wie lange die Überschreitung jeweils war.

Ich gucke mir dann erstmal das Thema Gruppieren an :)
Ach ja was mir noch einfällt:
Aktuell bin ich auf Python 3.4.1 beschränkt und verwende daher Pandas 0.21.
Benutzeravatar
kbr
User
Beiträge: 1501
Registriert: Mittwoch 15. Oktober 2008, 09:27

xx5198 hat geschrieben: Dienstag 25. Februar 2020, 10:41 Mein Max habe ich in dem beispiel auf 0,84 gesetzt.
"Cut" habe ich mir gebastelt, um die Überschreitung zu visualisieren.
Wo ich jetzt auf dem Schlauch stehe ist wie ich mit Pandas auswerte wie lange die Überschreitung jeweils war.
Mit 'where' selektierst Du alle Einträge, deren Werte über dem Grenzwert 'Max' liegen. D.h. Du verlierst die Information über alle Einträge, für das nicht der Fall ist, die sich also zeitlich zwischen den selektierten Daten befinden. Angenommen, dass die Messzeitpunkte alle genau äquidistant sind, dann kannst Du testen, ob die zeitliche Differenz zum vorhergehenden Eintrag größer als der äquidistante Wert ist. Wenn das der Fall ist, dann lag ein kleinerer Wert dazwischen. Ob pandas dafür eine brauchbare Funktion bietet, ist mir nicht bekannt. Falls nicht, bist Du vielleicht ohne pandas besser unterwegs.
Benutzeravatar
__blackjack__
User
Beiträge: 13925
Registriert: Samstag 2. Juni 2018, 10:21
Wohnort: 127.0.0.1
Kontaktdaten:

@xx5198: Das Beispiel ist IMHO nicht wirklich gut geeignet, denn da gibt es drei Gruppen die jeweils nur einen einzelnen Wert umfassen und die X-Werte liegen genau 1 auseinander. Also kann man hier auch einfach Zählen wie oft `True` vorkommt und daraus dann eine Liste mit so vielen 1s (wenn das die Zeiteinheit von X ist) erstellen:

Code: Alles auswählen

In [108]: df["Y"] > 0.84                                                        
Out[108]: 
0     False
1     False
2      True
3     False
4     False
5     False
6     False
7     False
8     False
9     False
10     True
11    False
12    False
13    False
14    False
15    False
16    False
17    False
18     True
19    False
20    False
21    False
22    False
23    False
24    False
25    False
Name: Y, dtype: bool

In [109]: (df["Y"] > 0.84).sum()                                                
Out[109]: 3

In [110]: [1] * (df["Y"] > 0.84).sum()                                          
Out[110]: [1, 1, 1]
“Java is a DSL to transform big Xml documents into long exception stack traces.”
— Scott Bellware
Benutzeravatar
kbr
User
Beiträge: 1501
Registriert: Mittwoch 15. Oktober 2008, 09:27

@__blackjack__: Die Frage hatte ich so verstanden, dass in Erfahrung gebracht werden soll, wie oft und wie lange *jeweils* ein Grenzwert überschritten wurde. Das geht mit einer Liste von Einsen nicht. Denn zwei Überschreitungen könnten zeitlich auch aufeinander folgen.
einfachTobi
User
Beiträge: 510
Registriert: Mittwoch 13. November 2019, 08:38

Ich würde wie folgt vorgehen:
  • Werte filtern, die <= Grenzwert sind
  • Werte in Blöcke an Überschreitungen gruppieren
  • Summe über die Zeitdifferenz je Block bzw. Differenz Start des Blocks und Ende des Blocks
In Code kann das dann so aussehen:

Code: Alles auswählen

import pandas as pd

zeiten = range(0, 21)
werte = [0, 0, 1, 1, 2, 2, 3, 4, 5, 6, 6, 6, 7, 7, 2, 2, 8, 8, 9, 9, 0]
df = pd.DataFrame({'Zeit': zeiten, 'Wert': werte})
print(df)
limit = 5
df_over_limit = df[df['Wert'] > limit]
df['Block'] = (df_over_limit['Zeit'].shift(1) != df_over_limit['Zeit'] - 1).astype(int).cumsum()
print('über Limit:\n', df, '\n')
df_groups = df.groupby('Block')
print(df_groups['Zeit'].last() - df_groups['Zeit'].first())
So spielt die Dauer der Überschreitung keine Rolle. Die Anzahl der Gruppen (= Anzahl der Überschreitungen) hast du damit auch.
Benutzeravatar
__blackjack__
User
Beiträge: 13925
Registriert: Samstag 2. Juni 2018, 10:21
Wohnort: 127.0.0.1
Kontaktdaten:

@kbr: Das hatte ich genau so verstanden, aber dann sollten die Beispieldaten halt auch diese Fälle enthalten. Was bringt es eine Lösung zu entwickelt die man an den Beispieldaten gar nicht testen kann, denn da kommt halt [1, 1, 1] heraus, auch wenn man es komplizierter löst.
“Java is a DSL to transform big Xml documents into long exception stack traces.”
— Scott Bellware
xx5198
User
Beiträge: 4
Registriert: Montag 24. Februar 2020, 16:40

Hallo nochmal und danke für die Antworten bzw. einfachTobi für den Code, den ich gerade versuche zu verstehen :D
Generell trifft seine Auffassung genau mein Problem und ich glaube damit komme ich weiter :)
Hätte das Ganze schon fast mit einer Schleife versucht...

Hier trotzdem noch mal ein besseres Beispiel zwecks Nachvollziehbarkeit (werde daran gleich versuchen die Lösung von einfachTobi anzuwenden):

Code: Alles auswählen

import pandas as pd
import matplotlib.pyplot as plt
import numpy as np

fig = plt.figure(figsize=(6.3, 9))
lX = [0,0.1,0.15,0.2,0.25,0.3,0.35,0.4,0.45,0.5,0.55,0.6,0.65,0.7,0.75,0.8,0.85,0.9,0.95,1,1.05,1.1,1.15,1.2,1.25,1.3,1.35,1.4,1.45,1.5,1.55,1.6,1.65,1.7,1.75,1.8,1.85,1.9,1.95,2,2.05,2.1,2.15,2.2,2.25,2.3,2.35,2.4,2.45,2.5,2.55,2.6,2.65,2.7,2.75,2.8,2.85,2.9,2.95,3,3.05,3.1,3.15,3.2]
lY = [0.451449335,0.445298971,0.58236723,0.705562534,0.752749136,0.728327045,0.760341842,0.715389262,0.662813051,0.599453911,0.560436457,0.528878113,0.510773381,0.502182416,0.501010977,0.50391669,0.504927319,0.504970614,0.505991139,0.390216598,0.352479439,0.307733438,0.276758958,0.256014468,0.248942539,0.248942539,0.245960132,0.244241939,0.241910194,0.349183709,0.342846017,0.682889586,0.673279642,0.887448797,1.079941459,1.153670525,1.115511008,1.165534128,1.095295722,1.013145392,0.914146736,0.853181964,0.803872052,0.775583408,0.762160025,0.760329652,0.764869828,0.766448936,0.766516584,0.768111155,0.390216598,0.352479439,0.307733438,0.276758958,0.256014468,0.248942539,0.248942539,0.245960132,0.244241939,0.241910194,0.237877574,0.233821451,0.19725202,0.19543363]
df = pd.DataFrame.from_dict(dict([('X', lX), ('Y', lY)]))
df.columns = ["X", "Y"]  # Tabellenkopf / Spalten definieren
df.set_index("X", inplace=True)  # Spalte für Index festlegen
df["Max"] = 0.6
df['Cut'] = np.where(df['Y'] >= df["Max"], 1, 0)
df["Y"].plot(label="Y")
df["Max"].plot(label="Max")
df["Cut"].plot(label="Cut")
plt.legend(loc='best')
plt.tight_layout()
plt.show()
xx5198
User
Beiträge: 4
Registriert: Montag 24. Februar 2020, 16:40

@ einfachTobi
Vielen Dank noch mal, funktioniert perfekt :)
Musste lediglich noch eine Spalte als Index in Integer hinzufügen, damit auch Schrittweiten von eins abweichend funktioniern.

Ich habe lediglich eine Verständnis-Frage:
Der Output ist perfekt, ich würde jedoch gerne die Überschriften anpassen, der Output bleibt aber bei "Block" bzw. die Dauer der Überschreitung hat keine Überschrift.
Hast du eine Idee was ich falsch mache? Bin da wohl noch zu sehr Anfänger :D

Danke noch mal.

Code: Alles auswählen

import pandas as pd

limit = 0.6
zeiten = [0,0.1,0.15,0.2,0.25,0.3,0.35,0.4,0.45,0.5,0.55,0.6,0.65,0.7,0.75,0.8,0.85,0.9,0.95,1,1.05,1.1,1.15,1.2,1.25,1.3,1.35,1.4,1.45,1.5,1.55,1.6,1.65,1.7,1.75,1.8,1.85,1.9,1.95,2,2.05,2.1,2.15,2.2,2.25,2.3,2.35,2.4,2.45,2.5,2.55,2.6,2.65,2.7,2.75,2.8,2.85,2.9,2.95,3,3.05,3.1,3.15,3.2]
werte = [0.451449335,0.445298971,0.58236723,0.705562534,0.752749136,0.728327045,0.760341842,0.715389262,0.662813051,0.599453911,0.560436457,0.528878113,0.510773381,0.502182416,0.501010977,0.50391669,0.504927319,0.504970614,0.505991139,0.390216598,0.352479439,0.307733438,0.276758958,0.256014468,0.248942539,0.248942539,0.245960132,0.244241939,0.241910194,0.349183709,0.342846017,0.682889586,0.673279642,0.887448797,1.079941459,1.153670525,1.115511008,1.165534128,1.095295722,1.013145392,0.914146736,0.853181964,0.803872052,0.775583408,0.762160025,0.760329652,0.764869828,0.766448936,0.766516584,0.768111155,0.390216598,0.352479439,0.307733438,0.276758958,0.256014468,0.248942539,0.248942539,0.245960132,0.244241939,0.241910194,0.237877574,0.233821451,0.19725202,0.19543363]
df = pd.DataFrame({'Zeit': zeiten, 'Wert': werte})
df['Index'] = df.index  # Index mit in den df aufnehmen
df_over_limit = df[df['Wert'] >= limit]  # Nur Werte, die den Grenzwert reißen
df['Block'] = (df_over_limit['Index'].shift(1) != df_over_limit['Index'] - 1).astype(int).cumsum()  # Überschreitungen nummerieren
df_groups = df.groupby('Block')  # In Blöcke gruppieren
dfNeu = df_groups['Zeit'].last() - df_groups['Zeit'].first()  # Überschreitungen mit Zeit auflisten
dfNeu.columns = ["Überschreitung", "Dauer"]
print(dfNeu)
einfachTobi
User
Beiträge: 510
Registriert: Mittwoch 13. November 2019, 08:38

Mit `dfNeu`deutest du an, dass es sich um ein DataFrame handeln würde. Das ist nicht der Fall. Es handelt sich um eine pandas.Series. Diese hat das Attribut `name`, welches du ändern kannst, jedoch keinen "Titel".
Antworten