matplotlib.pyplot.contour()
関数は、3つの入力配列X
、Y
およびZ
を受け取ります。
配列X
とY
は点のx座標とy座標を指定し、Z
は点で評価される対象の関数の対応する値を指定しますポイント。
np.meshgrid()
を使用すると、contour()
の引数として機能する配列を簡単に作成できることを理解しています。
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
これは正常に動作します。そして便利なことに、これもうまくいきます:
plt.contour(X, Y, Z_grid) # Works fine too
しかし、なぜZ
入力requiredが2D配列になるのですか?
同じデータがすべて適切に配置されているにもかかわらず、次のようなものが許可されないのはなぜですか?
plt.contour(X_grid.ravel(), Y_grid.ravel(), Z_grid.ravel()) # Disallowed
また、onlyZ
が指定されている場合(対応するX
およびY
なし)のセマンティクスは何ですか?
contour
のドキュメント を見ると、この関数を呼び出す方法がいくつかあることがわかります。 contour(Z)
またはcontour(X,Y,Z)
。したがって、X
またはY
の値をまったく必要としないことがわかります。
ただし、等高線をプロットするには、基になるグリッドが関数に認識されている必要があります。 Matplotlibのcontour
は、長方形グリッドに基づいています。それでも、z
を1D配列としてcontour(z)
を許可すると、フィールドのプロット方法を知ることができなくなります。 Z
が2D配列であるcontour(Z)
の場合、その形状は明確にプロットのグリッドを設定します。
そのグリッドがわかったら、オプションのX
およびY
配列がフラット化されているかどうかは、それほど重要ではありません。これは実際にドキュメントが私たちに伝えていることです:
XとYは両方ともZと同じ形状の2次元であるか、またはlen(X)がZの列数でlen(Y)がZの行数であるように、両方とも1次元でなければなりません。
グリッド形状に関するすべての情報が失われ、等高線関数がデータを解釈する方法を知ることができないため、plt.contour(X_grid.ravel(), Y_grid.ravel(), Z_grid.ravel())
のような何かが等高線図を生成できないことも明らかです。例えば。 len(Z_grid.ravel()) == 12
の場合、基になるグリッドの形状は_(1,12), (2,6), (3,4), (4,3), (6,2), (12,1)
_のいずれかになります。
もちろん、1D配列を許可し、shape
のように、plt.contour(x,y,z, shape=(6,2))
のように引数を導入することもできます。ただし、これは当てはまらないため、Z
は2Dである必要があるという事実に耐えなければなりません。
ただし、平坦化された(レーベル化された)配列でcountourプロットを取得する方法を探している場合、これは plt.tricontour()
を使用して可能です。
_plt.tricontour(X_grid.ravel(), Y_grid.ravel(), Z_grid.ravel())
_
ここでは、三角形のグリッドがDelaunay Triangualationを使用して内部的に生成されます。したがって、次の図に示すように、完全にランダム化されたポイントでもいい結果が得られます。これは、contour
に指定された同じランダムポイントと比較されます。
(これが この画像を生成するためのコード )です
_plt.contour
_の背後にあるアルゴリズムの実際のコードは、 _ countour.cpp にあります。これはかなり複雑なCコードなので、正確に追跡するのは困難ですが、輪郭生成コードを作成しようとすると、次のようになります。境界線のある点_(x, y)
_を選択し、そのz
- valueを修正します。近くのポイントを反復処理し、z値が最初のポイントのz値に最も近いものを選択します。新しい点の反復を続行し、目的の値に最も近いZ値で近くの点を選択します(ただし、直前に訪れた点に戻らないことを確認してください。そのため、「方向」に移動する必要があります)。サイクルまたは境界線に到達します。
__counter.cpp
_に近いもの(ただし、もう少し複雑)が実装されているようです。
アルゴリズムの非公式な説明からわかるように、続行するには、現在のポイントの「近く」にあるポイントを見つける必要があります。長方形の点グリッドがある場合は簡単です(このような約4または8回の反復が必要です:_(x[i+1][j], y[i+1][j])
_、_(x[i][j+1], y[i][j+1])
_、_(x[i-1][j], y[i-1][j])
_など)。しかし、ランダムに選択されたポイント(特定の順序なし)がある場合、この問題は困難になります。近くのポイントを見つけて次のステップを実行するために、すべてのポイントを反復処理する必要があります。 complexity そのようなステップの isO(n)
、ここでn
はポイントの数(通常は画像のサイズの2乗) )。したがって、長方形のグリッドがない場合、アルゴリズムは非常に遅くなります。
これが実際に、長方形グリッド上にあるいくつかのポイントのx、y、zに対応する3つの2D配列が必要な理由です。
正しく言及したように、x
とy
は1次元配列にすることができます。この場合、対応する2D配列はmeshgrid
で再構築されます。ただし、この場合はとにかく2d-arrayとしてz
が必要です。
z
のみが指定されている場合、x
およびy
はrange
の適切な長さです。
編集。 x
およびy
が行うような方法で、2次元のz
、x
およびy
配列を「偽造」しようとすることができます私の仮定が正しいかどうかを確認するために長方形のグリッドを形成しないでください。
_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)
_
ご覧のとおり、(x、y、z)がランダムな点である場合、画像は正しいグラフに近く見えません。
ここで、@ dhrummelがコメントで示唆しているように、x
が前処理ステップとしてソートされていると仮定します。 x
とy
は独立していないため、同時にソートすることはできません(同じポイントを保持したい)。
_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)
_
繰り返しますが、y
は(すべての列で)並べ替えられていないため、ランダムな点ではなく長方形のグリッドがある場合とは異なり、画像は正しくありません。
XとYが2Dになる理由は次のとおりです。 Zは、座標系の各(x、y)座標と対応する「深さ」を一致させて、x、y、z座標を持つ3Dプロットを作成します。
ここで、座標系内の任意の点を指すと仮定します。これを行うには、この点のx座標とy座標(x、y)を指定します(例:(0,0))。ここで、x値が1の「ライン」を考えます。このラインには、次のようなn個のy値がいくつかあります。
すべてのx値とy値についてこの線をプロットすると、smthが得られます。お気に入り:
ご覧のとおり、2 2D配列で構成される2Dアノテーションがあり、1つは形状を持つx値用です。
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
1つは次の形状を持つy値用です。
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
これら2つを合わせて、座標系内の各ポイントの(x、y)座標を提供します。これで、各点をプロットできます。「深さ」はZ値(z座標)を意味します。さて、Z変数が形状(len(x)、len(y))で2次元でなければならない理由も明らかです。それ以外の場合は、すべてのポイントに値を提供できません。
この動作は、2Dのx、y、z配列を関数に提供することで実現できますOR:1Dのx配列とy配列を関数に提供し、関数は内部的にxとyの値から2次元メッシュを作成します。 X、Y = np.meshgrid(x、y)と同様ですが、それでもzは2次元でなければなりません。
3次元グラフをプロットしたいとします。 x
ポイントのセットとy
ポイントのセットがあります。目標は、z
とx
の各ペアに対して値y
を生成することです。つまり、関数f
を生成して、 z
の値はz = f(x, y)
になるようにします。
以下は良い例です(MathWorksから取得)。
x
座標とy
座標は、それぞれ右下と左下にあります。関数f
があり、x
とy
の各ペアに対して、z
値が生成されます。したがって、提供したコードでは、_numpy.meshgrid
_呼び出しによって2つの2D配列が生成され、一意の空間位置ごとに、一意のx
およびy
値が観察されますその場所に。
たとえば、非常に小さな例を使用してみましょう。
_In [1]: import numpy as np
In [2]: x, y = np.meshgrid(np.linspace(-1, 1, 3), np.linspace(-1, 1, 3))
In [3]: x
Out[3]:
array([[-1., 0., 1.],
[-1., 0., 1.],
[-1., 0., 1.]])
In [4]: y
Out[4]:
array([[-1., -1., -1.],
[ 0., 0., 0.],
[ 1., 1., 1.]])
_
たとえば、行番号2と列番号1を見てみましょう(私は0 btwからインデックス作成を開始しています)。つまり、この空間位置では、_x = 0.
_と_y = 1
_が調整されます。 _numpy.meshgrid
_は、その特定の座標でx
の値を生成するために必要なy
とz
のペアを提供します。便宜上、2つの2D配列に分割されています。
z
変数に最終的に入れるのは、関数f
を使用し、x
のすべての値とそれに対応するy
。
明示的に、次のような2Dのz
配列を作成する必要があります。
_z = [f(-1, -1) f(0, -1) f(1, -1)]
[f(-1, 0) f(0, 0) f(1, 0)]
[f(-1, 1) f(0, 1) f(1, 1)]
_
x
とy
の用語の空間配置を注意深く確認してください。 x
値とy
値のペアごとに9つの一意の値を生成します。 x
の値の範囲は-1から1までで、y
も同じです。 z
のこの2D配列を生成したら、contourf
を使用してレベルセットを描画し、各等高線がすべての可能なx
およびy
の値は、z
の同じ値と同じです。また、隣接する異なる線のペアの間では、その間の領域を同じ色で塗りつぶします。
これを実際の例で終わりましょう。関数f(x, y) = exp(-(x**2 + y**2) / 10)
があるとします。これは、標準偏差がsqrt(5)
の2Dガウス分布です。
したがって、x
およびy
値のグリッドを生成し、これを使用してz
値を生成し、contourf
プロットを描画します。
_import numpy as np
import matplotlib.pyplot as plt
x = np.linspace(-1, 1, 101)
y = x
x, y = np.meshgrid(x, y)
z = np.exp(-(x**2 + y**2) / 10)
fig,ax2 = plt.subplots(1)
ax2.contourf(x,y,z)
plt.show()
_
我々が得る:
Zも2Dにすべきではないと思ったので、簡単に説明します。 contourf()
は、1D X、Y、Z情報を持つ複数のポイントを使用するだけでなく、独自のスペースを構築するためのXとY、および完全なスペースを構築するための関係Z(X、Y)を必要とします。