处理pcolormesh

时间:2016-03-25 21:31:07

标签: python numpy matplotlib plot

我正在研究一些气候模型输出的可视化。计算在投影的纬度/经度网格上完成。由于模型模拟海冰,所有陆地网格单元都被掩盖。在Python中绘制地理信息的标准工具是Basemap和Cartopy,它们都使用matplotlib例程。特别是pcolormesh是绘图的明显选择。如果没有地面掩护,那很简单:

X = longitude
Y = latitude
C = variable

fig, ax = plt.subplots()
plt.pcolormesh(X,Y,C)

虽然允许C成为屏蔽数组,但pcolormesh无法处理XY上的屏蔽数组。那我怎么能解决这个问题呢?

举一个简单的例子,请:

n = 100
X,Y = np.meshgrid(np.linspace(1,5,n),np.linspace(1,5,n))
C = np.sin(X*Y)
fig, ax = plt.subplots()
plt.pcolormesh(X,Y,C)

color plot of C

现在假设我们有一个面具:

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

我必须解决的第一个想法是只选择有效的条目并重新塑造XYC

M = np.isnan(X)
X_valid = X[~M]
Y_valid = Y[~M]
C_valid = C[~M]
X_valid.shape = (81,100)
Y_valid.shape = (81,100)
C_valid.shape = (81,100)
plt.pcolormesh(X_valid, Y_valid, C_valid)

color sin plot, screwy 像许多天真的方法一样,这不起作用。

理想情况下,结果图在面具所在的位置只是空白。怎么办呢?

1 个答案:

答案 0 :(得分:2)

我看到你的“天真”方法存在两个问题。

首先,您通常不应将坐标数组XY设置为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)

掩蔽之前(左)和之后(右)的结果:

before after

请注意contourf代表“填充轮廓图”,并通过计算输入数据集的水平曲线来工作。这意味着为了获得平滑而美观的情节,你需要密集的轮廓线,这就是为什么我选择了100行绘图。对于您的特定情况,您应该考虑使用levels关键字参数明确定义级别值。

更新

在评论中,您澄清了您的数据集是给定的,因此您还必须处理XY中的缺失值。这很难,因为你的输入网格中有洞,如果你对问题的看法非常精确,那么你只能弥补这一点。

在您的示例中,每个维度的坐标中都缺少完整区域。这是最好的方案,因为剩余的数据点可能是由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)

result from naive method

结果明显偏离缺失的数据:contourf(或pcolormesh如果您使用此插值)执行的插值将尝试填补空白,扭曲您的数据。您可以考虑在缺失的数据点上手动绘制白色色块,但仍然会在边缘处产生一些失真。请注意,我们必须了解丢失点的分布情况。

对于更加傻瓜式和通用的解决方案,我会尝试猜测底层网格。我的意思是,您应该获取uniqueX中出现的每个Y值,并在此完整网格上重建您的函数。这是基于原始数据位于矩形网格上的较弱假设,但不需要其他假设。如果数据中缺少完整波段,这对您的特定情况没有帮助,但如果数据中有nans补丁,它们将有所帮助。所以我给出了像这种情况的解决方案。为了你的

这是使用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)

如果没有nan s的缩小网格尺寸小于原始尺寸,即如果数据中有nan个完整行或列,则此解决方案将会中断。但是,如果不是这种情况,那么它会给你一个漂亮的结果:

final result

这也意味着如果您猜测原始问题中的XY值沿着一条线,例如通过知道两个网格是等距的,那么您可以修复第一行XY的第一列,并使用上面的最新代码:它应该为您生成完整的网格,产生类似于此帖子中第一个数字的结果。