web-dev-qa-db-de.com

Warum erfordert pyplot.contour (), dass Z ein 2D-Array ist?

Die Funktion matplotlib.pyplot.contour() nimmt 3 Eingangsarrays X, Y und Z an.
Die Arrays X und Y geben die x- und y-Koordinaten von Punkten an, während Z den entsprechenden Wert der an den Punkten ausgewerteten Funktion von Interesse angibt.

Ich verstehe, dass np.meshgrid() es leicht macht, Arrays zu erstellen, die als Argumente für contour() dienen:

X = np.arange(0,5,0.01)
Y = np.arange(0,3,0.01)

X_grid, Y_grid = np.meshgrid(X,Y)
Z_grid = X_grid**2 + Y_grid**2

plt.contour(X_grid, Y_grid, Z_grid)  # Works fine

Das funktioniert gut. Und das funktioniert auch gut:

plt.contour(X, Y, Z_grid)  # Works fine too

Warum ist die Z-Eingaberequiredjedoch ein 2D-Array? 

Warum ist so etwas wie das Folgende nicht zulässig, obwohl alle Daten in gleicher Weise ausgerichtet sind?

plt.contour(X_grid.ravel(), Y_grid.ravel(), Z_grid.ravel())  # Disallowed

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

9
dhrumeel

In der Dokumentation von contour sieht man, dass es mehrere Möglichkeiten gibt, diese Funktion aufzurufen, z. contour(Z) oder contour(X,Y,Z). Sie werden also feststellen, dass keine X- oder Y-Werte vorhanden sind.

Um jedoch eine Kontur zeichnen zu können, muss das darunterliegende Raster der Funktion bekannt sein. Matplotlibs contour basiert auf einem rechteckigen Gitter. Wenn Sie jedoch contour(z) zulassen, wobei z ein 1D-Array ist, ist es unmöglich zu wissen, wie das Feld gezeichnet werden soll. Im Fall von contour(Z), bei dem Z ein 2D-Array ist, wird durch seine Form das Raster für das Diagramm eindeutig festgelegt. 

Sobald dieses Raster bekannt ist, ist es ziemlich unwichtig, ob die optionalen Arrays X und Y abgeflacht sind oder nicht. Das sagt uns eigentlich die Dokumentation:

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

Es ist auch ziemlich offensichtlich, dass so etwas wie plt.contour(X_grid.ravel(), Y_grid.ravel(), Z_grid.ravel()) keine Konturdarstellung erzeugen kann, da alle Informationen über die Gitterform verloren gehen und die Konturfunktion keine Möglichkeit hat, die Daten zu interpretieren. Z.B. Wenn len(Z_grid.ravel()) == 12, könnte die Form des zugrundeliegenden Gitters (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, ein Countour-Diagramm mit abgeflachten Arrays zu erstellen, ist dies mit plt.tricontour() möglich.

plt.tricontour(X_grid.ravel(), Y_grid.ravel(), Z_grid.ravel()) 

Hier wird ein dreieckiges Gitter intern mit einer Delaunay-Triangualation erzeugt. Daher erzeugen auch vollständig randomisierte Punkte ein Nizza-Ergebnis, wie im folgenden Bild zu sehen ist, wo dieses mit den gleichen zufälligen Punkten verglichen wird, die für contour angegeben wurden.

 enter image description here

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

Den eigentlichen Code eines Algorithmus hinter plt.contour finden Sie in _countour.cpp . Es ist ein ziemlich komplizierter C-Code, daher ist es schwierig, ihm genau zu folgen, aber wenn ich versuchen würde, Code zu erzeugen, der Konturen erzeugt, würde ich das auf folgende Weise tun. Wählen Sie einen Punkt (x, y) am Rand und korrigieren Sie den z- Wert. Iterieren Sie über nahe gelegene Punkte und wählen Sie den Punkt aus, für den der Z-Wert dem Z-Wert des ersten Punktes am nächsten kommt. Setzen Sie die Iteration für einen neuen Punkt fort, wählen Sie den nächstgelegenen Punkt mit dem Z-Wert, der dem gewünschten Wert am nächsten ist (überprüfen Sie jedoch, dass Sie nicht zu einem Punkt zurückkehren, den Sie gerade besucht haben, sodass Sie in eine "Richtung" gehen müssen), und fahren Sie fort, bis Sie den Punkt erreichen einen Zyklus oder eine Grenze erreichen.

In _counter.cpp scheint etwas Nahes (etwas komplexeres) implementiert zu sein.

Wie Sie der informellen Beschreibung des Algorithmus entnehmen können, müssen Sie zum Fortfahren einen Punkt finden, der dem aktuellen Punkt "nahe" ist. Es ist einfach, wenn Sie ein rechteckiges Punktegitter haben (ungefähr 4 oder 8 Iterationen wie folgt: (x[i+1][j], y[i+1][j]), (x[i][j+1], y[i][j+1]), (x[i-1][j], y[i-1][j]) usw.). Wenn Sie jedoch zufällig ausgewählte Punkte (ohne bestimmte Reihenfolge) haben, wird dieses Problem schwierig: Sie müssen alle Punkte durchlaufen, die Sie in der Nähe finden müssen, um den nächsten Schritt zu finden. Die Komplexität eines solchen Schritts istO(n), wobei n eine Anzahl von Punkten ist (normalerweise ein Quadrat einer Bildgröße). Ein Algorithmus wird also viel langsamer, wenn Sie kein rechteckiges Gitter haben.

Aus diesem Grund benötigen Sie tatsächlich drei 2d-Arrays, die den x-, y- und z-Werten einiger Punkte entsprechen, die sich über einem rechteckigen Raster befinden.

Wie Sie richtig sagen, 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 z trotzdem als 2d-Array verwenden.

Wenn nur z angegeben wird, sind x und yranges geeigneter Länge.

BEARBEITEN. Sie können versuchen, zweidimensionale x-, y- und z-Arrays so zu "fälschen", dass x und y kein rechteckiges Raster bilden, um zu überprüfen, ob meine Annahmen korrekt sind.

import matplotlib.pyplot as plt
import numpy as np
%matplotlib inline

x = np.random.uniform(-3, 3, size=10000)
y = np.random.uniform(-3, 3, size=10000)
z = x**2 + y**2
X, Y, Z = (u.reshape(100, 100) for u in (x, y, z))
plt.contour(X, Y, Z)

 Incorrect result

Wie Sie sehen, sieht das Bild nicht nach etwas aus, das dem korrekten Diagramm nahe kommt, 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 möchten die gleichen Punkte beibehalten).

