Dictionary mit Matplotlib grafisch ausgeben

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
Benutzeravatar
carry03
User
Beiträge: 5
Registriert: Montag 8. Januar 2007, 12:05
Wohnort: Biberach

Hallo zusammen,
ich schreibe mein erstes Python-Programm, das zur Auswertung von Messreihen dienen soll. Die Ergebnisse der Messdatenauswertung habe ich in einem Dictionary gespeichert und möchte diese grafisch mit Matplotlib ausgeben. In meinem verschachtelten Dictionary habe ich außerdem alle Daten, die ich aus der Konfiguration erhalte, abgelegt.

Hier eine vereinfachte Darstellung meines Dictionaries:

dict{'Raum':{'Dateien': ['Datei 1', 'Datei 2', 'Datei 3'], 'Ergebnisse':{'Datei 1: [y1, y2, y3, y4], 'Datei 2: [y1, y2, y3, y4], 'Datei 3': [y1, y2, y3, y4]}}}

Die Namen der Schlüssel 'Raum' und 'Datei 1, Datei 2' erhalte ich aus der Konfiguration, d.h. die Namen sind mir quasi "unbekannt". Hierbei ist vielleicht wichtig zu erwähnen, dass die Anzahl der Räume und der Dateien variabel/beliebig ist.

Jetzt möchte ich die Ergebnisse [y1, y2, y3, y4]... für jeden Raum in einem Diagramm darstellen. Und weiß nicht, welche Anweisung für die y-Achse eingetragen werden muss. Die Werte der x-Achse (Frequenz in Hz) sind immer gleich. Ausserdem möchte ich die für jede Datei eine andere Farbe verwenden, und die Kurven mit dem Dateinamen, z.B.: Datei 1 benennen.

Ich wäre sehr dankbar, wenn ihr mir weiterhelfen könnt, bin nämlich am Ende meines Lateins :) ...
Nirven
User
Beiträge: 130
Registriert: Mittwoch 10. Mai 2006, 08:18
Wohnort: Bremerhaven

Ich schreib mal, was mir dazu einfällt:

Wenn du die Keys des äußeren Dictionaries abfragst, bekommst du die Räume in einer Liste, über die kannst du dann iterieren und (ich vermute so ist es gedacht) für jeden raum einen neuen Plot anlegen.

Das Dictionary Raum, brauchst du da den Eintrag "Dateien" wirklich? Ich würde da denke ich direkt 'Datei 1', 'Datei 2' usw als Key nehmen. Damit sparst du dir ein Dictionary, und wenn du die Namen der Dateien willst bekommst du sie über die Keys des Dicts. Allerdings unsortiert, wenn du sie sortiert brauchst, ist es so wohl besser.

Je nachdem wie das dann aufgebaut ist bekommst du entweder aus dict[Raum][Dateien] oder dict[Raum].keys() die Liste der Dateien. Über die kannst du dann iterieren, und in jeder Schleife die Werte der jeweiligen Datei als Kurvenwerte übergeben. Da dir der Name der aktuellen Datei bekannt ist, kannst du der Kurve diesen zuweisen, und auch eine Farbe.

Ich nehme mal an, du hast immer die gleichen Hz-Werte, an denen gemessen wird.

Beispielcode:

Code: Alles auswählen

x_werte = [Hertzwerte]
Farbe = iter(('rot', 'gelb', 'blau'))
for Datei in dict[Raum][Dateien]:
    y_werte = dict[Raum][Dateien][Ergebnisse][Datei]
    plot(x_werte, y_werte, Farbe.next(),  label=Datei)
    show()
Sicher nicht perfekt, aber ich hoffe, das hilft dir einen Ansatz zu finden.
Leonidas
Python-Forum Veteran
Beiträge: 16025
Registriert: Freitag 20. Juni 2003, 16:30
Kontaktdaten:

