Seite 1 von 1

Daten aus MySQL für Matplotlib

Verfasst: Sonntag 11. Januar 2015, 20:08
von pythondiger
Hallo zusammen,

ich nutze nun MySQL zur Speicherung von Wetterdaten.
Das Schreiben der Daten in die DB funktioniert wunderbar.
Beim Lesen bin ich mir nicht ganz sicher.
Aber beim Zugriff auf die gelesenen Daten gibt's einen Fehler. Um dem Fehler näher zu kommen, wollte ich das Ergebnis der SQL-Abfrage ausgeben. Aber bereits da gibt's den gleichen Fehler

Hier ist der Programmcode

Code: Alles auswählen

def create_diagram (diafile_name, title, x_label, y_label, city, duration, history):
        plot.clf
        plot.title (title + " (" + city + ")")
        plot.ylabel (y_label)
        plot.xlabel (x_label)
        plot.plot (history["datzeit"], history["temperatur"], color='#4884FF', linewidth=2, label = y_label)
        plot.savefig (city + "_" + diafile_name, format="png", dpi = 150)

def main ():
        SQL_str = "SELECT datzeit, temperatur, stadt FROM wetterwerte WHERE stadt = '" + city + "'"
        print "SQL " + SQL_str
        history = cur.execute (SQL_str)

        for row in history:
                print "raw data from database"
                print row[0], row [1]

        # Jetzt noch die Graphik erzeugen ...
        my_plot = plot.figure ()

        plot.setp (plot.gca().get_xticklabels(), rotation=35)
        plot.grid (True)

        create_diagram (plot_file_test, "Wetterdaten", "Zeit", "Temperatur", city, 0, history)

if  __name__=='__main__':
    main()

Die Fehlermeldung lautet
SQL SELECT datzeit, temperatur, stadt FROM wetterwerte WHERE stadt = 'YQC'
Traceback (most recent call last):
File "wetterdiagramme.py", line 193, in <module>
main()
File "wetterdiagramme.py", line 178, in main
create_diagram_for_city (city)
File "wetterdiagramme.py", line 147, in create_diagram_for_city
for row in history:
TypeError: 'long' object is not iterable
Wo liegt mein (Denk-) Fehler beim Zugriff auf die gelesenen Daten ?

Re: Daten aus MySQL für Matplotlib

Verfasst: Sonntag 11. Januar 2015, 21:03
von pythondiger
Den ersten Fehler habe ich schon einmal lokalisiert:

Es muss richtig heißen

Code: Alles auswählen

        print "SQL " + SQL_str
        numlines = cur.execute (SQL_str)
        history = cur.fetchall ()
Allerdings mag matplotlib die Daten trotzdem nicht.
Dort erhalte ich einen Fehler
plot.plot (history["datzeit"], history["temperatur"], color='#4884FF', linewidth=2, label = y_label)
TypeError: tuple indices must be integers, not str
Der Zeitstempel habe ich als String erhalten und gespeichert.

Wie kann ich am Besten matplotlib trotzdem diese Daten übergeben ?

Re: Daten aus MySQL für Matplotlib

Verfasst: Sonntag 11. Januar 2015, 21:28
von BlackJack
@pythondiger: Das ist wieder ein Problem damit das `history` einen anderen Datentyp hast als Du anscheinend vermutest. Lass Dir das doch mal ausgeben. Und dann was bei ``history['datzeit']`` passiert. Und was hat Dich glauben lassen das das so funktioniert?

Re: Daten aus MySQL für Matplotlib

Verfasst: Sonntag 11. Januar 2015, 22:09
von pythondiger
Nun, der Inhalt der DB hat mich das glauben lassen.
Und natürlich die entsprechende Zuweisung aus dem SELECT-Statement

Code: Alles auswählen

        SQL_str = "SELECT datzeit, temperatur, stadt FROM wetterwerte WHERE stadt = '" + city + "'"
        print "SQL " + SQL_str
        history = cur.fetchall ()
