Warum benötigt pyplot.contour () Z als 2D-Array?

8

Die Funktion matplotlib.pyplot.contour() benötigt 3 Eingabe-Arrays X , Y und Z .
Die Arrays X und Y geben die x- und y-Koordinaten der Punkte an, während Z den entsprechenden Wert der interessierenden Funktion an den Punkten angibt.

Ich verstehe, dass np.meshgrid() die Erstellung von Arrays erleichtert, die als Argumente für contour() dienen:

%Vor%

Das funktioniert gut. Praktisch funktioniert das auch:

%Vor%

Warum ist der Z -Eintrag erforderlich , um ein 2D-Array zu sein?

Warum ist etwas wie das Folgende nicht erlaubt, obwohl es genau dieselben Daten enthält, die entsprechend ausgerichtet sind?

%Vor%

Was ist auch die Semantik, wenn nur Z angegeben ist (ohne die entsprechende X und Y )?

    
dhrumeel 04.02.2017, 21:40
quelle

3 Antworten

4

Betrachten Sie die Dokumentation von contour Man findet, dass es mehrere Möglichkeiten gibt, diese Funktion aufzurufen, z contour(Z) oder contour(X,Y,Z) . Sie werden also feststellen, dass keine Werte X oder Y überhaupt vorhanden sein müssen.

Um jedoch eine Kontur zu zeichnen, muss das zugrundeliegende Gitter der Funktion bekannt sein. Matplotlibs contour basiert auf einem rechteckigen Raster. Aber selbst wenn man contour(z) zulässt, wobei z ein 1D-Array ist, würde es unmöglich sein zu wissen, wie das Feld geplottet werden soll. Im Fall von contour(Z) , wobei Z ein 2D-Array ist, wird durch seine Form das Raster für den Plot eindeutig festgelegt.

Sobald dieses Gitter bekannt ist, ist es ziemlich egal, ob die optionalen Arrays X und Y abgeflacht sind oder nicht; was ist eigentlich die Dokumentation sagt uns:

  

X und Y müssen beide 2-D mit der gleichen Form wie Z sein, oder sie müssen beide 1-D sein, so dass len (X) die Anzahl der Spalten in Z und len (Y) die Anzahl der Zeilen ist in Z.

Es ist auch ziemlich offensichtlich, dass etwas wie plt.contour(X_grid.ravel(), Y_grid.ravel(), Z_grid.ravel()) kann kein Konturdiagramm erstellen, da alle Informationen über die Gitterform verloren sind und die Konturfunktion nicht in der Lage ist, die Daten zu interpretieren. Z.B. Wenn len(Z_grid.ravel()) == 12 , könnte die Form des zugrundeliegenden Rasters (1,12), (2,6), (3,4), (4,3), (6,2), (12,1) sein.

Ein möglicher Ausweg könnte natürlich darin bestehen, 1D-Arrays zuzulassen und ein Argument shape , wie plt.contour(x,y,z, shape=(6,2)) , einzuführen. Dies ist jedoch nicht der Fall, also müssen Sie mit der Tatsache leben, dass Z 2D sein muss.

Wenn Sie jedoch nach einem Weg suchen, um ein countour-Plot mit abgeflachten (ravelled) Arrays zu erhalten, ist dies mit plt.tricontour() .

%Vor%

Hier wird ein Dreiecksgitter intern mit einer Delauney-Dreiecksbildung erzeugt. Daher werden sogar vollständig randomisierte Punkte ein schönes Ergebnis liefern, wie in dem folgenden Bild zu sehen ist, wo dies mit den gleichen Zufallspunkten verglichen wird, die contour gegeben wurden.

(Hier ist der Code, um dieses Bild zu erstellen )

    
ImportanceOfBeingErnest 04.02.2017, 22:50
quelle
2

Der eigentliche Code eines Algorithmus hinter plt.contour ist in _countour.cpp zu finden . Es ist ein ziemlich komplizierter C-Code, so dass es schwierig ist, genau zu folgen, aber wenn ich versuchen würde, einige Konturen erzeugende Codes zu erstellen, würde ich es auf die folgende Weise machen. Wählen Sie einen Punkt (x, y) am Rand und fixieren Sie dessen z -Wert. Iteriere über nahegelegene Punkte und wähle denjenigen aus, für den der z-Wert dem z-Wert des ersten Punktes am nächsten kommt. Setze die Iteration für den neuen Punkt fort, wähle den nahe gelegenen Punkt mit dem Z-Wert, der dem gewünschten am nächsten ist (aber überprüfe, dass du nicht zu einem Punkt zurückkehrst, den du gerade besucht hast), und fahre fort bis du kommst ein Zyklus oder eine Grenze erreichen.