Die einzelnen Werte bekommst du so recht einfach raus (ich habe es nur schnell in der Python-Console laufen lassen, daher ist der Code nicht optimal:

Code: Alles auswählen

In [30]: for root_key, root_item in d.iteritems():
    results = root_item['Ergebnisse']
    for node_key, node_value in results.iteritems():
        print node_key
        for result in node_value:
            print result,
        print
   ....:
Datei 2
y1 y2 y3 y4
Datei 3
y1 y2 y3 y4
Datei 1
y1 y2 y3 y4
In Nirvens Code kann man sich auch noch den Iterator sparen wenn man statt dessen mit ``zip()`` arbeitet. Der Code wird dann auch etwas klarer.
My god, it's full of CARs! | Leonidasvoice vs (former) Modvoice
Benutzeravatar
carry03
User
Beiträge: 5
Registriert: Montag 8. Januar 2007, 12:05
Wohnort: Biberach

Hallo,
erstmal vielen Dank für die schnelle Antwort! Hab's endlich geschafft meine Ergebnisse in ein Diagramm zu packen!

Allerdings werden die Ergebnisse aller Räume in einer Grafik dargestellt, habt ihr diesbezüglich noch einen Tipp für mich? Ich habe, nach Nirvens Vorschlag, zuerst über die Räume und anschließend über die Dateien iteriert.

Die Beschriftung der Graphen hat leider nicht funktioniert, da die Texte immer an derselben Stelle stehen. Macht aber nix, habe entschieden dass eine Legende eh viel besser ist. :P

Also nochmals VIELEN VIELEN DANK UND LIEBE GRÜßE,

Carry
Nirven
User
Beiträge: 130
Registriert: Mittwoch 10. Mai 2006, 08:18
Wohnort: Bremerhaven

Du kannst mehrere Plots gleichzeitig erstellen, mit subplot. Dazu guckst du dir am Besten die Tutorials zu matplotlib an. Dann werden mehrere Plots in einem Fenster dargestellt, also z.B. ein Plot pro Raum.

Ich erstelle mehrere Plots, in dem ich den ersten Plot zeichne, speichere und dann säubere und den nächsten zeichen, speichere, säubere, etc.

Code: Alles auswählen

for raum in raum_dict:
    for datei in datei_list:
        # zeichnen
    savefig(FileName) #speichert den Plot, anstatt in mit show( anzuzeigen
    cla() #löscht den Plot, um den nächsten zu zeichnen
Den FileName für den Plot kannst du aus den Raumnamen und Datum oder so generieren, damit sie gut unterscheidbar und auch zuzuordnen sind.
CM
User
Beiträge: 2464
Registriert: Sonntag 29. August 2004, 19:47
Kontaktdaten:

Hoi carry

Ich möchte mich Nirven anschließen: Mit subplot oder ggf axes, kannst Du mehrere Plots in ein Bild packen. Schau Dir mal die verlinkte Seite an, auf der matplotlib-Seite gibt es auch ein Cookbook mit versch. Lösungen für diverse "Probleme".

Weshalb ich eigentlich schreibe: Wieso stehen alle Texte übereinander? Da ist Dir mit Sichereit ein kleiner Fehler unterlaufen. Magst Du mal Dein Skript posten, vielleicht finden wir den Fehler und können helfen.

Außerdem findest Du vielleicht noch folgende Links intereressant: http://www.python-forum.de/topic-2319.html (Es hat da auch ein paar Links, die Du nicht auf der "Topical"-Seite von scipy siehst. (Mir scheint lediglich, daß Du gerade einsteigst und solche Links gebrauchen könntest ;-) .)

Gruß,
Christian
Benutzeravatar
carry03
User
Beiträge: 5
Registriert: Montag 8. Januar 2007, 12:05
Wohnort: Biberach

Hey zusammen,
vielem Dank für die Links. Ihr habt Recht, ich kann solche Tipps wirklich gut gebrauchen!

Also so hab ich mein Diagramm aufgebaut:

Code: Alles auswählen

for item in dict:
    Datei = a[item]["Messdateien"]
    for Datei in a[item]["ergebnisse"]:
        y_werte = a[item]["ergebnisse"][Datei]
        plot(freq, y_werte, label = Datei)
        text(4,200,Datei, fontsize = 12)
        legend(loc = 9)
        show() 
        
So werden alle Dateien, in einer Grafik ausgegeben. Werde mir auch erstmal das "subplot" ansehen und die links durchgehen, kannte ich noch gar nicht :shock:

Gruss Carry
CM
User
Beiträge: 2464
Registriert: Sonntag 29. August 2004, 19:47
Kontaktdaten:

Hoi,

habe gerade keine Zeit ins Detail zu gehen, aber der Fehler bei Text ist der, daß Du in der Schleife nicht über die Textposition (4,200) iterierst. Schau Dir mal diesen Link an:
http://matplotlib.sourceforge.net/scree ... gn_text.py
bzw. text_handles.py in den Beispielen.

Gruß,
Christian
BlackJack

Die erste Zeile in der äusseren Schleife hat keinen Effekt, weil der Name `Datei` in der inneren Schleife neu gebunden wird.

`dict` ist kein guter Name für ein Dictionary, weil damit der eingebaute `dict`-Typ "verdeckt" wird.

Das verschachtelte zugreifen auf eine Dictionary/Listen-Struktur wird schnell schwer verständlich. Es ist besser Zwischenergebnisse an Namen zu binden. Ungetestet und basierend auf der Datenstruktur im ersten Beitrag zu diesen Thema:

Code: Alles auswählen

raum = data['Raum']
ergebnisse = data['Ergebnisse']
for dateiname in raum['Dateien']:
    y_werte = ergebnisse[dateiname]
    plot(freq, y_werte, label=dateiname)
    # ...
Benutzeravatar
carry03
User
Beiträge: 5
Registriert: Montag 8. Januar 2007, 12:05
Wohnort: Biberach

Hallo zusammen,
es hat geklappt, für jeden Raum wird ein Diagramm erstellt... Danke
Hab auch erstmal meinen Code bereinigt und den Zwischenergebnissen eigene Namen zugeteilt. Dann wirds gleich übersichtlicher.

Leider hab ich keine Ahnung wie ich über die Textposition iterieren kann, damit meine Graphen einzeln beschriftet werden :?:

So sieht mein Code inzwischen aus (das Dictionary heisst jetzt "data"):

Code: Alles auswählen

for item in data:
   raum = data[item]
   name = item
   for dateiname in dateien:
        result =  raum['ergebnisse'][dateiname]   
        y_werte = result
        plot(freq,y_werte, label = dateiname)
        legend (loc = 9)
        text (100,100, dateiname, fontsize  = 9)
        savefig(name)    
    cla()
Liebe Grüße, Carry
CM
User
Beiträge: 2464
Registriert: Sonntag 29. August 2004, 19:47
Kontaktdaten:

Hoi,

ja, warum willst Du denn das denn überhaupt? Ich habe jetzt erst richtig Zeit mich damit auseinanderzusetzten: Aber was Du tust ist im Text und in der Legende dasselbe darstellen.

Wie wäre denn:

Code: Alles auswählen

for item in data:
   raum = data[item]
   name = item
   for dateiname in dateien:
        #Direktzuweisung ist moeglich - Du willst 'result' nicht mehr verwenden, oder?
        y_werte =  raum['ergebnisse'][dateiname]  
        plot(freq,y_werte, label = dateiname) #was ist 'freq'?
        # meist funktioniert das automatische Platzieren der Legende sehr gut, brauchst
        # Du 'loc=9'?
        legend (loc = 9)
        #kann entfallen, weil text == dateiname == label in Legende
        #text (100,100, dateiname, fontsize  = 9)
        #in die äußere Schleife  - sonst speicherst Du jedes Mal unter dem selben Namen
        #das macht das Skript langsam:
    savefig(name)
    cla()
Nebenbei: cla() ist im Übrigen nur sinnvoll, wenn Du keine xlim/ylim-Paramter oder Ähnliches übergibst - sonst entsteht sehr schnell ein Kuddelmuddel sondergleichen, das Du aber mit gesonderten Figures umgehen kannst. Das ist hier aber noch kein Problem, weil Deine x-Werte immer dieselben sind.

Zur Frage: In dem Du in der äußeren Schleife den Wert hundert setzt und in der inneren Schleife etwas davon abziehst. Wobei ich den Wert "100" nicht verstehe, da
text.__doc__ hat geschrieben: The default transform specifies that text is in data coords,
alternatively, you can specify text in axis coords (0,0 lower left and
1,1 upper right).
d. h. eigentlich müßtest Du Werte zwischen 0 und 1 dort übergeben, z. B. (aus einem aktuellen Skript von mir):

Code: Alles auswählen

ax = gca()
...
ax.text(0.8, 0.9, r'$\lambda_{max}\ = %.1f\ \rm{[nm]}$' % lambdamax,
            transform=ax.transAxes)
Also, in meiner Vorstellung solltest Du also - wenn Dein Text in der rechten oberen Ecke liegen soll - Werte ähnlich den meinen übergeben und dann darum iterieren, je nachdme, ob die Texte untereinander folgen sollen (2. Wert) oder nebeneinander (1.Wert).

Werte wie fontsize & Co. würde ich (fast) gar nicht im Skript setzen, sondern über die rcParameter (Du solltest irgendwo ein File names 'matplotlibrc' haben). Damit lebt es sich oft einfacher - natürlich nicht immer.

Soweit klar?

Gruß,
Christian
Benutzeravatar
carry03
User
Beiträge: 5
Registriert: Montag 8. Januar 2007, 12:05
Wohnort: Biberach

Guten Morgen Christian,
danke für deine Unterstützung. Du hattest gefragt woher der Wert "100" kommt. Damit habe ich den Text an der Stelle (x=100,y=100) platziert. Da über meine x-Achse die Frequenzen (= freq) von 50 bis 5000 Hz im Terzabstand aufgetragen sind. Werde deinen Vorschlag zur Beschriftung der Grafen erst ausprobieren (hatte leider wenig Zeit die letzten Tage), und hoffentich krieg ich das hin, dass die Beschriftung dynamisch zu jedem Grafen gesetzt wird. Hab aber schon ne Idee, wenn ichs raus hab geb ich gern Bescheid! Dank dir und nen schönen Tag :)
CM
User
Beiträge: 2464
Registriert: Sonntag 29. August 2004, 19:47
Kontaktdaten:

Moin,

Nebenbei:
Keine (?) plotting library ist clever genug alleine aus den "Roh"-Daten, die es zu plotten gilt die Größe aller Achsen, aller Unterplots, etc. in der Skala der Rohdaten abzuschätzen. Dafür gibt es schlicht zu viele Sonderfälle. Allerdings kann ein Anwender natürlich die eigenen Daten immer auf die relativen Koordinaten (im Falle von matplotlib von 0 bis 1) skalieren.

Ich schreibe das nochmal so explizit, weil ich den Eindruck hatte, daß hier das Mißverständnis war.

Gruß,
Christian
Antworten