
本文介绍如何使用 xarray 和 pandas 的原生分组与重采样功能,替代低效的三重嵌套循环,快速、鲁棒地将小时尺度气象数据(如温度)聚合为日均值,并自动检测时间序列的连续性。
本文介绍如何使用 xarray 和 pandas 的原生分组与重采样功能,替代低效的三重嵌套循环,快速、鲁棒地将小时尺度气象数据(如温度)聚合为日均值,并自动检测时间序列的连续性。
在处理高分辨率气象或观测时间序列时,将小时数据(如每小时气温)转换为日均值是常见需求。传统做法常采用多层 for 循环遍历年、月、日,手动索引并调用 np.nanmean——不仅代码冗长、可读性差,更存在严重性能瓶颈和逻辑脆弱性(如对非连续时间序列缺乏健壮处理)。幸运的是,现代科学计算生态提供了完全向量化、声明式且语义清晰的替代方案。
✅ 推荐方案:xarray .resample() + 连续性校验
最简洁、高效且生产就绪的方式是结合 xarray.DataArray.resample() 与 pandas 时间索引校验:
import pandas as pd
import xarray as xr
# 加载示例数据(实际中替换为您的 Dataset)
ds = xr.tutorial.load_dataset("air_temperature")
da = ds['air'] # shape: (time, lat, lon)
# 步骤1:按日重采样(自动对齐到日历日边界,支持缺失值)
daily_resampler = da.resample(time='D')
# 步骤2:执行日均值聚合(自动沿 time 维度求均值,保留其他维度)
daily_mean = daily_resampler.mean(dim='time')
# 步骤3:校验时间连续性(关键!避免静默错误)
time_index = daily_resampler.groups['time'] # 获取重采样后的时间点
if not (pd.Series(time_index).diff().dropna() == pd.Timedelta('1 days')).all():
raise ValueError("检测到非连续日期序列:重采样结果可能包含空缺或跳跃,请检查原始时间索引")
print(f"原始时间长度: {len(da.time)} 小时")
print(f"日均后时间长度: {len(daily_mean.time)} 天")
print(f"输出形状: {daily_mean.shape}")
⚠️ 注意:
resample(time='D')默认以日历日为单位(00:00–23:59),对齐到午夜。若需自定义起始偏移(如从 06:00 开始每日窗口),可使用resample(time='D', loffset='6H')或更灵活的grouper=pd.Grouper(freq='D', origin='start_day')。
❌ 为什么不要用 .groupby("time.day")?
原答案中提到的 .groupby("time.day") 是错误示范:它仅按“日号”(1–31)分组,会把所有1月1日、2月1日、3月1日……的数据混在一起求均值,完全丢失年月信息,导致结果毫无物理意义。正确分组应基于完整时间戳的自然日切分,即 resample('D') 或 groupby(da.time.dt.date)(后者等价但效率略低)。
? 高级技巧:处理不规则/缺失时间点
若原始数据存在小时级缺失(如传感器偶发离线),resample().mean() 仍能稳健工作——它会自动识别每个日窗口内实际存在的小时样本,并对它们求均值(跳过 NaN)。但若需严格要求每日必须有 24 个有效小时才参与统计,可添加过滤:
# 仅保留至少有12个有效小时记录的日子 daily_counts = da.resample(time='D').count(dim='time') valid_days = (daily_counts >= 12) daily_mean_masked = daily_mean.where(valid_days, drop=True)
✅ 总结
- 性能提升:向量化操作比 Python 三层循环快 10–100 倍,且内存友好;
- 健壮性增强:内置缺失值处理、自动时间对齐、明确的错误提示;
- 可维护性高:代码行数减少 80%+,逻辑一目了然;
-
扩展性强:轻松切换为
resample(time='MS')(月均)、resample(time='QS')(季均)等。
摒弃手工循环,拥抱 xarray 的声明式时间聚合——这是现代地球科学数据分析的标准实践。










