Tageshöchstwerte aus csv Datei selektieren

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
Schlangenmensch
User
Beiträge: 61
Registriert: Freitag 18. Februar 2022, 10:13

Hallo,

ich versuche nun schon seit einiger Zeit die Tageshöchstwerte aus einer csv Datei zu selektieren. In der Datei sind täglich im 10 Minuten Abstand Temperaturwerte geloggt.

Beispiel:

11/10/2023;9:59;24.4
11/10/2023;10:9;28.3
11/10/2023;10:119;60.2
11/10/2023;10:29;80.2
" " " " " "


Bis jetzt konnte ich wenigsten einmal nur die Temperaturwerte selektieren. Was kann ich tun um jeweils den höchsten Wert pro Tag anzeigen zu lassen. Ich komme hier leider nicht weiter und hoffe Ihr könnt mir helfen.

Code: Alles auswählen

import os
import csv

with open("/home/****/******/*****.csv","r") as csv_datei:
    reader = csv.reader(csv_datei, delimiter=';')

    for zeile in reader:
        print(zeile[3])
__deets__
User
Beiträge: 14543
Registriert: Mittwoch 14. Oktober 2015, 14:29

Pandas benutzen und mal in dessen Dokumentation nach Gruppierung und Maximalwerten schauen, ist alles dokumentiert.
Sirius3
User
Beiträge: 17754
Registriert: Sonntag 21. Oktober 2012, 17:20

Dafür gibt es `itertools.groupby`; einfach nach dem Datum gruppieren und per `max` das Maximum suchen.
Benutzeravatar
noisefloor
User
Beiträge: 3856
Registriert: Mittwoch 17. Oktober 2007, 21:40
Wohnort: WW
Kontaktdaten:

Hallo,

CSV ist halt nur ein einfaches, text-basiertes Dateiformat, was per Konvention einer bestimmten Struktur folgt.

Alternativ zu Pandas könntest du auch z.B. https://github.com/wireservice/csvkit/ nutzen.

Gruß, noisefloor
Schlangenmensch
User
Beiträge: 61
Registriert: Freitag 18. Februar 2022, 10:13

Vielen Dank für Euere Antworten. Da werde ich mich jetzt erst einmal in Pandas einlesen.
Benutzeravatar
DeaD_EyE
User
Beiträge: 1021
Registriert: Sonntag 19. September 2010, 13:45
Wohnort: Hagen
Kontaktdaten:

Du machst den Fehler, den alle Anfänger machen und lernst wie Pandas funktioniert, anstatt erstmal Python zu lernen.
itertools.groupby wäre die einfache Lösung gewesen.
sourceserver.info - sourceserver.info/wiki/ - ausgestorbener Support für HL2-Server
__deets__
User
Beiträge: 14543
Registriert: Mittwoch 14. Oktober 2015, 14:29

@DeaD_Eye: warum ist das ein Fehler? Und darf man csv verwenden, oder muss man auch lernen, wie man zeilenweise Dateien einliest? Und die ganzen Typkonvertierungen machen's auch nicht einfacher.

Man muss Grundlagen der Sprache beherschen, das sehe ich auch so. Aber warum man auf das schweizer Taschenmesser verzichten soll, wenn Python ansonsten auch dafuer gelobt wird, so viel Dinge einfach zu machen, ist nicht schluessig in meinen Augen.
Benutzeravatar
DeaD_EyE
User
Beiträge: 1021
Registriert: Sonntag 19. September 2010, 13:45
Wohnort: Hagen
Kontaktdaten:

Es geht nicht darum, keine Bibliotheken zu verwenden, sondern erstmal zu lernen, wie man mit Python programmiert.
Das bedeutet nicht, dass man jede Funktion/Methode einer Bibliothek verstehen muss, aber man sollte schon ungefähr wissen, was im Hintergrund passiert.
Wenn man das nicht weiß, landet man hier im Forum und stellt Fragen.

Und darf man csv verwenden, oder muss man auch lernen, wie man zeilenweise Dateien einliest
  1. Lernen, wie man Zeilenweise einliest
  2. das csv Modul verwenden
  3. dann Pandas
Pandas ist ein eigenes Schweizer Taschenmesser, dass man verstehen muss, um es richtig anzuwenden.

