Python 为什么 pyplot.contour() 要求 Z 是一个二维数组?

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/42045921/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-19 21:09:46  来源:igfitidea点击:

Why does pyplot.contour() require Z to be a 2D array?

pythonnumpymatplotlibcontour

提问by dhrumeel

The matplotlib.pyplot.contour()function takes 3 input arrays X, Yand Z.
The arrays Xand Yspecify the x- and y-coordinates of points, while Zspecifies the corresponding value of the function of interest evaluated at the points.

所述matplotlib.pyplot.contour()函数接受3个输入阵列XYZ
数组XY指定点的 x 和 y 坐标,同时Z指定在点处评估的感兴趣函数的相应值。

I understand that np.meshgrid()makes it easy to produce arrays which serve as arguments to contour():

我知道这样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

This works fine. And conveniently, this works fine too:

这工作正常。方便的是,这也很好用:

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

However, why is the Zinput requiredto be a 2D-array?

但是,为什么Z输入必须是二维数组?

Why is something like the following disallowed, even though it specifies all the same data aligned appropriately?

为什么像下面这样的内容是不允许的,即使它指定了所有相同的数据适当对齐?

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

Also, what are the semantics when onlyZis specified (without the corresponding Xand Y)?

另外,仅指定时的语义Z是什么(没有相应的XY)?

回答by ImportanceOfBeingErnest

Looking at the documentation of contourone finds that there are a couple of ways to call this function, e.g. contour(Z)or contour(X,Y,Z). So you'll find that it does not require any Xor Yvalues to be present at all.

查看一个人的文档contour发现有几种方法可以调用这个函数,例如contour(Z)or contour(X,Y,Z)。所以,你会发现它不需要任何X或者Y价值能够体现的。

However in order to plot a contour, the underlying grid must be known to the function. Matplotlib's contouris based on a rectangular grid. But even so, allowing contour(z), with zbeing a 1D array, would make it impossible to know how the field should be plotted. In the case of contour(Z)where Zis a 2D array, its shape unambiguously sets the grid for the plot.

但是,为了绘制轮廓,函数必须知道底层网格。Matplotlibcontour基于矩形网格。但即便如此,允许contour(z)z作为一维数组,将无法知道应如何绘制该字段。在contour(Z)whereZ是二维数组的情况下,它的形状明确地设置了绘图的网格。

Once that grid is known, it is rather unimportant whether optional Xand Yarrays are flattened or not; which is actually what the documentation tells us:

一旦知道该网格,则 optionalXY数组是否被展平就变得不重要了。这实际上是文档告诉我们的:

X and Y must both be 2-D with the same shape as Z, or they must both be 1-D such that len(X) is the number of columns in Z and len(Y) is the number of rows in Z.

X 和 Y 必须都是与 Z 形状相同的二维,或者它们都必须是一维的,这样 len(X) 是 Z 中的列数,len(Y) 是 Z 中的行数。

It is also pretty obvious that someting like plt.contour(X_grid.ravel(), Y_grid.ravel(), Z_grid.ravel())cannot produce a contour plot, because all the information about the grid shape is lost and there is no way the contour function could know how to interprete the data. E.g. if len(Z_grid.ravel()) == 12, the underlying grid's shape could be any of (1,12), (2,6), (3,4), (4,3), (6,2), (12,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)

A possible way out could of course be to allow for 1D arrays and introduce an argument shape, like plt.contour(x,y,z, shape=(6,2)). This however is not the case, so you have to live with the fact that Zneeds to be 2D.

一个可能的出路当然是允许一维数组并引入一个参数shape,比如plt.contour(x,y,z, shape=(6,2)). 然而事实并非如此,因此您必须接受Z需要是 2D的事实。

However, if you are looking for a way to obtain a countour plot with flattened (ravelled) arrays, this is possible using plt.tricontour().

但是,如果您正在寻找一种方法来获得具有展平(散开)阵列的计数图,则可以使用plt.tricontour().

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

Here a triangular grid will be produced internally using a Delaunay Triangualation. Therefore even completely randomized points will produce a nice result, as can be seen in the following picture, where this is compared to the same random points given to contour.

这里将使用 Delaunay Triangualation 在内部生成一个三角形网格。因此,即使是完全随机的点也会产生很好的结果,如下图所示,将其与给定的相同随机点进行比较contour

enter image description here

在此处输入图片说明

(Here is the code to produce this picture)

