如何使用三次插值将含NaN的二维网格数据插值到非结构化坐标?
问题根源分析
RegularGridInterpolatorcubic报错原因:三次插值依赖样条拟合(make_interp_spline),样条算法要求输入数据完全无NaN;而线性插值是基于局部邻点的计算,遇到邻点含NaN时直接返回NaN,因此两者对NaN的兼容性完全不同。interp2d使用错误:interp2d设计目标是生成网格插值结果,若要得到非结构化点的插值,需将目标坐标按成对方式传入,但该函数效率较低,不适合大量非结构化点场景。interpn参数错误:interpn第一个参数需传入规则网格的维度坐标(如(lat0, lon0)),而非扁平化的非NaN点坐标,你传入的(latM, lonM)是一维点集,导致维度匹配失败。
可行解决方案
方案1:直接用griddata对非结构化点做三次插值
跳过规则网格插值,直接用非NaN原始点对目标坐标做三次插值,最贴合你的需求:
import numpy as np from scipy.interpolate import griddata # 复用数据定义 lat0, lon0 = np.linspace(-60, -40, 20), np.linspace(-50, 0.0, 50) lonG, latG = np.meshgrid(lon0, lat0) data_nan = np.sin(latG*lonG) data_nan[1:8, 5:12] = np.nan data_nan[2:6, 12:20] = np.nan # 提取有效点坐标与对应值 mask = ~np.isnan(data_nan) source_points = np.column_stack((latG[mask], lonG[mask])) source_values = data_nan[mask] # 目标非结构化坐标 lat = np.random.uniform(low=-60, high=-40, size=2000) lon = np.random.uniform(low=-50, high=0.0, size=2000) target_points = np.column_stack((lat, lon)) # 三次插值,靠近NaN区域的点自动返回NaN interped_cub = griddata(source_points, source_values, target_points, method='cubic', fill_value=np.nan)
方案2:先填充NaN再用RegularGridInterpolator三次插值
先对带NaN的规则网格数据补全,再使用三次插值,适合需要保留规则网格结构的场景:
import numpy as np from scipy.interpolate import RegularGridInterpolator, griddata # 复用数据定义 lat0, lon0 = np.linspace(-60, -40, 20), np.linspace(-50, 0.0, 50) lonG, latG = np.meshgrid(lon0, lat0) data_nan = np.sin(latG*lonG) data_nan[1:8, 5:12] = np.nan data_nan[2:6, 12:20] = np.nan # 提取非NaN点并填充网格 mask = ~np.isnan(data_nan) points = np.column_stack((latG[mask], lonG[mask])) values = data_nan[mask] filled_data = griddata(points, values, (latG, lonG), method='linear', fill_value=np.nan) # 用填充后的数据做三次插值 fc = RegularGridInterpolator((lat0, lon0), filled_data, method='cubic', bounds_error=True) lat = np.random.uniform(low=-60, high=-40, size=2000) lon = np.random.uniform(low=-50, high=0.0, size=2000) interped_cub = fc((lat, lon))
方案3:结合线性插值掩码做三次插值
利用线性插值返回的NaN掩码,只对有效区域的点做三次插值,避免报错:
import numpy as np from scipy.interpolate import RegularGridInterpolator # 复用数据定义 lat0, lon0 = np.linspace(-60, -40, 20), np.linspace(-50, 0.0, 50) lonG, latG = np.meshgrid(lon0, lat0) data_nan = np.sin(latG*lonG) data_nan[1:8, 5:12] = np.nan data_nan[2:6, 12:20] = np.nan # 先做线性插值,得到有效点掩码 fl = RegularGridInterpolator((lat0, lon0), data_nan, method='linear', bounds_error=True) lat = np.random.uniform(low=-60, high=-40, size=2000) lon = np.random.uniform(low=-50, high=0.0, size=2000) interped_lin = fl((lat, lon)) valid_mask = ~np.isnan(interped_lin) # 仅对有效点执行三次插值 fc = RegularGridInterpolator((lat0, lon0), data_nan, method='cubic', bounds_error=True) interped_cub = np.full_like(interped_lin, np.nan) interped_cub[valid_mask] = fc((lat[valid_mask], lon[valid_mask]))
说明
- 方案1最直接,无需预处理规则网格,直接针对目标点插值,结果完全符合你的期望:有效区域返回三次插值结果,靠近NaN区域返回NaN。
- 方案2适合需要同时保留规则网格数据的场景,填充后的网格可用于其他后续处理。
- 方案3利用线性插值的掩码过滤报错点,避免NaN影响,但需要两次插值计算。
内容的提问来源于stack exchange,提问作者ofk123
相关产品推荐
相关产品推荐