Anfänger will Python lernen und das Erste, was er macht, ist dann ein Pandas-Tutorial. Also lernt der Anfänger Pandas und kein Python.
So lernen die Anfänger brav, wie man Pandas anwenden, scheitern aber schon bei den einfachsten Python-Basics.
Das ist meine Beobachtung über die Jahre und sie bestätigt sich immer wieder.
sourceserver.info - sourceserver.info/wiki/ - ausgestorbener Support für HL2-Server
Benutzeravatar
__blackjack__
User
Beiträge: 13116
Registriert: Samstag 2. Juni 2018, 10:21
Wohnort: 127.0.0.1
Kontaktdaten:

@Schlangenmensch: ``zeile[3]`` passt nicht zu den gezeigten Daten.

Hier mal eine Möglichkeit das mit Bordmitteln zu lösen:

Code: Alles auswählen

#!/usr/bin/env python3
import csv
from datetime import datetime as DateTime
from itertools import groupby
from operator import itemgetter


def main():
    with open(
        "/home/****/******/*****.csv", "r", newline="", encoding="utf-8"
    ) as csv_file:
        print(
            [
                (
                    #
                    # TODO: Eventuell muss man hier das Datumsformat anpassen,
                    #   da aus dem Beispiel nicht klar wird in welcher
                    #   Reihenfolge Tag und Monat in der Datei angegeben sind.
                    #
                    DateTime.strptime(date_text, "%d.%m.%Y").date(),
                    max(float(row[2]) for row in group),
                )
                for date_text, group in groupby(
                    csv.reader(csv_file, delimiter=";"), itemgetter(0)
                )
            ]
        )


if __name__ == "__main__":
    main()
Falls das erzeugen der Eingabedatei in Deiner Hand liegt: Datum und Zeit ergeben zusammen *einen* Wert, der nicht auf zweit Spalten verteilt werden sollte. Dann müsste man früher in ein `datetime`-Objekt umwandeln und die `key`-Funktion für `groupby()` anpassen. Wäre aber sowieso etwas sauberer die Umwandlung in die passenden Datentypen als *ersten* Schritt zu machen, bevor man die Daten weiterverarbeitet.
„All religions are the same: religion is basically guilt, with different holidays.” — Cathy Ladman
Schlangenmensch
User
Beiträge: 61
Registriert: Freitag 18. Februar 2022, 10:13

Hallo, ich bin hier nur zufällig vorbeigekommen und habe gesehen, es sind noch weitere Lösungsvorschläge eingegangen wofür ich mich hiermit recht herzlich bedanken möchte.

Bisher bin ich mit pandas zumindest schonmal soweit, daß ich bestimmte Einträge selektieren kann. Mir fehlt nun nur noch die tagweise Gruppierung bestimmter Werte.

Ich werde wohl erstmal das ganze versuchen mit pandas zu ende zu bringen um mich danach mit den anderen Lösungsvorschlägen zu beschäftigen.

Das ganze wird noch etwas dauern, da ich nur begrenzt Zeit habe.
Schlangenmensch
User
Beiträge: 61
Registriert: Freitag 18. Februar 2022, 10:13

Hallo allerseits,

ich habe mal wieder etwas Zeit gefunden um an meinem bzw. Euerem Projekt herumzubasteln.

Ich habe Datum und Zeit in "Excel" in einer Spalte zusammengefügt.

Nun habe ich aber das Problem, das ich diese Daten nicht in ein datetime Objekt umwandeln kann, da ich durch csv.reader die Daten in einer Liste vorliegen habe. Ich scheitere daran diese Liste in ein datetime Objekt zu verwandeln.

Hat jemand einen Tip für mich?

Code: Alles auswählen

def main():
    with open(
        "/home/bunjul/Schreibtisch/Lösch.csv", "r", newline="", encoding="utf-8"
    ) as csv_file:
    
    read = csv.reader(csv_file)
    for line in read:
        print(line)
    
       
    
        print(
            [
                (
                    #
                    # TODO: Eventuell muss man hier das Datumsformat anpassen,
                    #   da aus dem Beispiel nicht klar wird in welcher
                    #   Reihenfolge Tag und Monat in der Datei angegeben sind.
                    #
                    DateTime.strptime(date_text, "%d.%m.%Y").date(),
                    max(float(row[1]) for row in group),
                )

                for date_text, group in groupby(
                    csv.reader(csv_file, delimiter=";"), itemgetter(0)
                )
            ]
        )


if __name__ == "__main__":
    main()
Sirius3
User
Beiträge: 17754
Registriert: Sonntag 21. Oktober 2012, 17:20

Dein Programm funktioniert nicht, weil es falsch eingerückt ist. Und daher kann man auch nicht sehen, was wohin gehört.
Wie sehen Deine Input-Daten aus, und wie die Fehlermeldung, die Du erhältst?
Schlangenmensch
User
Beiträge: 61
Registriert: Freitag 18. Februar 2022, 10:13