(这是生成这张图片代码

回答by Ilya V. Schurov

The actual code of an algorithm behind plt.contourcan be found in _countour.cpp. It is rather complicated C-code, so it is difficult to follow it precisely, but if I were trying to make some contours-generating code I would do it in the following way. Pick some point (x, y)at the border and fix its z-value. Iterate over nearby points and pick that one for which z-value is the closest to z-value of the first point. Continue iteration for new point, pick nearby point with the z-value closest to the desired (but check that you do not return to a point you just visited, so you have to go in some "direction"), and continue until you get a cycle or reach some border.

背后算法的实际代码plt.contour可以在_countour.cpp 中找到。它是相当复杂的 C 代码,因此很难精确地遵循它,但是如果我尝试制作一些轮廓生成代码,我会按照以下方式进行。(x, y)在边界上选择一些点并修复它的z值。迭代附近的点并选择 z 值最接近第一个点的 z 值的点。继续迭代新点,选择 z 值最接近期望值的附近点(但检查您没有返回到您刚刚访问过的点,因此您必须朝某个“方向”前进),并继续直到您得到一个循环或到达某个边界。

It seems that something close (but a bit more complex) is implemented in _counter.cpp.

似乎在_counter.cpp.

As you see from the informal description of the algorithm, to proceed you have to find a point which is "nearby" to the current one. It is easy to do if you have a rectangular grid of points (need about 4 or 8 iterations like this: (x[i+1][j], y[i+1][j]), (x[i][j+1], y[i][j+1]), (x[i-1][j], y[i-1][j])and so on). But if you have some randomly selected points (without any particular order), this problem becomes difficult: you have to iterate over all the points you have to find nearby ones and make the next step. The complexityof such step isO(n), where nis a number of points (typically a square of a size of a picture). So an algorithm becomes much slower if you don't have a rectangular grid.

正如您从算法的非正式描述中看到的那样,要继续,您必须找到一个与当前点“附近”的点。如果您有一个矩形的点网格(需要大约 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])等等),这很容易做到。但是如果你有一些随机选择的点(没有任何特定的顺序),这个问题就变得很困难:你必须遍历所有点,找到附近的点并进行下一步。该步骤的复杂度O(n),其中n为点数(通常为图片大小的正方形)。因此,如果您没有矩形网格,算法会变得更慢。

This is why you actually need three 2d-arrays that correpsponds to x's, y's and z's of some points located over some rectangular grid.

这就是为什么您实际上需要三个二维数组,它们对应于位于某个矩形网格上的某些点的 x、y 和 z。

As you correctly mention, x's and y's can be 1d-arrays. In this case, the corresponding 2d-arrays are reconstructed with meshgrid. However, in this case you have to have zas 2d-array anyway.

正如您正确提到的,x's 和y's 可以是一维数组。在这种情况下,相应的 2d 阵列用 重建meshgrid。但是,在这种情况下,无论如何您都必须拥有z二维数组。

If only zis specified, xand yare range's of appropriate lengths.

如果 onlyz被指定,x并且yrange适当长度的。

EDIT. You can try to "fake" two-dimensional x, yand zarrays in such a way that xand ydoes not form a rectangular grid to check if my assumptions are correct.

编辑。您可以尝试“假”二维xyz阵列,这样的方式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)

Incorrect result

结果不正确

As you see, the picture does not look like anything close to the correct graph if (x, y, z)'s are just some random points.

如您所见,如果 (x, y, z) 只是一些随机点,则图片看起来与正确的图形不太相似。

Now let us assume that xis sorted as a preprocessing step as @dhrummel suggests in the comments. Note that we can't sort xand ysimultaniously as they are not independent (we want to preserve the same points).

现在让我们假设x按照@dhrummel 在评论中的建议将其分类为预处理步骤。请注意,我们不能同时排序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)

x is sorted now

x 现在已排序

Again, the picture is incorrect, due to the fact that y's are not sorted (in every column) as they were if we had rectangular grid instead of some random points.

同样,图片是不正确的,因为y's 没有排序(在每一列中),如果我们有矩形网格而不是一些随机点。

回答by 2Obe

The reason for X and Y to be 2D is the following. Z matches to each (x,y) coordinate in the axes system a corresponding "depth" to create a 3D plot with x,y,and z coordinates.

X 和 Y 为 2D 的原因如下。Z 与轴系统中的每个 (x,y) 坐标匹配相应的“深度”,以创建具有 x、y 和 z 坐标的 3D 绘图。

Now assume we want to point on a arbitrary point within the axes system. We can do that by providing the x and y coordinates (x,y) for this point.For example (0,0). Now consider the "line" with the x value 1. On this line there is a number of n y values, which looks smth like:

现在假设我们要指向轴系统内的任意点。我们可以通过提供该点的 x 和 y 坐标 (x,y) 来实现。例如 (0,0)。现在考虑 x 值为 1 的“线”。在这条线上有许多 ny 值,看起来像:

enter image description here

在此处输入图片说明

If we plot this lines for all x values and y values we will get smth. like:

如果我们为所有 x 值和 y 值绘制这条线,我们将得到 smth。喜欢:

enter image description here

在此处输入图片说明

As you can see we have a 2D annotation which is consisting of 2 2Darrays, one for the x values which has the shape:

如您所见,我们有一个由2 个 2D数组组成的 2D 注释,其中一个用于 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

and one for the y values which has the shape:

一个用于具有以下形状的 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

