Seite 1 von 1

Mit Pandas 5 CSV zu einem mergen aber nur eine Datumsspalte

Verfasst: Freitag 26. Mai 2023, 07:01
von Homer-S
Hallo zusammen,

ich habe ein script, was mir Heizungsdaten ausliest, und die einzelnen Werte dann in 5 CSV Dateien speichert.
Diese 5 CSV Dateien wurden dann in eine zusammengefasst.

Alle Dateien haben dieses Format:

Code: Alles auswählen

,time,state
0,1682914500000,5.388939393939403
1,1682915400000,5.591777777777786
2,1682916300000,6.0627777777777725
3,1682917200000,6.3917777777777705
4,1682918100000,6.80044444444444
Zielbild war bisher:

Code: Alles auswählen

2023-03-31 22:12:00;7,607142857142857;0,8;63650,600000000006;11213,3;2348,706153846154
2023-03-31 22:26:00;7,7;0,8;63651,535714285725;11213,3;2348,830769230769
2023-03-31 22:40:00;7,776923076923075;0,8;63652,43076923077;11213,3;2348,9516666666664

Mit dieser Zeile konnte ich die 5 CSV zu einer "mergen". Das lief über Jahre hinweg gut. Ich fürchte ein Pandas Update ist die Ursache. Da bekomme ich jetzt immer einen Fehler:

Code: Alles auswählen

merged = pandas.read_csv(all_filenames[0], index_col=[0], parse_dates=[0]).merge(pandas.read_csv(all_filenames[1], index_col=[0], parse_dates=[0]).merge(pandas.read_csv(all_filenames[2], index_col=[0], parse_dates=[0]).merge(pandas.read_csv(all_filenames[3], index_col=[0], parse_dates=[0]).merge(pandas.read_csv(all_filenames[4], index_col=[0], parse_dates=[0]), on='time'), on='time'), on='time'), on='time')
FEHLERMELDUNG:

Code: Alles auswählen

/home/homer/heizungsloggen/heizungsdatenloggen.py:81: UserWarning: Could not infer format, so each element will be parsed individually, falling back to `dateutil`. To ensure parsing is consistent and as-expected, please specify a format.
  merged = pandas.read_csv(all_filenames[0], index_col=[0], parse_dates=[0]).merge(pandas.read_csv(all_filenames[1], index_col=[0], parse_dates=[0]).merge(pandas.read_csv(all_filenames[2], index_col=[0], parse_dates=[0]).merge(pandas.read_csv(all_filenames[3], index_col=[0], parse_dates=[0]).merge(pandas.read_csv(all_filenames[4], index_col=[0], parse_dates=[0]), on='time'), on='time'), on='time'), on='time')
Ich habe (glaube ich) raus gefunden, dass es am parse_dates=[0] liegt.
Wenn ich es auf True setze, einfach weg lasse, bekomme ich aber nicht das was ich möchte.
Ich habe es dann noch mit concat versucht, da bekomme ich auch alle 5 vereint, aber es wird jedesmal die Datumszeile mit genommen. Also sieht die Struktur des CSV dann so aus:

Code: Alles auswählen

time,value,time,value,time,value,time,value,time,value

Code: Alles auswählen

    df0 = pandas.read_csv(all_filenames[0], index_col=[0])
    df1 = pandas.read_csv(all_filenames[1], index_col=[0])
    df2 = pandas.read_csv(all_filenames[2], index_col=[0])
    df3 = pandas.read_csv(all_filenames[3], index_col=[0])
    df4 = pandas.read_csv(all_filenames[4], index_col=[0])
    merged = pandas.concat([df0, df1, df2, df3, df4], axis=1, join='inner').sort_index()
Da stocke ich nun seit 2 Tagen und komme nicht mehr weiter.

Re: Mit Pandas 5 CSV zu einem mergen aber nur eine Datumsspalte

Verfasst: Freitag 26. Mai 2023, 07:33
von grubenfox
Wenn die Beispieldaten echte Daten sind, dann aus der vierten Zeile:

