为什么pyplot.contour()要求Z是2D数组?

时间:2017-02-04 21:40:19

标签: python numpy matplotlib contour

matplotlib.pyplot.contour()函数需要3个输入数组XYZ
数组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输入 需要 是2D数组?

为什么不允许使用以下内容,即使它指定了相应的所有相同数据?

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

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

6 个答案:

答案 0 :(得分:6)

查看the documentation of contour,我发现有几种方法可以调用此功能,例如contour(Z)contour(X,Y,Z)。因此,您发现它根本不需要任何XY值。

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

一旦知道该网格,可选的XY数组是否被展平是相当不重要的;这实际上是文档告诉我们的:

  

X和Y必须都是2-D,形状与Z相同,或者它们必须都是1-D,因此len(X)是Z中的列数,len(Y)是行数在Z。

很明显,有些像 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的事实。

但是,如果您正在寻找一种方法来获得带有扁平(ravelled)数组的countour图,可以使用plt.tricontour()

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

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

enter image description here

(这是code to produce this picture

答案 1 :(得分:2)

plt.contour后面的算法的实际代码可以在_countour.cpp中找到。它是相当复杂的C代码,因此很难准确地遵循它,但如果我试图制作一些轮廓生成代码,我会按照以下方式进行。在边框处选择一些点(x, y)并修复其z - 值。迭代附近的点并选择那个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 O(n)步骤的is,其中n是多个点(通常是图片大小的正方形)。因此,如果没有矩形网格,算法会变慢。

这就是为什么你实际上需要三个2d阵列,它们对应于位于某个矩形网格上的某些点的x,y和z。

正如您所正确提到的,xy可以是1d阵列。在这种情况下,使用meshgrid重建相应的2d阵列。但是,在这种情况下,无论如何都必须将z作为2d数组。

如果仅指定了z,则xy的{​​{1}}长度为range

EDIT。您可以尝试“伪造”二维xyz数组,使xy不形成矩形网格检查我的假设是否正确。

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)只是一些随机点,那么图片看起来并不像正确的图形。

现在让我们假设x被排序为@dhrummel在评论中建议的预处理步骤。请注意,我们无法同时对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 :(得分:1)

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

正如您所看到的,我们有一个2D注释,它包含 2个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

和一个具有形状的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

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

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

答案 3 :(得分:0)

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

答案 4 :(得分:0)

想象一下,您想绘制一个三维图形。您有一组x点和一组y点。目标是为每对zx生成一个值y,或者换句话说,您需要一个函数f以便它生成{{ 1}},这样z

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

surf

z = f(x, y)x坐标分别在右下角和左下角。您将拥有一个函数y,这样对于fx的每一对,我们都会生成一个y值。因此,在您提供的代码中,z调用将生成两个2D数组,以便对于每个唯一的空间位置,我们将观察到唯一的numpy.meshgridx值位置。

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

y

例如,以第2行和第1列为例(我将从0 btw开始索引)。这意味着在这个空间位置,我们将拥有坐标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.]]) x = 0.y = 1为我们提供了在特定坐标处生成numpy.meshgrid值所需的xy对。为了方便起见,它仅分成两个2D阵列。

现在,最后要放入z变量的是它应该使用函数z并处理f及其对应的{{1}中每个值的输出}。

明确地,您将需要制定一个二维x数组,使得:

y

非常仔细地查看zz = [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值生成9个唯一值。 x的值从-1到1,与y相同。为x生成此2D数组后,可以使用y绘制级别集,以便每个轮廓线将为您提供所有可能的zcontourf的集合等于x的相同值。另外,在每对相邻的不同线之间,我们用相同的颜色填充它们之间的区域。

让我们以一个实际的例子来结束。假设我们有函数y。这是2D高斯,标准偏差为z

因此,让我们生成一个f(x, y) = exp(-(x**2 + y**2) / 10)sqrt(5)值的网格,使用它来生成x值并绘制y图:

z

我们得到:

Contour

答案 5 :(得分:-2)

因为 Z 中的每个 z 都与 X 中的一个 x 和 Y 中的一个 y 相关联。如果 Z 是一维的,matplotlib 将不知道哪个 x、y 与每个 z 相关联并且无法绘制。