在含NaN的xarray数据中执行sklearn Ridge回归的问题咨询
带NaN的Xarray数据岭回归处理方案
1. 要不要迭代检查每个变量的NaN?
不用全量遍历检查所有变量的NaN,但每个网格点做回归前必须过滤掉含NaN的时间样本。你现在是逐经纬度网格独立做回归,每个网格的时间序列是单独的,只要在循环里把当前网格下x1/x2/x3和y中同时非NaN的时间步挑出来,就能让sklearn的Ridge正常运行,完全不用提前用fillna(0)硬填。
2. 替代fillna(0)的靠谱方法
方法一:逐网格过滤无效样本
修改你的回归循环部分,只保留有效时间步,无效网格直接留NaN标记:
a = np.full((72,144,3), np.nan) # 初始化为NaN,标记无法计算的网格 for i in range(len(data.lat)): for j in range(len(data.lon)): # 提取当前网格的所有变量时间序列 x1 = dataU.isel(lev=2).x1.values[:,i,j] x2 = dataU.isel(lev=2).x2.values[:,i,j] x3 = dataU.isel(lev=2).x3.values[:,i,j] y = data2U.y.values[:,i,j] # 找到所有变量都非NaN的时间索引 valid_mask = ~(np.isnan(x1) | np.isnan(x2) | np.isnan(x3) | np.isnan(y)) if valid_mask.sum() < 4: # 样本数太少,跳过回归 continue # 构建训练集 X = np.column_stack([x1[valid_mask], x2[valid_mask], x3[valid_mask]]) y_train = y[valid_mask] # 拟合Ridge ridge = Ridge() ridge.fit(X, y_train) a[i,j,:] = ridge.coef_ dataU = data.assign_coords(varname=['x1','x2','x3']) dataU['multiple_reg_coeff'] = (('lat','lon','varname'), a)
这种方法完全保留真实数据,不会凭空生成值,无法计算的网格(样本太少或全NaN)会留NaN,后续可以用xarray的where或者空间插值处理这些网格。
方法二:空间插值填充网格级NaN
如果是部分网格整个时间序列都是NaN(比如海洋边缘的零散网格),可以先做空间维度的插值:
# 对变量做空间线性插值,仅填充网格级的全时间NaN data = data.interpolate_na(dim='lon', method='linear').interpolate_na(dim='lat', method='linear') data2 = data2.interpolate_na(dim='lon', method='linear').interpolate_na(dim='lat', method='linear')
注意这种方法只适合空间连续的NaN,时间序列内的NaN还是要靠方法一过滤。
3. 关于sklearn支持NaN的回归模型
sklearn的Ridge确实不支持NaN,但SGDRegressor可以通过设置loss='squared_error'+penalty='l2'模拟岭回归,同时用missing_values=np.nan和strategy='mean'处理NaN,但它用的是随机梯度下降求解,和标准Ridge的闭式解有差异。如果你对Ridge的闭式解更熟悉,完全没必要换模型,用方法一的样本过滤就够了。
效率优化:用apply_ufunc替代嵌套循环
嵌套循环跑72*144个网格太慢,用xarray的apply_ufunc批量处理,速度快很多:
def ridge_reg_single_grid(X, y): valid_mask = ~(np.isnan(X).any(axis=1) | np.isnan(y)) if valid_mask.sum() < 4: return np.full(3, np.nan) X_valid = X[valid_mask] y_valid = y[valid_mask] return Ridge().fit(X_valid, y_valid).coef_ # 把x1/x2/x3堆叠成(time, lat, lon, 3)的数组 X_stack = np.stack([ dataU.isel(lev=2).x1.values, dataU.isel(lev=2).x2.values, dataU.isel(lev=2).x3.values ], axis=-1) y_arr = data2U.y.values # 用apply_ufunc批量处理每个网格 coeffs = xr.apply_ufunc( ridge_reg_single_grid, X_stack, y_arr, input_core_dims=[['time'], ['time']], output_core_dims=[['varname']], vectorize=True, dask='allowed' ) dataU['multiple_reg_coeff'] = coeffs.assign_coords(varname=['x1','x2','x3'])
内容的提问来源于stack exchange,提问作者Logan
相关产品推荐
相关产品推荐