Code: Alles auswählen

>>> time.ctime(1682918100000)
'Mon Jul 20 20:00:00 55299'
und wenn man ein paar Nullen (genau 3) streicht:

Code: Alles auswählen

>>> time.ctime(1682918100)
'Mon May  1 07:15:00 2023'
offenbar haben sich entweder die Daten von Sekunden nach Millisekunden geändert oder der Konverter hält den gegebenen Wert nicht mehr für Millisekunden, sondern jetzt für Sekunden....

Re: Mit Pandas 5 CSV zu einem mergen aber nur eine Datumsspalte

Verfasst: Freitag 26. Mai 2023, 09:54
von Homer-S
Die time Spalte wandel ich nach dem mergen noch um, damit man die lesen kann.
Das ist aber gerade nicht das Problem, das funktioniert noch.

Code: Alles auswählen

    #epochdatum in lesbares wandeln
    merged.iloc[:,0] = pandas.to_datetime((merged.iloc[:,0].values*1000000).astype(int))
    merged.to_csv(SAVE_PATH+str(gestern)+"_HeizungGesamt.csv", sep=';', header=False, index=False, decimal=',')

Re: Mit Pandas 5 CSV zu einem mergen aber nur eine Datumsspalte

Verfasst: Freitag 26. Mai 2023, 11:10
von __blackjack__
@Homer-S: Und wenn das aber doch das Problem ist? Da sollten nach dem einlesen der einzelnen Dateien und vor dem Mergen schon die korrekten Werte stehen. Es geht hier nicht um ”lesbar” sondern ob die korrekt interpretiert werden.

Die Beispieldaten passen nicht zum Code. Spaltenindex 0 ist ja nicht die Zeitangabe sondern anscheinend eine fortlaufende Nummer, also ist ``parse_dates=[0]`` falsch.

Re: Mit Pandas 5 CSV zu einem mergen aber nur eine Datumsspalte

Verfasst: Freitag 26. Mai 2023, 13:46
von Homer-S
Ich kann verstehen, dass die Datenbeispiel vielleicht widersprüchlich sind, weil es aus alten Dateien und neuen gemischt war zur Darstellung. Deshalb versuch ich es noch mal besser zu erklären.

Ich lese 5 csv Dateien aus.
die sehen alle so aus (natürlich mit mehr Zeilen):

Code: Alles auswählen

,time,state
0,1682914500000,5.388939393939403
So sollte es aussehen, nach dem das merge Kommando gelaufen ist (so war es bis her auch immer)
"Datum, Wert, Wert, Wert, Wert, Wert"

Code: Alles auswählen

1682914500000,5.388939393939403,irgendeine andere Zahl,irgendeine andere Zahl,irgendeine andere Zahl,irgendeine andere Zahl,
danach hab ich jedesmal das epoch-Datum geändert und die Werte waren vernünftig. Es sind, wie geschrieben, die täglichen Werte der Heizung.

Im ersten Schritt wäre es wichtig, die Zielstruktur wieder hin zu bekommen, also "Datum, Wert, Wert, Wert, Wert, Wert" und nicht wie nach dem concat "Datum, Wert, Datum, Wert, Datum, Wert, Datum, Wert, Datum, Wert"
Danke

Re: Mit Pandas 5 CSV zu einem mergen aber nur eine Datumsspalte

Verfasst: Freitag 26. Mai 2023, 14:00
von __blackjack__
@Homer-S: Es bleibt dabei das das Problem in der Datumsspalte in den Werten liegt und dem Unsinn das erst in einen falschen Datumswert zu konvertieren. Mach das am Anfang schon richtig und merge dann, oder lass den Zeitstempel in Millisekunden und wandle das nach dem mergen in den entsprechenden Datentyp.

