【问题标题】:Dealing with masked coordinate arrays in pcolormesh处理 pcolormesh 中的蒙面坐标数组
【发布时间】:2021-02-07 03:18:08
【问题描述】:

我正在对一些气候模型输出进行可视化。计算是在投影的纬度/经度网格上完成的。由于模型模拟海冰,所有陆地网格单元都被遮盖。在 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)

现在想象我们有一个面具:

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)

像许多幼稚的方法一样,这是行不通的。

理想情况下,生成的图在掩码所在的位置是空白的。如何做到这一点?

【问题讨论】:

    标签: python numpy matplotlib plot


    【解决方案1】:

    我发现您的“幼稚”方法存在两个问题。

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

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

    请注意,contourf 代表“填充等高线图”,它通过计算输入数据集的水平曲线来工作。这意味着为了获得平滑漂亮的绘图,您需要密集的等高线,这就是为什么我选择 100 条线进行绘图。对于您的具体情况,您应该考虑使用 levels 关键字参数显式定义级别值。


    更新:

    在 cmets 中,您澄清了您的数据集是给定的,因此您还必须处理 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)
    

    结果在缺失数据附近明显偏离:contourf(或pcolormesh,如果您使用它)执行的插值将尝试填补空白,从而扭曲您的数据。您可能会考虑在丢失的数据点上手动绘制一个白色补丁,但仍然会沿着边缘出现一些失真。请注意,我们必须自己了解缺失点的分布情况。

    为了更简单和通用的解决方案,我会尝试猜测底层网格。我的意思是你应该获取出现在XY 中的每个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 的完整行或列,则此解决方案将失效。但是,如果不是这种情况,那么它会给你一个像这样的漂亮结果:

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

    【讨论】:

    • 我明白你想要contourf 而不是pcolormesh 的意思。问题是我无法选择让 X/Y 网格中的 nan 消失 - 我正在使用现有数据集,而不是我自己创建的数据集。 contourfpcolormesh 有相同的问题,如果坐标网格中有掩码值,则会失败。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2021-02-16
    • 1970-01-01
    • 2021-10-06
    • 1970-01-01
    • 2013-01-10
    • 2023-02-09
    • 1970-01-01
    相关资源
    最近更新 更多