Es scheint, dass etwas in der Nähe (aber ein bisschen komplexer) in _counter.cpp implementiert ist.

Wie Sie aus der informellen Beschreibung des Algorithmus sehen können, müssen Sie einen Punkt finden, der dem aktuellen "nahe" ist. Es ist einfach zu tun, wenn Sie ein rechteckiges Gitter von Punkten haben (ungefähr 4 oder 8 Iterationen wie folgt benötigen: (x[i+1][j], y[i+1][j]) , (x[i][j+1], y[i][j+1]) , (x[i-1][j], y[i-1][j]) und so weiter). Aber wenn Sie einige zufällig ausgewählte Punkte haben (ohne eine bestimmte Reihenfolge), wird dieses Problem schwierig: Sie müssen über alle Punkte iterieren, die Sie haben, um die nächsten zu finden und den nächsten Schritt zu machen. Die Komplexität eines solchen Schritts ist O(n) , wobei n eine Anzahl von Punkten ist (normalerweise ein Quadrat von der Größe eines Bildes). Ein Algorithmus wird also viel langsamer, wenn Sie kein rechteckiges Gitter haben.

Das ist der Grund, warum Sie tatsächlich zwei 2d-Arrays benötigen, die mit den x-, y- und z-Werten einiger Punkte übereinstimmen, die sich über einem rechteckigen Gitter befinden.

Wie Sie richtig angeben, können x und y 1d-Arrays sein. In diesem Fall werden die entsprechenden 2d-Arrays mit meshgrid rekonstruiert. In diesem Fall müssen Sie jedoch z als 2d-array haben.

Wenn nur z angegeben wird, sind x und y range von geeigneten Längen.

BEARBEITEN. Sie können versuchen, die zweidimensionalen Arrays x , y und z so zu "fälschen", dass x und y kein rechteckiges Gitter bilden, um zu überprüfen, ob meine Annahmen richtig sind.

%Vor%

Wie Sie sehen, sieht das Bild nicht so aus, als wäre es in der Nähe des richtigen Graphen, wenn (x, y, z) nur zufällige Punkte sind.

Nehmen wir nun an, dass x als Vorverarbeitungsschritt sortiert ist, wie @dhrummel in den Kommentaren vorschlägt. Beachten Sie, dass wir x und y nicht gleichzeitig sortieren können, da sie nicht unabhängig sind (wir wollen die gleichen Punkte erhalten).

%Vor%

Auch hier ist das Bild falsch, weil y nicht sortiert sind (in jeder Spalte), so wie sie waren, wenn wir ein rechteckiges Gitter anstatt einiger zufälliger Punkte hätten.

    
Ilya V. Schurov 04.02.2017 22:38
quelle
0

Der Grund für X und Y, um 2D zu sein, ist der folgende. Z passt zu jeder (x, y) -Koordinate im Achsensystem eine entsprechende "Tiefe" an, um ein 3D-Diagramm mit x-, y- und z-Koordinaten zu erstellen.

Nehmen wir nun an, wir wollen auf einen beliebigen Punkt innerhalb des Achsensystems zeigen. Dies können wir tun, indem wir die x- und y-Koordinaten (x, y) für diesen Punkt angeben. Zum Beispiel (0,0). Betrachte nun die "Linie" mit dem x-Wert 1. In dieser Zeile gibt es eine Anzahl von n y -Werten, die ungefähr so ​​aussehen:

Wenn wir diese Zeilen für alle x-Werte und y-Werte plotten, erhalten wir smth. wie:

Wie Sie sehen können, haben wir eine 2D Annotation, die aus 2 2D Arrays besteht, eines für die x Werte mit der Form:

%Vor%

und eins für die y-Werte mit der Form:

%Vor%

Diese beiden zusammen ergeben die (x, y) Koordinaten für jeden Punkt innerhalb des Koordinatensystems. Jetzt können wir für jeden Punkt eine Kurve zeichnen, die "Tiefe" bedeutet den Z-Wert (z-Koordinate). Jetzt ist es auch offensichtlich, warum die Z-Variable mit der Form (len (x), len (y)) zweidimensional sein muss, weil sie sonst keinen Wert für alle Punkte liefern kann.

Dieses Verhalten kann entweder durch Bereitstellen von 2D-x-, y- und z-Feldern für die Funktion OR realisiert werden: Bereitstellen von 1D x- und y-Feldern für die Funktion und das interne Erzeugen der 2D-Vernetzung aus den x- und y-Werten mit etw. wie X, Y = np.meshgrid (x, y) aber trotzdem muss z zweidimensional sein.

    
2Obe 18.07.2017 14:02
quelle

Tags und Links