使用经纬度绘制海洋数据地图时,避免插值致陆地显数据的方法
解决CMIP6海洋数据绘图时陆地区域被插值填充的问题
这是CMIP6海洋数据绘图的典型坑——多数海洋模式采用不规则的二维海洋网格(不是咱们熟悉的规则经纬度网格),当你用x='lon', y='lat'调用xarray的plot方法时,matplotlib会默认把这些二维坐标当成规则网格处理,自动插值填充了本该是NaN的陆地区域。结合你的代码,给你几个针对性的解决方案:
1. 先确认数据里的陆地确实是NaN
CMIP6海洋数据一般默认把陆地区域设为NaN,但个别情况可能用了其他填充值(比如0),先检查一下:
# 看看有多少NaN值(对应陆地) print(sub2.isnull().sum()) # 如果发现有非NaN的填充值,替换成NaN(比如填充值是0的话) sub2 = sub2.where(sub2 != 0)
2. 用pcolormesh直接传入二维坐标,关闭插值
这是最有效的解决办法,核心是让绘图函数知道这是不规则网格,别瞎插值。修改你的绘图代码:
ax = plt.axes(projection=ccrs.Robinson()) # 直接用matplotlib的pcolormesh,传入二维的lon、lat和数据 q = ax.pcolormesh( ds.lon, ds.lat, sub2, transform=ccrs.PlateCarree(), vmin=0, vmax=0.4 ) # 也可以用xarray的plot.pcolormesh,注意直接传二维数组而非字符串 # q = sub2.plot.pcolormesh( # ax=ax, x=ds.lon, y=ds.lat, # transform=ccrs.PlateCarree(), # vmin=0, vmax=0.4, # cbar_kwargs={'shrink': 0.5}, # shading='flat' # 关闭插值 # ) # 添加颜色条 plt.colorbar(q, shrink=0.5) ax.set_global(); ax.coastlines();
这里的关键是别用'lon'/'lat'字符串当x/y参数,直接传二维的ds.lon和ds.lat,这样matplotlib就不会把它当成规则网格插值了。
3. 处理跨0度经线的经度(可选)
如果你的经度范围是0-360度,可能会在0度经线附近出现奇怪的断裂,建议转成-180到180度的范围:
# 转换经度范围 ds['lon'] = xr.where(ds.lon > 180, ds.lon - 360, ds.lon) # 重新提取需要的数据 sub2 = ds.o2.sel(member_id=ds.member_id.values[0], time=ds.time.values[0]).sel(lev=100, method='nearest')
4. 用contour/contourf的话,记得屏蔽NaN区域
如果非要用等高线绘图,得给contourf加个mask参数,让它跳过NaN区域:
ax = plt.axes(projection=ccrs.Robinson()) q = ax.contourf( ds.lon, ds.lat, sub2, transform=ccrs.PlateCarree(), vmin=0, vmax=0.4, mask=sub2.isnull() # 告诉它别画NaN的区域 ) ax.set_global(); ax.coastlines(); plt.colorbar(q, shrink=0.5)
这样等高线就只会在海洋区域绘制,陆地部分保持空白,配合cartopy的海岸线就对了。
内容的提问来源于stack exchange,提问作者FunkySheep




