从netcdf文件获取每月的平均小时数

11

我有一个netCDF文件,其中时间维度包含了2年每小时的数据。我想将其平均以获得每月每小时的平均值。我尝试了以下方法:

import xarray as xr
ds = xr.open_mfdataset('ecmwf_usa_2015.nc')    
ds.groupby(['time.month', 'time.hour']).mean('time')

但是我收到了这个错误:

*** TypeError: `group` must be an xarray.DataArray or the name of an xarray variable or dimension

我该如何修复这个问题?如果我这样做:

ds.groupby('time.month', 'time.hour').mean('time')
我没有收到错误提示,但结果的时间维度为12(每个月一个值),我想要每个月的小时平均数,即每个月24个值。数据可以在此处找到:https://www.dropbox.com/s/yqgg80wn8bjdksy/ecmwf_usa_2015.nc?dl=0

我认为dsxarray.Dataset,而不是netCDF4.Dataset,这正确吗? - SiggyF
请提供一些示例数据,并澄清没有数据的小时应该如何处理。如果需要考虑缺失数据,则需要进行“重新采样”。 - Maarten Fabré
@SiggyF,你说得对,ds是通过读取netCDF文件生成的xarray.Dataset。 - user308827
@MaartenFabré,我会尝试获取一个样本数据集(完整数据集大小为数GB)。您可以假设没有缺失的数据。 - user308827
1
一个带有虚拟数据(例如随机数据)的最小示例通常效果最好。虽然重点是Pandas,但这个问题/答案可能会对此有所帮助:https://dev59.com/O2Ij5IYBdhLWcg3wk182 - Bart
显示剩余9条评论
5个回答

6
您得到了一个TypeError:group必须是xarray变量、数组的名称或维度名称,因为ds.groupby()需要采用xarray数据集变量或数组,而您传递了一个变量列表。

您有两个选择:

1. xarray分组-->按小时分组

参考分组文档,将数据集转换成splitsbins,然后应用groupby('time.hour')

这是因为先对月份进行分组,然后对小时进行单独或同时操作会聚合所有数据。如果将它们分成月份数据,则可以按每个月份应用聚合函数。

您可以尝试如文档所述的方法:

GroupBy: split-apply-combine

xarray支持与pandas相同的API实现“分组”操作以实施分割-应用-组合策略:

  • 将数据拆分为多个独立组。=>使用groupby_bins将它们分成月份组
  • 对每个组应用某些函数。=>应用group by
  • 将组合并回单个数据对象。 =>应用聚合函数mean('time')

2. 将其转换为pandas dataframe并使用group by

警告::并非所有的netcdfs都可以转换为pandas dataframe,转换时可能会丢失元数据。

通过df = ds.to_dataframe()将ds转换为pandas dataframe,并使用pandas.Grouper进行所需的group by操作。

df.set_index('time').groupby([pd.Grouper(freq='1M'), 't2m']).mean()

注意: 我看到了几篇使用 pandas.TimeGrouper 的答案,但这个方法已经被弃用,现在必须使用 pandas.Grouper

由于你的数据集太大,并且问题没有最小化数据,处理它需要消耗大量资源,我建议查看以下关于 pandas 的示例:

  1. 按工作日分组
  2. 按时间分组
  3. 根据每行的日期范围进行分组
  4. 按月份和年份分组和计数行

5

如果您还没有解决问题,可以按照以下步骤进行:

# define a function with the hourly calculation:
def hour_mean(x):
     return x.groupby('time.hour').mean('time')

# group by month, then apply the function:
ds.groupby('time.month').apply(hour_mean)

这与@Prateek给出的第一种选项使用的策略相同,基于文档,但对我来说文档不够清晰,因此我希望这可以帮助澄清。您不能对groupby对象应用groupby操作,因此必须将其构建到函数中,并使用.apply()才能正常工作。

1
另一种解决使用xarray库检索netcdf文件的多时间分组函数问题的方法是使用xarray-DataArray方法"resample"结合"groupby"方法。这种方法也适用于xarray-DataSet对象。
通过这种方法,可以检索像月度-小时平均值或其他类型的时间聚合(例如:年度月平均值,半年度三个月总和等)之类的值。
下面的示例使用标准的xarray教程数据集日常空气温度(Tair)。请注意,我必须将教程数据的时间维度转换为pandas日期时间对象。如果不进行此转换,则重采样函数将失败,并出现错误消息(如下所示):
错误消息:
“TypeError:仅限于DatetimeIndex、TimedeltaIndex或PeriodIndex有效,但得到了'Index'的实例”
尽管存在时间索引问题(这可能是StackOverFlow中讨论的另一个问题),但以下代码提供了xarray对象中多时间分组问题的两个可能解决方案。第一个使用xarray.core.groupby.DataArrayGroupBy类,而第二个仅使用来自正常xarray-dataArray和xarray-DataSet类的groupby方法。
此致

菲利普·里斯卡拉·莱尔

代码片段:

ds = xr.tutorial.open_dataset('rasm').load()

def parse_datetime(time):
    return pd.to_datetime([str(x) for x in time])

ds.coords['time'] = parse_datetime(ds.coords['time'].values)


# 1° Option for multitemporal aggregation:


time_grouper = pd.Grouper(freq='Y')

grouped = xr.core.groupby.DataArrayGroupBy(ds, 'time', grouper=time_grouper)

for idx, sub_da in grouped:
    print(sub_da.resample({'time':'3M'}).mean().coords)


 # 2° Option for multitemporal aggregation:


grouped = ds.groupby('time.year')
for idx, sub_da in grouped:
    print(sub_da.resample({'time':'3M'}).mean().coords)

0

虽然不是Python解决方案,但我认为你可以在bash脚本循环中使用CDO来完成:

# loop over months:
for i in {1..12}; do
   # This gives the hourly mean for each month separately 
   cdo yhourmean -selmon,${i} datafile.nc mon${i}.nc
done
# merge the files
cdo mergetime mon*.nc hourlyfile.nc
rm -f mon*.nc # clean up the files

请注意,如果您的数据不是从一月份开始的,那么最终文件时间会出现“跳跃”... 如果这对您是一个问题,我认为可以通过在yhourmean命令之后设置年份来解决。

谢谢@Adrian,我正在寻找一个Python解决方案,但是感谢你的努力。 - user308827

0

使用这个

import xarray as xr
ds = xr.open_mfdataset('ecmwf_usa_2015.nc')
print ds.groupby('time.hour' ).mean('time')

我得到了类似这样的东西:

维度:(小时:24,纬度:93,经度:281)坐标:

  • 经度(longitude)float32 230.0 230.25 230.5 230.75 231.0 231.25 ... * 纬度(latitude)float32 48.0 47.75 47.5 47.25 47.0 46.75 46.5 ... * 小时(hour)int64 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 ...
我认为这就是你想要的。

我也原本认为是一样的,但是这是24。OP想要的是24*12。 - Morse

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接