Matplotlib - Achsen

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.
muhazz
User
Beiträge: 37
Registriert: Donnerstag 1. April 2010, 11:58

Hallo liebe Python-Gemeinde,

wer meine letzten Threads gelesen hat, weiß wohl schon, um was es wieder gehen wird. Ich benötige leider wieder Hilfe beim Darstellen einer Julia-Menge.
Matplotlib wurde in diesem Zusammenhang schon einmal diskutiert (zur Errechnung des Randes einer Figur), ich hatte es aber für diesen Zweck verworfen und einen eigenen Algorithmus entworfen, statt auf die automatische Verarbeitung von matplotlib zu vertrauen. Da weiß ich wenigstens, was passiert :)

Aber für die Bewältigung eines anderen Problems scheint mir matplotlib gut geeignet zu sein.
Ich habe den Rand einer Julia-Menge errechnet und in Form eines Arrays gespeichert. Nun möchte ich einige einzelne Bahnen "darüberplotten".
Das klappt an sich problemlos, nur die Achsen des Koordinatensystems bereiten mir noch große Schwierigkeiten und einige tieferliegende Verständnisprobleme, die ich mir alleine nicht schnell genug beantworten kann.

Ich möchte die Achsen so transformieren, dass sie den tatsächlichen x und y Werten der Einträge entsprechen und nicht mehr den ganzzahligen Positionen der Einträge im Array.
Ich weiß nun nicht, wie ich diese Transformation umsetzen soll, und auch nicht, ob ich mich in den nachfolgenden Angaben des plot-Befehls nun weiterhin nach den ganzzahligen Positionen im Array oder den tatsächlichen x/y-Werten richten muss (ein Teil der "Verständnisschwierigkeiten" über die Funktionsweise von matplotlib). Beides wäre aber kein Problem, genausowenig wie der mathematische Algorithmus für die Transformation der Achsen - ich weiß nur nicht, wie ich das matplotlib konkret mitteilen soll.

Viele Grüße,
muhazz
Benutzeravatar
gkuhl
User
Beiträge: 600
Registriert: Dienstag 25. November 2008, 18:03
Wohnort: Hong Kong

Das geht z.B. mit ``xticks`` und ``yticks``. Schöner ist aber die Verwendung von z.B. ``pcolormesh`` oder ``contour``, wo man die richtigen Koordinaten gleich mit übergeben kann (siehe mein Beispiel).
Darii
User
Beiträge: 1177
Registriert: Donnerstag 29. November 2007, 17:02

Wie sehen deine Daten genau aus, und wie plottest du sie? Bitte immer Code-Beispiele, hab keine Lust meine Zeit mit raten zu verschwenden. Dein Problem klingt aber sehr danach als würdest du die plot-Funktion falsch benutzen. Lies dir am besten nochmal die Doku dazu durch.

Aus der Doku, das 1. willst du benutzen, das 2. benutzt du vermutlich:
plot(x, y) # plot x and y using default line style and color

plot(y) # plot y using x as index array 0..N-1
Benutzeravatar
gkuhl
User
Beiträge: 600
Registriert: Dienstag 25. November 2008, 18:03
Wohnort: Hong Kong

Ich hatte angekommen es geht im diesen Code: http://www.python-forum.de/viewtopic.ph ... ht=#166491
muhazz
User
Beiträge: 37
Registriert: Donnerstag 1. April 2010, 11:58

Darii hat geschrieben:Bitte immer Code-Beispiele, hab keine Lust meine Zeit mit raten zu verschwenden.
Das klingt nach einer guten Idee - ich glaube, ihr habt mein Problem misverstanden. Raten möchte ich niemand zumuten, ich dachte nur, ich hätte es schon eindeutig genug formuliert.

Also, hier mein Programm

Edit: Hmm, ich muss mich wohl korrigieren - ich wurde doch nicht misverstanden, ich habe nur dein Programm nicht verstanden, gkhul :roll:
So elegant das im Programmcode auch aussieht, ich möchte contour() auf keinen Fall benutzen.
Darii
User
Beiträge: 1177
Registriert: Donnerstag 29. November 2007, 17:02

muhazz hat geschrieben:Raten möchte ich niemand zumuten, ich dachte nur, ich hätte es schon eindeutig genug formuliert.
Das schafft man fast nie -> Minimalbeispiel.
Also, hier mein Programm
Wie ruf ich das auf? Was macht das?
muhazz
User
Beiträge: 37
Registriert: Donnerstag 1. April 2010, 11:58

