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

新手上路,请多包涵

---- Y matplotlib.pyplot.contour() 函数占3个输入阵列 X 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 输入 必须 是二维数组?

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

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

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

原文由 dhrumeel 发布,翻译遵循 CC BY-SA 4.0 许可协议

阅读 1.6k
2 个回答

查看 contour 的文档, 发现有几种方法可以调用此函数,例如 contour(Z)contour(X,Y,Z) 因此,您会发现它根本不需要任何 XY 值。

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

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

X 和 Y 必须都是二维的,且形状与 Z 相同,或者它们必须都是一维的,这样 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) 中的任何一个。

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

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

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

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

在此处输入图像描述

(这是 生成这张图片的代码

原文由 ImportanceOfBeingErnest 发布,翻译遵循 CC BY-SA 4.0 许可协议

背后算法的实际代码 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]) 等等) .但是如果你有一些随机选择的点(没有任何特定的顺序),这个问题就变得困难了:你必须遍历所有你必须找到附近的点并进行下一步。该步骤的 复杂度 O(n) ,其中 n 是点的数量(通常是图片大小的正方形)。所以如果你没有矩形网格,算法会变得很慢。

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

正如您正确提到的那样, xy 可以是一维数组。在这种情况下,相应的二维数组用 meshgrid 重建。但是,在这种情况下,您必须将 z 作为二维数组。

如果仅 z 指定, x - range y

编辑。 You can try to “fake” two-dimensional x , y and z arrays in such a way that x and 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) 只是一些随机点,则图片看起来与正确的图形并不相似。

现在让我们假设 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 现在已排序

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

原文由 Ilya V. Schurov 发布,翻译遵循 CC BY-SA 3.0 许可协议

撰写回答
你尚未登录,登录后可以
  • 和开发者交流问题的细节
  • 关注并接收问题和回答的更新提醒
  • 参与内容的编辑和改进,让解决方法与时俱进
推荐问题