web-dev-qa-db-ja.com

なぜpyplot.contour()はZを2D配列にする必要があるのですか?

matplotlib.pyplot.contour()関数は、3つの入力配列XYおよびZを受け取ります。
配列XYは点の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なし)のセマンティクスは何ですか?

13
dhrumeel

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に指定された同じランダムポイントと比較されます。

enter image description here

(これが この画像を生成するためのコード )です

_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配列が必要な理由です。

正しく言及したように、xyは1次元配列にすることができます。この場合、対応する2D配列はmeshgridで再構築されます。ただし、この場合はとにかく2d-arrayとしてzが必要です。

zのみが指定されている場合、xおよびyrangeの適切な長さです。

編集。 xおよびyが行うような方法で、2次元のzxおよび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)
_

Incorrect result

ご覧のとおり、(x、y、z)がランダムな点である場合、画像は正しいグラフに近く見えません。

ここで、@ dhrummelがコメントで示唆しているように、xが前処理ステップとしてソートされていると仮定します。 xyは独立していないため、同時にソートすることはできません(同じポイントを保持したい)。

_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

繰り返しますが、yは(すべての列で)並べ替えられていないため、ランダムな点ではなく長方形のグリッドがある場合とは異なり、画像は正しくありません。

2
Ilya V. Schurov

XとYが2Dになる理由は次のとおりです。 Zは、座標系の各(x、y)座標と対応する「深さ」を一致させて、x、y、z座標を持つ3Dプロットを作成します。

ここで、座標系内の任意の点を指すと仮定します。これを行うには、この点のx座標とy座標(x、y)を指定します(例:(0,0))。ここで、x値が1の「ライン」を考えます。このラインには、次のようなn個のy値がいくつかあります。

enter image description here

すべてのx値とy値についてこの線をプロットすると、smthが得られます。お気に入り:

enter image description here

ご覧のとおり、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次元でなければなりません。

0
2Obe

3次元グラフをプロットしたいとします。 xポイントのセットとyポイントのセットがあります。目標は、zxの各ペアに対して値yを生成することです。つまり、関数fを生成して、 zの値はz = f(x, y)になるようにします。

以下は良い例です(MathWorksから取得)。

surf

x座標とy座標は、それぞれ右下と左下にあります。関数fがあり、xyの各ペアに対して、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の値を生成するために必要なyzのペアを提供します。便宜上、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)]
_

xyの用語の空間配置を注意深く確認してください。 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()
_

我々が得る:

Contour

Zも2Dにすべきではないと思ったので、簡単に説明します。 contourf()は、1D X、Y、Z情報を持つ複数のポイントを使用するだけでなく、独自のスペースを構築するためのXとY、および完全なスペースを構築するための関係Z(X、Y)を必要とします。

0
Wey Shi