Das Programm gibt den Rand der Julia Menge mit dem zugehörigen komplexen Parameter c aus und plottet 2 festgelegte Punkte (zu Testzwecken)in default-Einstellung darüber.
Zuerst wird die Ausgefüllte Julia-Menge in rechnen() errechnet und in Form eines Arrays gebracht.
Danach wird durch rand_menge() eine Liste mit allen Randpunkten der Julia-Menge erzeugt, die mithilfe von rand_array() wieder zu einem Array verarbeitet wird, das jetzt den Rand der Julia-Menge enthält.
In darstellen() wird dieses Array durch matplotlib.pyplot.matshow() dargestellt.

Bahn_rechnen() kann vorerst ignoriert werden.

Aufgerufen wird das Programm durch rand() mit den angegebenen Parametern - die letzten beiden haben aber noch keine Funktion (da noch keine Iteration gepolottet wird mit dem Startwert "start" und und der Anzahl an Iterationsschritten "punktanzahl").

Bsp: rand(0.3-0.4j , 0+0j , 10) erzeugt den Rand der Julia-Menge mit dem komplexen Parameter 0.3-0.4i

@gkuhl: Ich verstehe die Informationen zu xticks leider nicht. Ich kann nicht herausfinden, was "ticks" sind und erkenne auch keinen Zusammenhang zu meinem Problem :?
BlackJack

@muhazz: Ich denke Du machst nicht wirklich viel Gebrauch von den Möglichkeiten die `numpy` bietet sondern behandelst die Datenobjekte zu sehr wie normale verschachtelte Listen.

Bei `rand_array()` ist `e` überflüssig weil Du das entpacken auch schon in der ``for``-Schleife machen kannst: ``for x, y in randmenge:``. Allerdings ist bei `numpy.array`\s ``a[x][y]`` gleichwertig zu ``a[(x, y)]``, dass heisst eigentlich bräuchtest Du die Elemente von der Randmenge gar nicht auf zwei Namen verteilen sondern kannst sie direkt verwenden. Wenn `randmenge` eine zweidimensionales Array mit den koordinaten wäre, könnte man sogar ganz ohne externe Schleife auskommen:

Code: Alles auswählen

def rand_array(randmenge):
    ergebnis = numpy.zeros((LAENGE + 1, LAENGE + 1))
    ergebnis[randmenge[...,0], randmenge[...,1]] = -1
    return ergebnis
`rand_menge()` lässt sich mit einer "list comprehension" (LC) ausdrücken:

Code: Alles auswählen

def rand_menge(ergebnis):
    range_ = xrange(1, len(ergebnis) - 1)
    return [(x, y) for x in range_ for y in range_
            if ergebnis[x, y] == -1 and is_rand(y, x, ergebnis)]
Bei `rechnen()` ist dagegen die LC völlig überflüssig denn `range()` liefert schon eine Liste mit Zahlen. Die wird da durch die LC nur in eine weitere Liste umkopiert. Bei der Funktion solltest Du aber auf jeden Fall mal die `numpy`-Dokumentation konsultieren, denn für das Aufbauen eines Arrays mittels einer Funktion die von den Indizes abhängen gibt einem `numpy` Werkzeuge in die Hand.

Wenn man in einem ``if`` abhängig von der Bedingung `True` oder `False` zurückgibt, dann kann man auch gleich das Ergebnis der Bedingung zurückgeben, denn das ist ja `True` oder `False`. Ausserdem kann man `is_rand` mit wesentlich weniger Wiederholungen schreiben, wenn man mal die Unterschiede in den einzelnen ``or``-Teilen in eine Datenstruktur herauszieht:

Code: Alles auswählen

def is_rand(y,x,ergebnis):
    deltas = [(-1, -1), (-1, 0), (-1, 1), (0, -1),
              (0, 1), (1, -1), (0, 0), (0, 1)]
    return any(ergebnis[x + x_d, y + y_d] == 0 for x_d, y_d in deltas)
Bei einem Namen der mit `is_` oder `has_` beginnt, erwarten die meisten Programmierer, dass ein Wahrheitswert zurückgegeben wird. Das könnte man bei `is_element()` doch auch wunderbar machen -- warum ist das da 0 und -1? Es wäre sowieso eine Überlegung Wert das ganze als `bool`-Array zu speichern oder zumindest als Werte 0 und 1. Das spart Platz und Du kannst auch die Randpixel mittels Array-Operationen berechnen. Wenn Du das Bild um ein Pixel nach rechts "schiebst" und vom Original abziehst, dann bleiben nur die Pixel übrig die links einen nicht gesetzten Nachbarn und rechts einen gesetzten haben, also der linke Rand. Das machst Du noch mit den anderen drei Richtungen und die Resultate kann man zu einem "Gesamtrandbild" zusammenfügen.