Nun sollte history zwei Spalten haben: datzeit und temperatur.

Wenn ich history [] ausgebe, erhalte ich
Sun Jan 11 19:25:07 2015 4
mit Datum und Temperatur (die 4).
Das Datum in dieser Form ist richtig.
In der DB ist die Spalte als VARCHAR(50) definiert.

Ich hatte gedacht, dass matplotlib damit arbeiten kann. Das scheint aber nicht der Fall zu sein.
Jetzt muss ich wohl diese Datum in einen für matplotlib passenden Wert / Datentyp umwandeln. Allerdings nicht nur in eine Zahl, sondern in eine Zahl, die das Datum darstellt.

Und momentan verzweifle ich bei den vielen Datentypen in Python (zumindest beim Datum) etwas.

Re: Daten aus MySQL für Matplotlib

Verfasst: Sonntag 11. Januar 2015, 22:39
von Sirius3
@pythondiger: Na, was machst Du denn bei Deinem print anders als beim plot?
Warum speicherst Du ein Datum nicht mit dem Datentyp in der Datenbank, der dafür vorgesehen ist?

Re: Daten aus MySQL für Matplotlib

Verfasst: Montag 12. Januar 2015, 07:15
von MagBen
Wenn Du viel Zeitbereichsdaten auswerten willst, dann kann es sinnvoll sein, das mit Pandas zu machen. Pandas basiert auf Numpy und Matplotlib, hat aber für die vielen kleinen Dinge die man bei der Datenanalyse dann doch selbst programmiert fertige Klassen und Methoden.
Z.B. Zeitbereichsdaten aus einer Datenbank auswerten:
http://pandas.pydata.org/pandas-docs/st ... ql-queries

Re: Daten aus MySQL für Matplotlib

Verfasst: Montag 12. Januar 2015, 23:11
von pythondiger
So, hier ein aktueller Stand meiner Fehlersuche (vieleicht hilft es ja dem Einen oder Anderen):
1. Problem mit dem Zeitwert in der DB:
Ich habe es nach längerem Versuch in der DB geschafft, eine Spalte vom Typ DATETIME zu füllen (wird aus dem String "kopiert").
Damit hatte ich nun ein passendes Feld.

2. Fehlermeldung von matplotlib
Schreibt man

Code: Alles auswählen

       plot.plot (history[0], history[1], color='#4884FF', linewidth=2, label = y_label)
statt

Code: Alles auswählen

       plot.plot (history["datzeit"], history["temperatur"], color='#4884FF', linewidth=2, label = y_label)
dann ist die Fehlermeldung von matplotlib
plot.plot (history["datzeit"], history["temperatur"], color='#4884FF', linewidth=2, label = y_label)
TypeError: tuple indices must be integers, not str
weg. Allerdings sind die Diagramme völliger Quatsch.

Also habe ich weiter experimentiert und das SELECT-Statement verändert. Da keine Auswirkung auf das Diagramm war, habe ich versuchsweise x- und y-Werte getrennt aus der DB gelesen (zwei SELECT und zwei Zielfelder (zeiten und temperaturen)). Und schon liefert

Code: Alles auswählen

        plot.plot (zeiten, temperatur, color='#4884FF', linewidth=2, label = y_label)
