如何用xarray计算多年间每年同日的变量均值(特定时段处理)
问题描述
我有一个xarray DataArray,包含1970年至2023年10月至3月的每日数据,想计算变量每年同日的平均值(比如所有1月1日的平均值)。数据结构如下:
<xarray.DataArray 'u' (valid_time: 9990, latitude: 321, longitude: 361)> Size: 5GB dask.array<getitem, shape=(9990, 321, 361), dtype=float32, chunksize=(260, 161, 181), chunktype=numpy.ndarray> Coordinates: number int64 8B 0 pressure_level float64 8B 500.0 * latitude (latitude) float64 3kB 0.0 -0.25 -0.5 ... -79.5 -79.75 -80.0 * longitude (longitude) float64 3kB -100.0 -99.75 -99.5 ... -10.25 -10.0 * valid_time (valid_time) datetime64[ns] 80kB 1970-01-01 ... 2023-12-31
我试过用groupby方法,但只能单独按月份或日期分组(比如所有月份的1日),没法同时按月份和日期分组。嵌套分组的方式能得到目标数据,但不够优雅:
da.groupby('valid_time.month')[1].groupby('valid_time.day')[1]
还参考了方法尝试用字符串格式化分组,结果报错:
da.groupby(da.index.strftime('%m-%d')).mean()
报错信息:
AttributeError: 'DataArray' object has no attribute 'index'
期望结果形状为(D, latitude, longitude),其中D是10月1日至3月31日的天数(已排除所有2月29日)。
解决方案
方法1:按月份+日期组合分组
xarray的groupby支持传入多个坐标属性的元组,直接实现“月-日”组合分组:
# 按月份和日期的组合分组,计算同日平均值 da_clim = da.groupby(['valid_time.month', 'valid_time.day']).mean() # 筛选10-3月的数据,并排除2月29日 mask = (da_clim.month >= 10) | (da_clim.month <= 3) mask = mask & ~((da_clim.month == 2) & (da_clim.day == 29)) da_clim_filtered = da_clim.where(mask, drop=True)
方法2:利用时间坐标的dt.strftime生成标签
之前报错是因为误操作了DataArray本身,应该直接对valid_time坐标的dt属性调用strftime:
# 生成"月-日"格式的字符串标签,用于分组 month_day = da.valid_time.dt.strftime('%m-%d') da_clim = da.groupby(month_day).mean() # 生成目标日期列表(10-3月,排除2月29日) target_days = [] for m in [10,11,12,1,2,3]: max_day = 31 if m in [1,3,10,12] else 30 if m in [11] else 28 for d in range(1, max_day+1): target_days.append(f"{m:02d}-{d:02d}") # 筛选目标日期的数据 da_clim_filtered = da_clim.sel(valid_time=target_days)
方法3:用dayofyear分组(兼容闰年)
先排除2月29日的数据,再按年积日分组计算平均值,最后筛选目标日期范围:
# 先移除所有2月29日的数据 da_no_leap = da.where(~((da.valid_time.dt.month == 2) & (da.valid_time.dt.day == 29)), drop=True) # 按年积日分组计算平均 da_clim = da_no_leap.groupby('valid_time.dayofyear').mean() # 筛选10月1日(年积日274)至3月31日(年积日90)的数据 mask = (da_clim.dayofyear >= 274) | (da_clim.dayofyear <= 90) da_clim_filtered = da_clim.where(mask, drop=True)
内容的提问来源于stack exchange,提问作者Ramiro chalar
相关产品推荐
相关产品推荐