Das ``return`` bei `bahn_berechnen()` scheint mir falsch eingerückt zu sein!?
muhazz
User
Beiträge: 37
Registriert: Donnerstag 1. April 2010, 11:58

Vielen Dank für die ausführliche und verständliche Optimierung, BlackJack!
Alles, was ich auf die Schnelle nachvollziehen konnte, habe ich geändert.
Eine Frage habe ich noch: Warum speicherst du das Ergebnis des xrange-Operators in eine lokale Variable, statt ihn an passender Stelle einfach zweimal zu verwenden?
Spart das auch Zeit (range_ muss später doch eh wieder durchlaufen werden?) oder ist nur übersichtlicher?

Code: Alles auswählen

def rand_menge(ergebnis):
    range_ = xrange(1, len(ergebnis) - 1) # Randpunkte werden nicht angenommen
    return [(y, x) for y in range_ for x in range_
            if ergebnis[y, x] == -1 and is_rand(y, x, ergebnis)]

Aber generell kann ich nur wieder sagen, was ich in meinen letzten Threads schon oft betont hatte:
Ich bin wirklich dankbar um jede Optimierung, aber die Community hier scheint wesentlich höhere Ansprüche an meine Programme zu stellen, als ich selbst.
Von daher werde ich auch nicht jede mögliche Optimierung vornehmen, da das aus Zeitgründen einfach ineffektiv wäre für mich.

Deshalb möchte ich auch wieder auf mein Hauptproblem verweisen, damit das nach den vielen Verbesserungsvorschlägen von BlackJack (und evtl. folgenden Diskussionen um diese) nicht in Vergessenheit gerät:

Das Programm läuft im Moment, auch wenn es nicht sehr elegant und effektiv programmiert zu sein scheint, es müssen nur noch die Achsen so verschoben und skaliert werden, dass sie den wirklichen x/y - Koordinaten der Punkte in der komplexen Ebene entsprechen.

Viele Grüße,
muhazz
Benutzeravatar
gkuhl
User
Beiträge: 600
Registriert: Dienstag 25. November 2008, 18:03
Wohnort: Hong Kong

muhazz hat geschrieben:Ich verstehe die Informationen zu xticks leider nicht. Ich kann nicht herausfinden, was "ticks" sind und erkenne auch keinen Zusammenhang zu meinem Problem :?
Dein Problem ist das du die ``ticks`` ändern möchtest. Hast du das Beispiel in der Dokumentation mal ausprobiert?
muhazz hat geschrieben:Ich bin wirklich dankbar um jede Optimierung, aber die Community hier scheint wesentlich höhere Ansprüche an meine Programme zu stellen, als ich selbst.
Von daher werde ich auch nicht jede mögliche Optimierung vornehmen, da das aus Zeitgründen einfach ineffektiv wäre für mich.
Ich sehe das eher so, dass du unnötig viel Zeit damit verschwendest die Fähigkeiten von NumPy und Matplotlib suboptimal neu zu programmieren. Stattdessen könntest du deren Möglichkeiten auch gleich verwenden. Das mag beim ersten Einarbeiten etwas mehr Zeit in Anspruch nehmen, wird sich aber lohnen, wenn du später vor einem ähnlichen Problem stehst.

Für was für ein Projekt programmierst du denn eigentlich?
BlackJack

@muhazz: Das `xrange`-Objekt hatte ich nur an einen Namen gebunden weil mir die LC sonst zu lang und unübersichtlich geworden wäre.
muhazz
User
Beiträge: 37
Registriert: Donnerstag 1. April 2010, 11:58

gkuhl hat geschrieben: Dein Problem ist das du die ``ticks`` ändern möchtest. Hast du das Beispiel in der Dokumentation mal ausprobiert?
Ich bin dabei, habe das Konzept aber nicht wirklich verstanden.
Ich habe gesehen, dass die Werte, die von numpy.arange() erzeugt werden, an der betreffenden Achse stehen und dass ich die Achseneinheiten durch einem optionalen Parameter mit 'Harry', 'Sally' und 'Sue' durchzählen kann (statt mit Zahlen).
Aber ich verstehe nicht, nach welchen Regeln sich diese Werte auf der Achse verteilen und weiß nicht, wie ich matplot dazu bringen kann, nur einige Achsenbeschriftungen anzuzeigen statt immer alle.
Außerdem war mein Rechner hoffnungslos überfordert, als ich
arange(-LAENGE , LAENGE+1+1/ZOOM , 1/ZOOM) als Parameter verwenden wollte.
Ich glaube, ich habe da etwas grundlegend falsch verstanden.