Wobei Du soweit ich das sehe auch noch gar nicht das wirkliche Problem gezeigt hast. Du schreibst zwar „FEHLERMELDUNG“ komplett in Grossbuchstaben, aber das ist ja nur eine Warnung. Sind denn die Daten danach tatsächlich falsch? Und falls ja, wie sehen die Fehler denn konkret aus?

Re: Mit Pandas 5 CSV zu einem mergen aber nur eine Datumsspalte

Verfasst: Montag 29. Mai 2023, 15:08
von Homer-S
@__blackjack__
Ich verstehe ja, dass das Zeitdatum ein Problem ist, aber das bekomme ich vielleicht auch alleine hin, das andere nicht.

Wie geschrieben, ob Warnung oder Fehler, die Daten kommen schon richtig an, aber leider nicht in der richtigen Struktur, oder wie man das immer nennen möchte.

Besser als im vorherigen Post kann ich es leiser nicht erklären. Ich brauch nur einmal den Zeitstempel und dahinter dann die Werte. Danke

Re: Mit Pandas 5 CSV zu einem mergen aber nur eine Datumsspalte

Verfasst: Montag 29. Mai 2023, 16:18
von __blackjack__
@Homer-S: Ich sehe nicht das Du überhaupt irgendwo erklärt hast wie sich das Problem bei `merge()` äussert. Und der gezeigte Code passt nicht zu den Daten, weil in Spalte 0 dort nicht das Datum, sondern ein fortlaufender Index ist. Und das falsche Ergebnis das Du beschreibst kommt von `concat()`. Das ist ja auch das falsche Werkzeug hier.

Re: Mit Pandas 5 CSV zu einem mergen aber nur eine Datumsspalte

Verfasst: Dienstag 30. Mai 2023, 22:07
von Homer-S
__blackjack__ hat geschrieben: Montag 29. Mai 2023, 16:18 @Homer-S: Ich sehe nicht das Du überhaupt irgendwo erklärt hast wie sich das Problem bei `merge()` äussert.
Das merge Kommando, was mal funktioniert hat, hatte ich ja schon gezeigt:

Code: Alles auswählen

 merged = pandas.read_csv(all_filenames[0], index_col=[0], parse_dates=[0]).merge(pandas.read_csv(all_filenames[1], index_col=[0], parse_dates=[0]).merge(pandas.read_csv(all_filenames[2], index_col=[0], parse_dates=[0]).merge(pandas.read_csv(all_filenames[3], index_col=[0], parse_dates=[0]).merge(pandas.read_csv(all_filenames[4], index_col=[0], parse_dates=[0]), on='time'), on='time'), on='time'), on='time')  
Dazu auch die Warnmeldung:

Code: Alles auswählen

/home/homer/heizungsloggen/heizungsdatenloggen.py:81: UserWarning: Could not infer format, so each element will be parsed individually, falling back to `dateutil`. To ensure parsing is consistent and as-expected, please specify a format.
  merged = pandas.read_csv(all_filenames[0], index_col=[0], parse_dates=[0]).merge(pandas.read_csv(all_filenames[1], index_col=[0], parse_dates=[0]).merge(pandas.read_csv(all_filenames[2], index_col=[0], parse_dates=[0]).merge(pandas.read_csv(all_filenames[3], index_col=[0], parse_dates=[0]).merge(pandas.read_csv(all_filenames[4], index_col=[0], parse_dates=[0]), on='time'), on='time'), on='time'), on='time')
Mehr kann ich dir nicht zeigen, weil leider keine gemergte Datei angezeigt/angelegt wird.

Re: Mit Pandas 5 CSV zu einem mergen aber nur eine Datumsspalte

Verfasst: Dienstag 30. Mai 2023, 22:20
von __blackjack__
@Homer-S: Aber es gibt da doch `merged` das einen Wert hat. Welcher ist das denn?

Und gibt doch mal ein nachvollziehbares Beispiel. Denn wenn ich Deine Beispiel-CSV-Daten in einer Datei speichere und das dann mit Pandas 2.0.2 (aktuellste Version) lade, dann bekomme ich die Warnung nicht. Kann es sein, dass sich in den Datendateien etwas geändert hat? Und ``parse_dates=[0]`` ist auf jeden Fall unsinnig, denn in Spalte 0 der Ausgangsdaten befindet sich wie gesagt gar kein Datum.