ein (fast) richtiges Diagramm.
Nur die Beschriftung der X-Achse passt nicht. Diese ist mit Werten um 7,35609 * 10^5 beschriftet und nicht mit dem Datum :(

Re: Daten aus MySQL für Matplotlib

Verfasst: Montag 12. Januar 2015, 23:22
von EyDu
So funktioniert Programmieren nicht, du kannst nicht einfach probieren sich da durch zu raten. Zumindest dann, wenn du halbwegs effektiv sein möchtest. Lass dir doch einfach mal das Ergebnis der SQL-Anfrage deines ersten Beispiels ausgeben. Anschließend überprüfst du, was die plot-Funktion erwartet. Das wirst du sicher feststellen, wo genau dein Fehler ist und was du ändern musst.

Re: Daten aus MySQL für Matplotlib

Verfasst: Donnerstag 15. Januar 2015, 21:54
von pythondiger
EyDu hat geschrieben:So funktioniert Programmieren nicht, du kannst nicht einfach probieren sich da durch zu raten. Zumindest dann, wenn du halbwegs effektiv sein möchtest. Lass dir doch einfach mal das Ergebnis der SQL-Anfrage deines ersten Beispiels ausgeben. Anschließend überprüfst du, was die plot-Funktion erwartet. Das wirst du sicher feststellen, wo genau dein Fehler ist und was du ändern musst.
@EyDu: Danke für diesen hilfreichen Tipp.
Ich bin Neuling, was die Nutzung von Python angeht. Ich schreibe jedoch nicht mein erstes Programm. Aber wie ich feststellen muss, unterscheidet sich Python von Sprachen wie COBOL oder FORTRAN77 erheblich.
Zum Schreiben nutze ich natürlich parallel mysql, um auch die Daten zu sehen.

Ich habe übrigens auch die Doku von matplotlib. Allerdings kann man sich als Neuling in den 2578 Seiten der Doku leicht verlaufen und auch das eine oder andere Wichtige übersehen.

Aber das Herumprobieren (auch wenn es angeblich nicht geht), hat mich jetzt zum Ziel geführt.

Der passende Codeschnipsel, der mir den letzten Schritt ermöglichte, war das "Casten" der Datumswerte für matplotlib. Zugleich habe ich eine effizientere Möglichkeit gefunden, um mit nur einem SELECT die Daten zu holen und aufzubereiten.

Der wesentliche Gesamtcode

Code: Alles auswählen

def create_diagram (diafile_name, title, x_label, y_label, city, duration, zeiten, temperaturen):
        filename = city + "_" + diafile_name

        plot.clf
        plot.title (title + " ( - " + city + " - )")
        plot.ylabel (y_label)
        plot.xlabel (x_label)
        plot.plot (zeiten, temperaturen, color='#4884FF', linewidth=2, label = y_label)
        plot.savefig (filename, format="png", dpi = 150)

def main():
        SQL_str = "SELECT datzeit_num, temperatur FROM wetterwerte WHERE stadt = '" + city + "' ORDER BY datzeit_num"
        _history = cur.fetchall ()

        zeiten = [date for (date, value) in _history]
        temperaturen = [value for (date, value) in _history]

        # Jetzt noch die Graphik erzeugen ...
        my_plot = plot.figure ()

        plot.setp (plot.gca().get_xticklabels(), rotation=35)
        plot.grid (True)

        create_diagram (plot_file_test, "Wetterdaten", "Zeit", "Temperatur", city, 0, zeiten, temperaturen)

Ob der Code den ästhetischen Ansprüchen genügt, weiß ich nicht.

Einen letzten Schönheitsfehler habe ich allerdings noch.
Matplotlib gbt das Datum (X-Achse) leicht gedreht aus. Wenn ich die Ausgabe "Jan 13 2015" habe, wird diese unten (bei mir der "Anfang") abgeschnitten. Der Monatsname ist dadurch nicht lesbar.
Aber damit kann ich momentan gut leben.

Re: Daten aus MySQL für Matplotlib

Verfasst: Donnerstag 15. Januar 2015, 22:28
von EyDu
pythondiger hat geschrieben:Ich habe übrigens auch die Doku von matplotlib. Allerdings kann man sich als Neuling in den 2578 Seiten der Doku leicht verlaufen und auch das eine oder andere Wichtige übersehen.
Du musst auch nicht die ganze Dokumentation durchlesen, du musst nur an der richtigen Stelle suchen ;-) Bei Matplotlib bietet es sich an, wenn du zunächst in die Gallery schaust. Dort suchst du das Beispiel, welches deinem Wunsch am nächsten kommt, und orientierst dich dann daran.
pythondiger hat geschrieben:Aber das Herumprobieren (auch wenn es angeblich nicht geht), hat mich jetzt zum Ziel geführt.
Zwischen mehr oder weniger gezielt herumprobieren und raten besteht ja auch noch ein unterschied. So lange man am Ende versteht was passiert, ist auch alles in Ordnung.