Außerdem wäre es schick, wenn die Achsen nicht am Rand der Abbildung wären, sondern ein richtiges Koordinatensystem bilden würden, sich also am Punkt (0|0) schneiden.

gkuhl hat geschrieben: Ich sehe das eher so, dass du unnötig viel Zeit damit verschwendest die Fähigkeiten von NumPy und Matplotlib suboptimal neu zu programmieren. Stattdessen könntest du deren Möglichkeiten auch gleich verwenden. Das mag beim ersten Einarbeiten etwas mehr Zeit in Anspruch nehmen, wird sich aber lohnen, wenn du später vor einem ähnlichen Problem stehst.
Da hast du wohl recht, aber da es sich um ein einmaliges Projekt handelt und ich nicht wieder vor ähnliche probleme gestellt werde, ist das völlig ok für mich und meiner Meinung nach eindeutig effektiver.
Mit Listen kenne ich mich aus dem Informatik-Unterricht verhältnismäßig gut aus, in numpy,scipy und matplot muss ich mich völlig neu einarbeiten.

Ich programmiere übrigens für eine Mathe-Facharbeit. Die Programme dienen dabei lediglich als Hilfsmittel bzw. Werkzeug, um Grafiken zu erstellen und verschiedene Dinge zu errechnen- der Progammierstil wird nicht bewertet.
Wesentlich wichtiger als Eleganz im Programmtext ist eher mein Verständis des Programmes und die Durchsichtigkeit des Algorithmus.
Deshalb möchte ich beispielsweise auch auf die contour() Funktion verzichten - ich weiß nicht genau, welchen Algorithmus matplotlib nutzt, um sich den Rand einer Figur zu errechnen.

Viele Grüße,
muhazz
Darii
User
Beiträge: 1177
Registriert: Donnerstag 29. November 2007, 17:02

Versuch es doch mal mit einem einfachen scatter-Plot. wobei die Punktmenge dann dem Rand deiner Julia-Menge entspricht. Dann hast du automatisch richtige Achsenbeschriftungen.
Benutzeravatar
gkuhl
User
Beiträge: 600
Registriert: Dienstag 25. November 2008, 18:03
Wohnort: Hong Kong

muhazz hat geschrieben:Aber ich verstehe nicht, nach welchen Regeln sich diese Werte auf der Achse verteilen und weiß nicht, wie ich matplot dazu bringen kann, nur einige Achsenbeschriftungen anzuzeigen statt immer alle.
Grundsätzlich gilt da: Konsole öffnen und ausprobieren! Dann sollte das eigentlich recht schnell klar werden. Das erste Argument ist eine Liste mit Positionen an denen ein "tick" (die Dinger an den Achsen, wo normalerweise Zahlen dran stehen) erscheinen soll und das zweite Argument ist eine optionale Liste mit Bezeichnern für eben diese Positionen.

Um das nochmal klar zu machen: Wenn du die Beschriftung an der Achse veränderst, ändert das nichts an den Koordinatenachse. Bei x=0 wird immer 0 sein, auch wenn du da etwas anderes hinschreibst. Und ``matshow`` wird dir bei 0 immer die erste Spalte der Matrix plotten. Wenn du richtige Achsen und Beschriftung haben willst, musst du eine anderen Plot-Funktion verwenden. Aber das habe ich dir ja schon mehrmals gesagt.
muhazz hat geschrieben:Außerdem wäre es schick, wenn die Achsen nicht am Rand der Abbildung wären, sondern ein richtiges Koordinatensystem bilden würden, sich also am Punkt (0|0) schneiden.
Hier gibt es ein Beispiel.
muhazz
User
Beiträge: 37
Registriert: Donnerstag 1. April 2010, 11:58

gkuhl hat geschrieben:ein "tick" (die Dinger an den Achsen, wo normalerweise Zahlen dran stehen)
Ah!
gkuhl hat geschrieben: Um das nochmal klar zu machen: Wenn du die Beschriftung an der Achse veränderst, ändert das nichts an den Koordinatenachse. Bei x=0 wird immer 0 sein, auch wenn du da etwas anderes hinschreibst. Und ``matshow`` wird dir bei 0 immer die erste Spalte der Matrix plotten. Wenn du richtige Achsen und Beschriftung haben willst, musst du eine anderen Plot-Funktion verwenden. Aber das habe ich dir ja schon mehrmals gesagt.
So klar war mir das noch nicht. Ich muss also "schummeln" und mir die Beschriftungen einfach zurechtbasteln...naja, das Ergebnis sollte ja trotzdem genauso exakt sein.