Code: Alles auswählen

In [1]: import pandas as pd

In [2]: pd.read_csv("test.csv", index_col=[0], parse_dates=[0])
Out[2]: 
            time     state
0  1682914500000  5.388939
Edit: Beim `merge()` fehlt auch das `how`-Argument, denn beim Defaultwert "inner" hast Du am Ende nur Zeilen deren Zeitwert in allen fünf Dateien vorkommt. Sollte ein Zeitwert nur in einer Datei fehlen, gehen die Daten aus allen vier anderen Dateien für diesen Zeitwert verloren.

Re: Mit Pandas 5 CSV zu einem mergen aber nur eine Datumsspalte

Verfasst: Mittwoch 31. Mai 2023, 00:23
von Homer-S
Hab es raus,
so gehts:

Code: Alles auswählen

def merge_csv(all_filenames):
    #print("START merge_csv")
    #combine all files in the list
    merged_data = pd.DataFrame()
    for i, filename in enumerate(all_filenames):
        data = pd.read_csv(filename)
        if i == 0:
            merged_data['time'] = pd.to_datetime(data['time'], unit='ms')
        merged_data = pd.concat([merged_data, data['state']], axis=1)
    merged_data.set_index('time', inplace=True)
    merged_data.sort_index(inplace=True)
    # Save the merged data to a new CSV file
    merged_data.to_csv(SAVE_PATH + str(gestern) + "_HeizungGesamt.csv", sep=";", decimal=',', header=False)

so siehts dann aus:
Merged data:
state state state state state
time
2023-05-01 04:15:00 5.388939 2.145846 65155.0 11507.5 2671.29
2023-05-01 04:30:00 5.591778 2.100000 65155.8 11507.5 2671.43
2023-05-01 04:45:00 6.062778 2.184000 65156.3 11507.5 2671.51

Re: Mit Pandas 5 CSV zu einem mergen aber nur eine Datumsspalte

Verfasst: Mittwoch 31. Mai 2023, 12:47
von __blackjack__
@Homer-S: Naja, nicht wirklich. Das setzt voraus das alle Dateien gleich lang sind, und das *ohne das zu prüfen* die "time"-Spalte bei allen die gleiche Werte haben, denn es wird ja nur die von der ersten Datei verwendet.

`merged_data` ist demnach auch falsch als Name, denn es wird ja gar nicht ge-merged, sondern einfach blind konkateniert.

`concat()` mehrfach aufrufen ist ineffizient, dem kann man doch eine Liste mit Daten übergeben. Und in der Schleife etwas besonderes mit dem ersten Element machen ist auch unnötig kompliziert, weil man das ja vor der Schleife machen könnte.

`SAVE_PATH` mit ``+`` mit dem Dateinamen zu verbinden ist falsch. Man setzt Pfade nicht mit Zeichenkettenoperationen zusammen. Dafür gibt es das `pathlib`-Modul.

Ungetestet:

Code: Alles auswählen

SAVE_PATH = Path("...")


def concat_and_save_csv_files(filenames, gestern):
    filenames = iter(filenames)
    first_data = pd.read_csv(next(filenames))
    first_data["time"] = pd.to_datetime(first_data["time"], unit="ms")
    result = pd.concat(
        [
            first_data,
            *(pd.read_csv(filename)["state"] for filename in filenames),
        ],
        axis=1,
    )
    result.set_index("time", inplace=True)
    result.sort_index(inplace=True)
    result.to_csv(
        SAVE_PATH / f"{gestern}_HeizungGesamt.csv",
        sep=";",
        decimal=",",
        header=False,
    )
Und wie gesagt: IMHO Fehlerhaft weil total unsicher ob da nicht Daten verloren gehen/nicht zusammenpassen.