我发现您的“幼稚”方法存在两个问题。
首先,您通常不应将坐标数组X 和Y 设置为nan,而应仅设置要绘制的函数的值。大多数绘图函数(matplotlib 和其他函数)会自动将它们视为缺失值,用空白代替它们(另一方面,将坐标设置为 nan,可能会干扰涉及插值等的内部例程)。
但是,这仍然不适用于pcolor(mesh)。但这没关系,因为我也不同意你的说法,即它“是绘图的明显选择”。在我看来,pcolor(mesh) 最适合绘制矩阵。对于像你这样的不平凡的情节,像plt.contourf 这样的东西应该会产生奇迹。它还固有地包括插值,使您的情节更漂亮。它还按照我们的预期处理 nan 数据点:
n = 100
X, Y = np.meshgrid(np.linspace(1, 5, n), np.linspace(1, 5, n))
C = np.sin(X*Y)
C[50:60,:] = np.nan
C[:,50:60] = np.nan
fig, ax = plt.subplots()
n_levels = 100 # number of contour levels to plot
ax.contourf(X, Y, C, n_levels)
屏蔽前(左)和后(右)的结果:
请注意,contourf 代表“填充等高线图”,它通过计算输入数据集的水平曲线来工作。这意味着为了获得平滑漂亮的绘图,您需要密集的等高线,这就是为什么我选择 100 条线进行绘图。对于您的具体情况,您应该考虑使用 levels 关键字参数显式定义级别值。
更新:
在 cmets 中,您澄清了您的数据集是给定的,因此您还必须处理 X 和 Y 中的缺失值。这很难,因为您的输入网格中有孔,只有在您对问题的外观有非常准确的了解时才能弥补这一点。
在您的示例中,沿每个维度的坐标中都缺少完整区域。这是最好的方案,因为剩余的数据点可以通过meshgrid 调用生成,只是每个维度上的坐标向量更小。
在这个非常简单的情况下,您自己尝试了一个简单的补救措施:丢弃 nan 值。你几乎做对了,但是如果你取一个形状为(100,100) 的数组并从每个维度中切出 10-10,你最终会得到一个形状为(90,90) 而不是(81,100) 的数组。这就是为什么你的身材看起来如此跳跃的原因。如果你用适当的形状来做,结果会好得多:
n = 100
X, Y = np.meshgrid(np.linspace(1, 5, n), np.linspace(1, 5, n))
C = np.sin(X*Y)
X[50:60, :] = np.nan
X[:, 50:60] = np.nan
Y[50:60, :] = np.nan
Y[:, 50:60] = np.nan
C[50:60, :] = np.nan
C[:, 50:60] = np.nan
endshape = (90, 90) # needs to be known a priori!
inds = np.logical_not(np.isnan(X) | np.isnan(Y) | np.isnan(C))
X_plot = np.reshape(X[inds], endshape)
Y_plot = np.reshape(Y[inds], endshape)
C_plot = np.reshape(C[inds], endshape)
fig, ax = plt.subplots()
n_levels = 100 # number of contour levels to plot
ax.contourf(X_plot, Y_plot, C_plot, n_levels)
结果在缺失数据附近明显偏离:contourf(或pcolormesh,如果您使用它)执行的插值将尝试填补空白,从而扭曲您的数据。您可能会考虑在丢失的数据点上手动绘制一个白色补丁,但仍然会沿着边缘出现一些失真。请注意,我们必须自己了解缺失点的分布情况。
为了更简单和通用的解决方案,我会尝试猜测底层网格。我的意思是你应该获取出现在X 和Y 中的每个unique 值,并在这个完整的网格上重建你的函数。这是基于原始数据位于矩形网格上的弱得多的假设,但不需要其他假设。当数据中缺少完整的波段时,这在您的特定情况下不会有帮助,但如果您在数据中有 nan 补丁,它们会有所帮助。所以我也在为这个案例提供一个解决方案。
这是一个使用scipy.interpolate.griddata 重建网格的实现(使用插值可能有点矫枉过正,尤其是因为我们丢弃了部分结果,但另一种选择是循环整个数据集,而我没有喜欢这样做):
import numpy as np
import matplotlib.pyplot as plt
import scipy.interpolate as interp
n = 100
X, Y = np.meshgrid(np.linspace(1, 5, n), np.linspace(1, 5, n))
C = np.sin(X*Y)
# poke a hole into the data
X[40:60, 40:60] = np.nan
Y[40:60, 40:60] = np.nan
C[40:60, 40:60] = np.nan
# indices where nobody is nan
inds = np.logical_not(np.isnan(X) | np.isnan(Y) | np.isnan(C))
X_notnan = X[inds]
Y_notnan = Y[inds]
C_notnan = C[inds]
# construct new mesh
X_vals = np.unique(X[inds])
Y_vals = np.unique(Y[inds])
X_plot, Y_plot = np.meshgrid(X_vals, Y_vals)
# use nearest-neighbour interpolation to match the two meshes
C_plot = interp.griddata(np.array([X_notnan, Y_notnan]).T, C_notnan,
(X_plot, Y_plot), method='nearest')
# fill in the nans in C
C_plot[np.logical_not(inds)] = np.nan
fig, ax = plt.subplots()
n_levels = 100 # number of contour levels to plot
ax.contourf(X_plot, Y_plot, C_plot, n_levels)
如果没有nans 的缩减网格的尺寸小于原始网格,即如果数据中有nans 的完整行或列,则此解决方案将失效。但是,如果不是这种情况,那么它会给你一个像这样的漂亮结果:
这也意味着,如果您沿着一条线猜测原始问题中的 X 和 Y 值,例如通过知道两个网格是等距的,那么您可以修复第一行 X 和Y 的第一列,并使用上面的最新代码:它应该会为您生成完整的网格,产生类似于本文第一个图的结果。