pandas scatter grafik gefiltert

Wenn du dir nicht sicher bist, in welchem der anderen Foren du die Frage stellen sollst, dann bist du hier im Forum für allgemeine Fragen sicher richtig.
Antworten
franze_m
User
Beiträge: 11
Registriert: Donnerstag 9. März 2023, 20:27

Hallo,
ich bekommen eine gefilterte Grafik mit pandas nicht hin. Ich habe eine sehr grosse CSV-Datei meiner Wärmepumpe und möchte in einer Grafik nur die Werte der Kollektortemperatur anzeigen, wenn die Wärmepumpe eine Zeit lang läuft, denn der Programmierer der Wärmepumpe hat Mist gebaut. Der Fühler zeigt teilweise über 30 Grad an, da müsste mein Erdkollektor ja kochen. Das liegt daran, das der Fühler im innern der Wärmepumpe eingebaut ist und die richtige Temperatur nur angezeigt wird, wenn die Wärmepumpe eine zeitlang das Wasser vonaussen umgepumpt hat.

Ich möchte also vom Dataframe ein Plot erzeugen, bei dem alle Werte der Temp_EQ_Eintritt nur dann verwendet werden, wenn Verdichter_laeuft_seit >800 sec ist. Ich habe schon alles mögliche versucht komme aber nicht zurecht.
Sourcecode:
#!/bin/python3
#
# Examples in https://www.w3schools.com/python/pandas
import matplotlib
import matplotlib.pyplot as plt
import numpy as np
import datetime
import pandas as pd
import matplotlib.pyplot as plt
file="/home/t759f/wp-werte.csv"
def get_num_lines(fname):
with open(fname, "r") as f:
for i, _ in enumerate(f):
pass
return i + 1

num_lines = get_num_lines(file)
#print(num_lines)
# Interval 5 minutes
#n = 12 # print last x lines (1 Hour)
#n = 288 # print last x lines (1 Day)
#n = 2016 # print last x lines (1 Week)
#n = 62496 # print last x lines (1 Month)
#n = num_lines+1
#df=pd.read_csv(file, header=0, sep=';',
# usecols= ['DateTime','Temp_EQ_Eintritt'], skiprows=range(1,num_lines-n))
df=pd.read_csv(file, header=0, sep=';',usecols= ['DateTime', 'Verdichter_laeuft_seit', 'Temp_EQ_Eintritt'],dtype={'Verdichter_laeuft_seit': 'float','Temp_EQ_Eintritt': 'float'})
#dfx=pd.read_csv(file, header=0, sep=';')
# usecols= ['DateTime','Verdichter_laeuft_seit','Temp_EQ_Eintritt'])
#print(dfx.columns.values.tolist())
mask = df['Verdichter_laeuft_seit'].values > 800
# getting non zero indices
pos = np.flatnonzero(mask)
print("\nRows selected :", pos)
df.iloc[pos]
#df = dfx.query('Verdichter_laeuft_seit > 800')
#df = dfx[dfx['Verdichter_laeuft_seit'] > float(800)]
df.DateTime=pd.to_datetime(df['DateTime'])

df.Temp_EQ_Eintritt = pd.to_numeric(df['Temp_EQ_Eintritt'],errors = 'coerce')
#df.info()
#df.head(3)
#
#
plt.scatter(df.DateTime, df[mask] ,s=1)
#plt.scatter(df.DateTime, df.Temp_EQ_Eintritt,s=1)
plt.xticks(rotation=30, ha='right')
plt.title('WP Temp_EQ_Eintritt Werte ')
plt.legend(["Temp_EQ_Eintritt C"])

plt.subplots_adjust(bottom=0.3,left=0.08,right=0.917,top=0.9)
plt.show()

#plt.savefig("/var/www/http/home.bb-24.net/Native/homepage/Privat/WP/wp-Temp_EQ_Eintritt.png", dpi=600)


Fehler:
Rows selected : [29127 29128 29129 ... 60508 60509 60510]
Traceback (most recent call last):
File "/home/t759f/PycharmProjects/graph/venv/lib/python3.9/site-packages/matplotlib/axis.py", line 1769, in convert_units
ret = self.converter.convert(x, self.units, self)
File "/home/t759f/PycharmProjects/graph/venv/lib/python3.9/site-packages/matplotlib/dates.py", line 1888, in convert
return self._get_converter().convert(*args, **kwargs)
File "/home/t759f/PycharmProjects/graph/venv/lib/python3.9/site-packages/matplotlib/dates.py", line 1816, in convert
return date2num(value)
File "/home/t759f/PycharmProjects/graph/venv/lib/python3.9/site-packages/matplotlib/dates.py", line 461, in date2num
d = d.astype('datetime64[us]')
ValueError: Could not convert object to NumPy datetime