Sorry, da gat mir irgendwas die Formatierung zerschossen.

Im Prinzip gabe ich das Programm von blackjack genommen und wollte dies Erweitern.

Ich lese eine csv datei in folgendem Format ein 02.03.2023 22:30 ; 22,9
02.03.2023 23:50 ;34,5
" " " "

Diese Werte werden, wenn ich das richtig verstanden habe, in einer Liste gespeichert.

Aus dieser Liste möchte ich wie von blackjackbeschrieben ein datetime Objekt machen. Nun weis ich aber nicht wie ich den Datumsstring der Liste in ein datetime Objekt umwandeln kann.

Leider kann ich mlmentan keine Fehlermeldungen posten, da ich unterwegs bin und keine Möglichkeit habe auf eine Konsole bzw. das Programm zuzugreifen. Ich werde die Meldungen bei Gelegenheit posten.
Benutzeravatar
__blackjack__
User
Beiträge: 13116
Registriert: Samstag 2. Juni 2018, 10:21
Wohnort: 127.0.0.1
Kontaktdaten:

Das `groupby()` funktioniert natürlich nicht mehr einfach durch den Wert in der ersten Spalte wenn der nicht mehr nur das Datum sondern den kompletten Zeitstempel enthält. Statt ``itemgetter(0)`` muss da etwas stehen das von dem Wert in der ersten Spalte das Datum abfragt. Also beispielsweise in dem man den kompletten Zeitstempel in ein `datetime`-Objekt parst und darauf dann `date()` aufruft.

Man könnte auch der Versuchung erliegen und das über Zeichenkettenoperationen zu machen, aber ich mag den Weg über `datetime` lieber, weil da dann auch gleich eine gewisse Validierung erfolgt ob die Daten valide Zeitpunkte sind.

Einen `reader` `read` zu nennen ist komisch und eine Tabellenzeile heisst im Englischen `row` und nicht `line`. Die Schleife ist aber sowieso falsch, denn man will das ja nicht pro Zeile machen, beziehungsweise passiert das ja nur für die erste Zeile weil alle anderen durch den zweiten `reader` *in* der Schleife ja schon verarbeitet wird. Im Moment wird durch dieses wirre Konstrukt die erste Zeile ausgegeben und im weiteren nicht berücksichtigt. Sollte das Absicht sein, beispielsweise weil das eine Kopfzeile ist, dann macht man das nicht *so*, sondern einfach mit einem `next()` auf den *einen* `reader` den man erstellt, bevor man den restlichen Inhalt verarbeitet.
„All religions are the same: religion is basically guilt, with different holidays.” — Cathy Ladman
Benutzeravatar
snafu
User
Beiträge: 6741
Registriert: Donnerstag 21. Februar 2008, 17:31
Wohnort: Gelsenkirchen

Ich find's schon etwas fies, einem Anfänger ein derart verschachteltes Konstrukt vor die Nase zu setzen und dann, wenn er es (erwartungsgemäß) nicht versteht, etwas von wirrem Code zu erzählen. Die LC sollte man IMHO mit zwei Hilfsfunktionen implementieren, damit sie anschließend für den Leser verständlicher wird. Also ich blicke da schon noch durch und viele Regulars bestimmt auch, nur halte ich das nicht für einen wirklich gelungenen Code. Das kannst du eigentlich besser, __blackjack__. :)
Benutzeravatar
__blackjack__
User
Beiträge: 13116
Registriert: Samstag 2. Juni 2018, 10:21
Wohnort: 127.0.0.1
Kontaktdaten:

@snafu: Diese äussere Schleife über alle Datensätze scheint einzig und alleine dazu da zu sein den ersten Datensatz zu ignorieren. Das ist ja unabhängig davon wie (un)verständlich der Code *in* der Schleife ist, der *tatsächlich* alle Datensätze verarbeitet (bis auf den ersten). Ich finde das extrem verwirrend, denn ``for datensatz in datensätze:`` vermittelt den Eindruck die Schleife sei dazu da alle Datensätze zu behandeln und nicht nur um den *ersten* zu behandeln.

Ungetestet:

Code: Alles auswählen

#!/usr/bin/env python3
import csv
from collections import namedtuple
from datetime import datetime as DateTime
from itertools import groupby
from operator import attrgetter