Those two together provides the (x,y) coordinates for each point within the coordinate system. Now we can plot for each point the "depth" means the Z value (z coordinate). Now it is also obvious why the Z variable must be 2 dimensional with the shape (len(x),len(y)) because otherwise it can't provide a value for all points.

这两者一起为坐标系中的每个点提供 (x,y) 坐标。现在我们可以为每个点绘制“深度”表示 Z 值(z 坐标)。现在也很明显为什么 Z 变量必须是具有形状 (len(x),len(y)) 的二维,否则它无法为所有点提供值。

This behaviour can be realised by either providing 2D x,y, and z arrays to the function OR: provide 1D x and y arrays to the function and the function internally creates the 2D mesh from the x and y values with smth. like X,Y=np.meshgrid(x,y) but nevertheless z must be two dimensional.

这种行为可以通过向函数提供 2D x、y 和 z 数组来实现,或者:向函数提供 1D x 和 y 数组,函数在内部使用 smth 从 x 和 y 值创建 2D 网格。像 X,Y=np.meshgrid(x,y) 但 z 必须是二维的。

回答by rayryeng

Imagine that you want to plot a three-dimensional graph. You have a set of xpoints and a set of ypoints. The goal is to produce a value zfor each pair of xand y, or in other words you need a function fsuch that it generates a value of zso that z = f(x, y).

想象一下,您要绘制一个三维图形。你有一组x点和一组y点。目标是z为每对xand生成一个值y,或者换句话说,您需要一个函数f,以便它生成一个值zso that z = f(x, y)

Here's a good example (taken from MathWorks):

这是一个很好的例子(取自 MathWorks):

surf

冲浪

The xand ycoordinates are at the bottom right and bottom left respectively. You will have a function fsuch that for each pair of xand y, we generate a zvalue. Therefore, in the code you have provide, the numpy.meshgridcall will generate two 2D arrays such that for each unique spatial location, we will observe the xand yvalue that are unique to that location.

xy坐标分别处于左底部右侧和底部。您将拥有一个函数f,对于每对xand y,我们都会生成一个z值。因此,在您提供的代码中,该numpy.meshgrid调用将生成两个二维数组,以便对于每个唯一的空间位置,我们将观察该位置唯一的xy值。

For example, let's use a very small example:

例如,让我们使用一个非常小的例子:

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.]])

Take a look at row number 2 and column number 1 for example (I'm starting indexing at 0 btw). This means at this spatial location, we will have coordinate x = 0.and y = 1. numpy.meshgridgives us the xand ypair that is required to generate the value of zat that particular coordinate. It's just split up into two 2D arrays for convenience.

例如,看看第 2 行和第 1 列(顺便说一下,我从 0 开始索引)。这意味着在这个空间位置,我们将有坐标x = 0.y = 1numpy.meshgrid为我们提供了在该特定坐标处生成 的值所需的xyz。为方便起见,它只是分成两个二维数组。

Now what to finally put in your zvariable is that it should use the function fand process what the output is for every value in xand its corresponding y.

现在最终要放入您的z变量的是它应该使用该函数f并处理输出中的每个值x及其对应的y.

Explicitly, you will need to formulate a zarray that is 2D such that:

明确地,您将需要制定一个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)]

Look very carefully at the spatial arrangement of xand yterms. We generate 9 unique values for each pair of xand yvalues. The xvalues span from -1 to 1 and the same for y. Once you generate this 2D array for z, you can use contourfto draw out level sets so that each contour line will give you the set of all possible xand yvalues that equal the same value of z. In addition, in between each adjacent pair of distinct lines, we fill in the area in between by the same colour.

仔细观察xy术语的空间排列。我们为每对xy值生成 9 个唯一值。的x值跨越从-1到1,并且在同一对y。一旦你为 生成了这个二维数组z,你就可以contourf用来绘制水平集,这样每条等高线都会为你提供所有可能的xy等于 的相同值的值的集合z。此外,在每对相邻的不同线条之间,我们用相同的颜色填充它们之间的区域。

Let's finish this off with an actual example. Suppose we have the function f(x, y) = exp(-(x**2 + y**2) / 10). This is a 2D Gaussian with a standard deviation of sqrt(5).

让我们用一个实际的例子来结束这个。假设我们有函数f(x, y) = exp(-(x**2 + y**2) / 10)。这是一个标准差为 的二维高斯sqrt(5)

Therefore, let's generate a grid of xand yvalues, use this to generate the zvalues and draw a contourfplot:

因此,让我们生成一个xy值的网格,使用它来生成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()

We get:

我们得到:

Contour

轮廓

回答by Wey Shi

Let me explain in a simple way, since I thought Z should not be 2D as well. contourf()needs X and Y to build its own space, and the relation Z(X,Y) to build a complete space, rather than merely using several points with 1D X,Y,Z information.

让我用简单的方式解释一下,因为我认为 Z 也不应该是 2D 的。contourf()需要X和Y来构建自己的空间,需要关系Z(X,Y)来构建一个完整的空间,而不是仅仅使用具有一维X,Y,Z信息的几个点。