Noch kurz zu deinem Code (ich fange einfach mal oben an):
In Zeile 4 passiert nichts, du musst das clf auch aufrufen, wenn es einen Effekt haben soll. Momentan ist der Aufruf aber auch gar nicht notwendig, da das Diagramm zu dem Zeitpunkt so oder so noch leer ist. Nach dem Methodennamen sollte beim Aufruf auch kein Leerzeichen stehen, das ist da unnötig und führt auch nicht zu besseren Lesbarkeit. Verwende einfach ``plot.title(...)`` stat ``plot.title (...)``.

Strings solltest du nicht mittels "+" zusammensetzen, es gibt eine hübsche format-Methode auf Srings, dann wird das ganze gleich viel leserlicher.

Zeile 12 sollte da so auf gar keinen Fall stehen, das öffnet nur Tür und Tor für SQL-Injections. Schau dir dazu mal die execute-Methode auf den Datenbankobjekten an, damit kannst du sicher Werte in deine Abfrage packen. Wo kommen eigentlich "city" und "cur" her?

An deiner Namensgebung solltest du auch noch arbeiten. Statt "cur" könntest du einfach "cursor" schreiben, dann weiß jeder worum es geht. Die zwei Zeichen nehmen nicht so viel Speicherplatz weg. Auch solltest du dich für deutsche oder englische Bezeichner entscheiden, der momentane Mix liest sich unschön. Auch ist der führende Unterstrich bei "_history" unnötig. Der wird nur verwendet, um Objekte als privat zu markieren. Das ist in diesem Fall aber gar nicht notwenig, das sieht von außen eh niemand.

Auch sind die Namen "date" und "value" schlecht gewählt. Value ist viel zu generisch und könnte einfach "temperature" heißen. Die Daten lassen sich auch leichter Teilen:

Code: Alles auswählen

zeiten, temperaturen = zip(*_history)

Re: Daten aus MySQL für Matplotlib

Verfasst: Freitag 16. Januar 2015, 22:12
von pythondiger
Hallo EyDu, vielen Dank für die zusätzlichen Hinweise.

Der hier gepostete Code ist nicht wirklich komplett. Ich habe mich auf das für die Problembeschreibung notwendige beschränkt.
Dabei ist mir auch ein Fehler unterlaufen. Es fehlt nämlich in Zeile 12,5 das cur.execute (SQL_str).

main() ist auch nicht wirklich main(), sondern wird in einer Schleife aufgerufen, die über city läuft und die DB wird ebenfalls bereits vorher verbunden.

Deshalb ist aber meines Erachtens das plot.clf notwendig, da ich ja wieder ein weiteres Diagramm generieren möchte.

Je mehr ich jetzt davon lerne, umso wenig hat Python eine Chance, eine von mir präferierte und gern genutzte Sprache zu werden.
Dafür gibt es bei Python zuviel Freiheit und harte Formatierungsvorschriften.
Ich bin eine strikte Variablendeklaration mit sauberer Typwandlung, aber freier Formatierung gewohnt; hier haben insbesondere ADA und Nachfolger ihre Spuren hinterlassen.
Aber das ist jetzt OT und kann in eine sehr hitzige Diskussion über Vor- und Nachteile dieser verschiedenen Konzepte führen.

Re: Daten aus MySQL für Matplotlib

Verfasst: Freitag 16. Januar 2015, 22:22
von BlackJack
@pythondiger: Harte Formatierungsvorschriften ist doch eigentlich nur die Einrückung. Und die macht jeder in den Sprachen bei denen man das nicht machen müsste trotzdem. Also ist es im Grunde genau das gleiche, nur ohne Klammern oder Schlüsselworte die Blöcke umschliessen.