class Record(namedtuple("_Record", "timestamp temperature")):

    @property
    def date(self):
        return self.timestamp.date()

    @classmethod
    def from_row(cls, row):
        return cls(
            DateTime.strptime(row[0], "%d.%m.%Y %H:%M"),
            float(row[1].replace(",", ".")),
        )


def main():
    with open(
        "/home/bunjul/Schreibtisch/Lösch.csv", newline="", encoding="utf-8"
    ) as csv_file:
        rows = csv.reader(csv_file, delimiter=";")
        print(next(rows))  # Skip and print header.
        records = map(Record.from_row, rows)
        daily_max_temperatures = (
            max(group, key=attrgetter("temperature"))
            for _, group in groupby(records, attrgetter("date"))
        )
        print(list(daily_max_temperatures))


if __name__ == "__main__":
    main()
„All religions are the same: religion is basically guilt, with different holidays.” — Cathy Ladman
Benutzeravatar
snafu
User
Beiträge: 6741
Registriert: Donnerstag 21. Februar 2008, 17:31
Wohnort: Gelsenkirchen

__blackjack__ hat geschrieben: Dienstag 16. April 2024, 08:42 @snafu: Diese äussere Schleife über alle Datensätze scheint einzig und alleine dazu da zu sein den ersten Datensatz zu ignorieren.
Ich unterstelle mal, dass hier mehr Glück als Verstand dabei war. Wirklich durchschaut haben wird der TE deinen Ansatz wohl eher nicht.

Das zweite Snippet mit dem "namedtuple" befindet der Rat der Snafus übrigens als deutlich gelungener. Wir haben unsere Sitzung gerade beendet. :D
Schlangenmensch
User
Beiträge: 61
Registriert: Freitag 18. Februar 2022, 10:13

Hallo allerseits,

ich glaube ich habe die letzte Version des Programmes einigermassen verstanden.

Nun versuche ich noch etwas an der Ausgabe bzw. an der Formatierung herumzubasteln.

Da die Ausgabe der in einer Liste erfolgt, die nicht Zeilenweise ist habe ich mal versucht die Werte zeilenweise zu gliedern. Gibt bestimmt schönere Lösungen. :oops:

Code: Alles auswählen

import csv
from collections import namedtuple
from datetime import datetime as DateTime
from itertools import groupby
from operator import attrgetter


class Record(namedtuple("_Record", "timestamp temperature")):

    @property
    def date(self):
        return self.timestamp.date()

    @classmethod
    def from_row(cls, row):
        return cls(
            DateTime.strptime(row[0], "%d.%m.%Y %H:%M"),
            float(row[1].replace(",", "." )),

        )


def main():
    with open(
        "/home/bunjul/Schreibtisch/Lösch.csv", newline="", encoding="utf-8"
    ) as csv_file:
        rows = csv.reader(csv_file, delimiter=";")
        print(next(rows))  # Skip and print header.
        records = map(Record.from_row, rows)
        daily_max_temperatures = (
            max(group, key=attrgetter("temperature"))
            for _, group in groupby(records, attrgetter("date"))
        )
        

        x =  (list(daily_max_temperatures))
        for l in x:

            print(l)
            




if __name__ == "__main__":
    main()


Des Weiteren würde ich die "Listenausgabe":

Record(timestamp=datetime.datetime(2023, 1, 2, 16, 33), temperature=88.1)
Record(timestamp=datetime.datetime(2023, 3, 5, 1, 44), temperature=55.4)
Record(timestamp=datetime.datetime(2023, 4, 5, 5, 22), temperature=22.5)
Record(timestamp=datetime.datetime(2023, 5, 5, 10, 22), temperature=33.4)

wie folgt formatieren: 22.12.2023 16:33 , 44.5

Leider fehlt mir hierzu schlichtweg das Wissen, wie ich diese Formatierung umsetzen kann.

Gerne bin ich auch bereit eine kleine Spende zu überweisen, wenn mir jemand die Formatierung schreibt.
Sirius3
User
Beiträge: 17754
Registriert: Sonntag 21. Oktober 2012, 17:20

`l` ist der schlechteste Variablennamen, den es überhaupt gibt, nicht nur, dass der nichtssagend ist, sondern auch noch, dass man den leicht mit 1 oder I verwechseln kann.
`x` ist auch nicht viel besser für eine Liste.
Für die Ausgabe benutzt man f-Strings:

Code: Alles auswählen

for daily_max_temperature in daily_max_temperatures:
    print(f"{daily_max_temperature.timestamp:%d.%m.%Y %H:%M}, {daily_max_temperature.temperature:.1f}")
Antworten