x = np.random.uniform(-3, 3, size=10000)
y = np.random.uniform(-3, 3, size=10000)
z = x**2 + y**2
xyz = np.array([x, y, z]).T
x, y, z = xyz[xyz[:, 0].argsort()].T
assert (x == np.sort(x)).all()
X, Y, Z = (u.reshape(100, 100) for u in (x, y, z))
plt.contour(X, Y, Z)

 x is sorted now

Wieder ist das Bild falsch, da y (s) nicht in jeder Spalte sortiert sind, als wären wir ein rechteckiges Raster anstelle einiger zufälliger Punkte.

2
Ilya V. Schurov

Der Grund dafür, dass X und Y 2D sind, ist der folgende .__ Z stimmt mit jeder (x, y) -Koordinate im Achsensystem eine entsprechende "Tiefe" überein, um einen 3D-Plot 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. Beispiel (0,0). Betrachten Sie nun die "Zeile" mit dem x-Wert 1. In dieser Zeile befinden sich eine Anzahl von y-Werten, die wie folgt aussehen:

 enter image description here

Wenn wir diese Linien für alle x- und y-Werte zeichnen, erhalten wir smth. mögen:

 enter image description here

Wie Sie sehen, haben wir eine 2D-Annotation, die aus 2 2D -Arrays besteht, eines für die x-Werte, die die Form haben:

1 2 3 4 5 6 7 8 9 10
1 2 3 4 5 6 7 8 9 10
1 2 3 4 5 6 7 8 9 10
1 2 3 4 5 6 7 8 9 10
1 2 3 4 5 6 7 8 9 10
1 2 3 4 5 6 7 8 9 10
1 2 3 4 5 6 7 8 9 10
1 2 3 4 5 6 7 8 9 10
1 2 3 4 5 6 7 8 9 10
1 2 3 4 5 6 7 8 9 10
#--> Two dimensional x values array

und eine für die y-Werte, die die Form hat:

10 10 10 10 10 10 10 10 10 10
9 9 9 9 9 9 9 9 9 9 
8 8 8 8 8 8 8 8 8 8
...
1 1 1 1 1 1 1 1 1 1
0 0 0 0 0 0 0 0 0 0
#--> Two dimensional y values array

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

Dieses Verhalten kann verwirklicht werden, indem entweder 2D-x-, y- und z-Arrays für die Funktion OR bereitgestellt werden: Stellen Sie der Funktion 1D x- und y-Arrays bereit, und die Funktion erstellt intern das 2D-Netz aus den x- und y-Werten mit smth. wie X, Y = np.meshgrid (x, y), aber trotzdem muss z zweidimensional sein.

0
2Obe

Lassen Sie mich das auf einfache Weise erklären, da ich dachte, Z sollte auch nicht 2D sein. contourf() benötigt X und Y, um einen eigenen Raum aufzubauen, und die Beziehung Z (X, Y), um einen vollständigen Raum aufzubauen, anstatt nur mehrere Punkte mit 1D X, Y, Z-Informationen zu verwenden.

0
Wey Shi