The above exception was the direct cause of the following exception:

Traceback (most recent call last):
File "/home/t759f/PycharmProjects/graph/main.py", line 45, in <module>
plt.scatter(df.DateTime, df[mask] ,s=1)
File "/home/t759f/PycharmProjects/graph/venv/lib/python3.9/site-packages/matplotlib/pyplot.py", line 3684, in scatter
__ret = gca().scatter(
File "/home/t759f/PycharmProjects/graph/venv/lib/python3.9/site-packages/matplotlib/__init__.py", line 1465, in inner
return func(ax, *map(sanitize_sequence, args), **kwargs)
File "/home/t759f/PycharmProjects/graph/venv/lib/python3.9/site-packages/matplotlib/axes/_axes.py", line 4646, in scatter
x, y = self._process_unit_info([("x", x), ("y", y)], kwargs)
File "/home/t759f/PycharmProjects/graph/venv/lib/python3.9/site-packages/matplotlib/axes/_base.py", line 2573, in _process_unit_info
return [axis_map[axis_name].convert_units(data)
File "/home/t759f/PycharmProjects/graph/venv/lib/python3.9/site-packages/matplotlib/axes/_base.py", line 2573, in <listcomp>
return [axis_map[axis_name].convert_units(data)
File "/home/t759f/PycharmProjects/graph/venv/lib/python3.9/site-packages/matplotlib/axis.py", line 1771, in convert_units
raise munits.ConversionError('Failed to convert value(s) to axis '
matplotlib.units.ConversionError: Failed to convert value(s) to axis units: DateTime Verdichter_laeuft_seit Temp_EQ_Eintritt
29127 2023-08-21 15:55:00 1799.0 16.7
29128 2023-08-21 16:00:00 1604.0 34.6
29129 2023-08-21 16:05:00 1304.0 35.3
29130 2023-08-21 16:10:00 1003.0 36.6
29134 2023-08-21 16:30:00 1799.0 17.5
... ... ... ...
60506 2023-12-09 13:55:00 3007.0 6.7
60507 2023-12-09 14:00:00 3307.0 6.7
60508 2023-12-09 14:05:00 3607.0 6.7
60509 2023-12-09 14:10:00 3907.0 6.6
60510 2023-12-09 14:15:00 4207.0 6.6

[3914 rows x 3 columns]

Process finished with exit code 1


Unmaskiert läuft es, ist aber nicht hilfreich in der Aussage des Plots.

Vielen Dank (ich werde in diesem Leben wohl nicht mehr mit der objektorientieren Programmierung warm)
__deets__
User
Beiträge: 14545
Registriert: Mittwoch 14. Oktober 2015, 14:29

Das du mit OO nicht warm wirst, mag ja sein. Doch langsam aber sicher darfst auch du deinen Code in die dafuer vorgesehenen Code-Tags packen, damit die hilfsbereiten Leute hier sich die Einrueckung nicht irgendwie verschaffen muessen. Was je nach Medium (zB mobilen Geraeten) auch einfach nervig und schwierig ist.
Benutzeravatar
__blackjack__
User
Beiträge: 13154
Registriert: Samstag 2. Juni 2018, 10:21
Wohnort: 127.0.0.1
Kontaktdaten:

@franze_m: Das ist alles sehr unübersichtlich, angefangen mit den Importen wo alles durcheinander und teilweise mehrfach und auch gar nicht alles gebraucht wird.

Leerzeichensetzung scheint auch so per Zufall gemacht zu sein. Üblich ist um ``=`` bei Zuweisungen ausserhalb von Argumentlisten, um binäre Operatoren, und nach Kommas Leerzeichen zu setzen.

Der ganze auskommentierte Code hilft auch nicht das übersichtlicher zu machen. Und ein paar strategisch platzierte Leerzeilen würden auch helfen, beispielsweise die definition von `file` zwischen den Importen und der Funktionsdefinition zu sehen.

`datetime` und `matplotlib` werden importiert, aber nirgends verwendet.

`file` ist keine Datei sondern ein Datei*name*. Bei etwas das `file` heisst, erwartet der Leser keine Zeichenkette, sondern ein Objekt das eine `read()`- oder `write()`-Methode und eine `close()`-Methode besitzt.

`get_num_lines()` hiesse besser `get_line_count()` und ist fehlerhaft. Bei leeren Dateien wird das mit einer Ausnahme auf die Nase fallen, weil `i` dann undefiniert ist bei der ``return``-Anweisung. Die Funktion lässt sich mit `sum()` viel einfacher ausdrücken:

Code: Alles auswählen

def get_line_count(filename):
    with open(filename, "r", encoding="utf-8") as lines:
        return sum(1 for line in lines)
Aber die Funktion ist überflüssig, denn sie wird zwar aufgerufen, aber das Ergebnis von dem Aufruf wird nicht verwendet.

`pos` wird definiert aber nirgends wirklich verwendet, womit dann auch der Import von Numpy hinfällig ist.

Dann könnte man die Zeilen so sortieren das erst das Einlesen und umwandeln komplett durchläuft bevor man sich ans Filtern und Weiterverarbeiten macht.

Der `pd.to_numeric()` macht keinen Sinn. Wenn in der Spalte in der Datei etwas gestanden hätte was nicht in eine Gleitkommazahl umwandelbar ist, dann wäre das Einlesen schon mit einer Ausnahme ausgestiegen.

Das parsen der Zeitstempel sollte man auch schon beim Einlesen machen.

Damit wären wir bei diesem Code angekommen:

Code: Alles auswählen

#!/bin/env python3
import matplotlib.pyplot as plt
import pandas as pd

CSV_FILENAME = "/home/t759f/wp-werte.csv"


def main():
    df = pd.read_csv(
        CSV_FILENAME,
        header=0,
        sep=";",
        usecols=["DateTime", "Verdichter_laeuft_seit", "Temp_EQ_Eintritt"],
        parse_dates=["DateTime"],
        dtype={"Verdichter_laeuft_seit": "float", "Temp_EQ_Eintritt": "float"},
    )
    df["DateTime"] = pd.to_datetime(df["DateTime"])

    mask = df["Verdichter_laeuft_seit"] > 800

    plt.scatter(df["DateTime"], df[mask], s=1)
    plt.xticks(rotation=30, ha="right")
    plt.title("WP Temp_EQ_Eintritt Werte ")
    plt.legend(["Temp_EQ_Eintritt C"])

    plt.subplots_adjust(bottom=0.3, left=0.08, right=0.917, top=0.9)
    plt.show()


if __name__ == "__main__":
    main()
Ich sehe nicht so wirklich was hier das Problem mit OO sein soll, denn das ist ja alles ziemlich prozedural ausgedrückt und dazu noch ohne irgendwelche Entscheidungen oder Schleifen, also selbst für prozeduralen Code sehr simpel.

Man muss sich halt klarmachen was die einzelnen Operationen als Ergebnis haben, beziehungsweise was sie als Eingabedaten erwarten. `scatter()` möchte X-Werte und Y-Werte. Und davon jeweils gleich viele, weil ja immer zwei Werte einen Punkt beschreiben. Als X-Werte werden die ganzen Zeitstempel übergeben. Und als Y-Werte eine zweidimensionale Tabelle mit drei Spalten. Das kann nicht funktionieren. Entweder du wählst aus dieser Tabelle dann noch die Spalte aus, oder Du wählst vorher aus `df` die Spalte und selektierst von der Spalte mit der Maske, statt von der ganzen Tabelle.

Und dann kommt das Problem das X- und Y-Werte die gleiche Anzahl haben müssen. Wenn aus den Temperaturen welche rausgefiltert werden mit `mask`, dann gibt es mehr Zeitstempel als Temperaturwerte. Man muss also die Maske auch dort anwenden.
“There will always be things we wish to say in our programs that in all known languages can only be said poorly.” — Alan J. Perlis
franze_m
User
Beiträge: 11
Registriert: Donnerstag 9. März 2023, 20:27

Danke blackjack User,
ich habe deinen Vorschlag umgesetzt und zusätzlich mask durch df.drop(df[df['Verdichter_laeuft_seit'] < 800].index, inplace=True)
ersetzt, nun läuft es.
Ich wünsche noch ein frohes neues Jahr.
Sirius3
User
Beiträge: 17778
Registriert: Sonntag 21. Oktober 2012, 17:20

Kann man machen, sollte man aber nicht.
Warum nicht einfach:

Code: Alles auswählen

mask = df['Verdichter_laeuft_seit'] > 800
df_filtered = df[mask]
Antworten