Das klappt auch soweit ganz gut, das Zentrieren der Achsen hingegen klappt aber noch nicht.

Ich habe das Beispiel gelesen, ausprobiert und oberflächlich "verstanden", aber mein Programm ist ja ein wenig anders aufgebaut und hier kommt mir nun wieder mein mangelndes tiefergehendes Verständnis der ganzen Vorgänge in die Quere.
Im Beispiel gibt es ein Datenobjekt "ax", bei mir nicht. Wenn ich in meinen Code einfach

Code: Alles auswählen

spines['left'].set_position('zero')
schreibe, dann erhalte ich eine Fehlermeldung, und auch alle sonstigen Versuche, den Befehl zwanghaft irgendwie zu integrieren oder ein ähnliches Datenobjekt zu erstellen, sind genauso fehlgeschlagen.

Als erfahrener Programmierer sträuben sich da wohl die Haare, aber ich bewege mich hier auf einem Gebiet, von dem ich nicht viel verstehe und auf die Schnelle auch nicht durchschauen kann.
Ich hoffe, ihr habt noch einmal Geduld :)
Benutzeravatar
gkuhl
User
Beiträge: 600
Registriert: Dienstag 25. November 2008, 18:03
Wohnort: Hong Kong

``ax`` ist einen Achsenobjekt. Woher das kommt siehst du in dem Beispiel.

Lass dir mal deine Matplotlib-Version in der Konsole ausgeben. ``spines`` gibt es erst ab Version 0.99:

Code: Alles auswählen

>>> import matplotlib
>>> matplotlib.__version__
muhazz
User
Beiträge: 37
Registriert: Donnerstag 1. April 2010, 11:58

Version 0.99.1 spuckt er mir aus.
Das sollte dann ja funktionieren :)

Ich sehe auch, wo das Achsenobjekt herkommt, aber ich habe keine figure und möchte kein subplot - in meiner Situation weiß ich also nicht, wie ich den spines Befehl einbinden soll.
Ganz abgesehen davon, dass ich von objektorientierter Programmierung keine Ahnung habe und mir das alles einfach sehr oberflächlich zusammenreime, weiß ich nicht, an welches Objekt ich spines "dranhängen" soll (und einfach ohne Objekt geht auch nicht).
Ich habe da ein bisschen auf gut Glück rumprobiert, aber das hat alles nicht geklappt und ich glaube auch nicht, dass das irgendwie Sinn gemacht hat.

Also muss ich hier um ganz konkrete Hilfe bitten.
Benutzeravatar
gkuhl
User
Beiträge: 600
Registriert: Dienstag 25. November 2008, 18:03
Wohnort: Hong Kong

muhazz hat geschrieben:Ich sehe auch, wo das Achsenobjekt herkommt, aber ich habe keine figure und möchte kein subplot - in meiner Situation weiß ich also nicht, wie ich den spines Befehl einbinden soll.
Matplotlib erstellt automatisch eine figure- und subplot-Objekt, wenn keins vorhanden ist. Da du die Objekte jetzt brauchst, musst nun eimmal einen Namen dran binden um z.B. ``spines`` verwenden zu können. Wenn du matploblib verwenden möchtest, musst du dich auch mit dessen Grundlagen auseinander setzen wollen.

Wenn du ein Problem mit ``spines`` hast, poste doch bitte etwas Code, wo dein Problem auftaucht.
muhazz
User
Beiträge: 37
Registriert: Donnerstag 1. April 2010, 11:58

Der relevante Teil des Codes sieht jetz so aus:
http://paste.pocoo.org/show/208248/

Beim Aufruf von beispielsweise

Code: Alles auswählen

>>> rand(0+0j, 0.2+0.5j, 0.6+0.8j, 0.9+0.7j)
wird keine Fehlermeldung ausgegeben und es öffnet sich ein Fenster, das aber weiß bleibt. PC hängt und "keine Rückmeldung" des Ausgabe-Fensters.

Die einzige Änderungen ab der letzten funktionierenden Version war das Einfügen von Zeile 8-13.
Benutzeravatar
gkuhl
User
Beiträge: 600
Registriert: Dienstag 25. November 2008, 18:03
Wohnort: Hong Kong

Versuche es mal mit ``plt.show()`` anstelle von ``fig.show()``.
Antworten