如何使用全年的每小时数据计算每天的总降水量?
How to calculate total precipitation per day using hourly data for whole year?
我有特定年份中每一天来自 ERA5 的每小时数据。我想将该数据从每小时转换为每天。我知道这样做的漫长而艰难的方法,但我需要一些可以轻松做到这一点的东西。
Copernicus 在这里有一个代码 https://confluence.ecmwf.int/display/CKB/ERA5%3A+How+to+calculate+daily+total+precipitation,如果数据集只转换一天,它工作正常,但是当转换一整年时,我遇到了问题。
Link 下载 ERA5 数据集,可在 https://cds.climate.copernicus.eu/cdsapp#!/home
下载
按照此处的步骤使用copernicus服务器
https://confluence.ecmwf.int/display/CKB/How+to+download+ERA5
此脚本仅下载 2 天(2017 年 1 月 1 日和 2 日)的每小时数据:
#!/usr/bin/env python
"""
Save as get-tp.py, then run "python get-tp.py".
Input file : None
Output file: tp_20170101-20170102.nc
"""
import cdsapi
c = cdsapi.Client()
r = c.retrieve(
'reanalysis-era5-single-levels', {
'variable' : 'total_precipitation',
'product_type': 'reanalysis',
'year' : '2017',
'month' : '01',
'day' : ['01', '02'],
'time' : [
'00:00','01:00','02:00',
'03:00','04:00','05:00',
'06:00','07:00','08:00',
'09:00','10:00','11:00',
'12:00','13:00','14:00',
'15:00','16:00','17:00',
'18:00','19:00','20:00',
'21:00','22:00','23:00'
],
'format' : 'netcdf'
})
r.download('tp_20170101-20170102.nc')
## Add multiple days and multiple months to donload more data
下面的脚本将创建一个仅一天的 netCDF 文件
#!/usr/bin/env python
"""
Save as file calculate-daily-tp.py and run "python calculate-daily-tp.py".
Input file : tp_20170101-20170102.nc
Output file: daily-tp_20170101.nc
"""
import time, sys
from datetime import datetime, timedelta
from netCDF4 import Dataset, date2num, num2date
import numpy as np
day = 20170101
d = datetime.strptime(str(day), '%Y%m%d')
f_in = 'tp_%d-%s.nc' % (day, (d + timedelta(days = 1)).strftime('%Y%m%d'))
f_out = 'daily-tp_%d.nc' % day
time_needed = []
for i in range(1, 25):
time_needed.append(d + timedelta(hours = i))
with Dataset(f_in) as ds_src:
var_time = ds_src.variables['time']
time_avail = num2date(var_time[:], var_time.units,
calendar = var_time.calendar)
indices = []
for tm in time_needed:
a = np.where(time_avail == tm)[0]
if len(a) == 0:
sys.stderr.write('Error: precipitation data is missing/incomplete - %s!\n'
% tm.strftime('%Y%m%d %H:%M:%S'))
sys.exit(200)
else:
print('Found %s' % tm.strftime('%Y%m%d %H:%M:%S'))
indices.append(a[0])
var_tp = ds_src.variables['tp']
tp_values_set = False
for idx in indices:
if not tp_values_set:
data = var_tp[idx, :, :]
tp_values_set = True
else:
data += var_tp[idx, :, :]
with Dataset(f_out, mode = 'w', format = 'NETCDF3_64BIT_OFFSET') as ds_dest:
# Dimensions
for name in ['latitude', 'longitude']:
dim_src = ds_src.dimensions[name]
ds_dest.createDimension(name, dim_src.size)
var_src = ds_src.variables[name]
var_dest = ds_dest.createVariable(name, var_src.datatype, (name,))
var_dest[:] = var_src[:]
var_dest.setncattr('units', var_src.units)
var_dest.setncattr('long_name', var_src.long_name)
ds_dest.createDimension('time', None)
var = ds_dest.createVariable('time', np.int32, ('time',))
time_units = 'hours since 1900-01-01 00:00:00'
time_cal = 'gregorian'
var[:] = date2num([d], units = time_units, calendar = time_cal)
var.setncattr('units', time_units)
var.setncattr('long_name', 'time')
var.setncattr('calendar', time_cal)
# Variables
var = ds_dest.createVariable(var_tp.name, np.double, var_tp.dimensions)
var[0, :, :] = data
var.setncattr('units', var_tp.units)
var.setncattr('long_name', var_tp.long_name)
# Attributes
ds_dest.setncattr('Conventions', 'CF-1.6')
ds_dest.setncattr('history', '%s %s'
% (datetime.now().strftime('%Y-%m-%d %H:%M:%S'),
' '.join(time.tzname)))
print('Done! Daily total precipitation saved in %s' % f_out)
我想要的是一个代码,它将遵循与上述数据相同的步骤,但假设我有一个包含一年每小时数据的输入文件并将其转换为一年每日数据。
结果应该是全年计算变量(如降水量等)的每日值。
示例:假设我有一个全年的降水量数据,每天 1 毫米/小时,那么全年会有 2928 个值。
我想要的是全年24mm/天,非闰年只有365个值。
示例输入数据集: 可以从此处下载数据的子集(2017 年 1 月 1 日和 2 日)https://www.dropbox.com/sh/0vdfn20p355st3i/AABKYO4do_raGHC34VnsXGPqa?dl=0。只需使用此之后的第二个脚本来检查代码。 {全年代码>10GB无法上传
提前致谢
xarray resample 正是适合您的工具。它将 netCDF 数据在一行中从一种时间分辨率(例如每小时)转换为另一种时间分辨率(例如每天)。使用您的示例数据文件,我们可以使用以下代码创建每日均值:
import xarray as xr
ds = xr.open_dataset('./tp_20170101-20170102.nc')
tp = ds['tp'] # dimensions [time: 48, latitude: 721, longitude: 1440]
tp_daily = tp.resample(time='D').mean(dim='time') # dimensions (time: 2, latitude: 721, longitude: 1440)
您会看到 resample
命令接受一个时间代码,在本例中 'D'
表示每天,然后我们指定我们要使用.mean(dim='time')
当天的每小时数据。
例如,如果您想计算每日最大值而不是每日平均值,则可以将 .mean(dim='time')
替换为 .max(dim='time')
。您还可以从每小时转到每月(MS
或月开始)、每年(AS
或每年开始)等等。时间频率代码可以在 Pandas docs.
中找到
从命令行使用 CDO 的另一种快速方法是:
cdo daysum -shifttime,-1hour era5_hourly.nc era5_daily.nc
请注意,根据此处 answer/discussion:
ERA5 每小时数据的时间步长位于每小时 window 的末尾,因此您需要在求和之前移动时间戳,我不确定 xarray 解决方案是否处理该问题。还有mm/day,我觉得要求和,不要取均值。
我有特定年份中每一天来自 ERA5 的每小时数据。我想将该数据从每小时转换为每天。我知道这样做的漫长而艰难的方法,但我需要一些可以轻松做到这一点的东西。
Copernicus 在这里有一个代码 https://confluence.ecmwf.int/display/CKB/ERA5%3A+How+to+calculate+daily+total+precipitation,如果数据集只转换一天,它工作正常,但是当转换一整年时,我遇到了问题。
Link 下载 ERA5 数据集,可在 https://cds.climate.copernicus.eu/cdsapp#!/home
下载按照此处的步骤使用copernicus服务器
https://confluence.ecmwf.int/display/CKB/How+to+download+ERA5
此脚本仅下载 2 天(2017 年 1 月 1 日和 2 日)的每小时数据:#!/usr/bin/env python
"""
Save as get-tp.py, then run "python get-tp.py".
Input file : None
Output file: tp_20170101-20170102.nc
"""
import cdsapi
c = cdsapi.Client()
r = c.retrieve(
'reanalysis-era5-single-levels', {
'variable' : 'total_precipitation',
'product_type': 'reanalysis',
'year' : '2017',
'month' : '01',
'day' : ['01', '02'],
'time' : [
'00:00','01:00','02:00',
'03:00','04:00','05:00',
'06:00','07:00','08:00',
'09:00','10:00','11:00',
'12:00','13:00','14:00',
'15:00','16:00','17:00',
'18:00','19:00','20:00',
'21:00','22:00','23:00'
],
'format' : 'netcdf'
})
r.download('tp_20170101-20170102.nc')
## Add multiple days and multiple months to donload more data
下面的脚本将创建一个仅一天的 netCDF 文件
#!/usr/bin/env python
"""
Save as file calculate-daily-tp.py and run "python calculate-daily-tp.py".
Input file : tp_20170101-20170102.nc
Output file: daily-tp_20170101.nc
"""
import time, sys
from datetime import datetime, timedelta
from netCDF4 import Dataset, date2num, num2date
import numpy as np
day = 20170101
d = datetime.strptime(str(day), '%Y%m%d')
f_in = 'tp_%d-%s.nc' % (day, (d + timedelta(days = 1)).strftime('%Y%m%d'))
f_out = 'daily-tp_%d.nc' % day
time_needed = []
for i in range(1, 25):
time_needed.append(d + timedelta(hours = i))
with Dataset(f_in) as ds_src:
var_time = ds_src.variables['time']
time_avail = num2date(var_time[:], var_time.units,
calendar = var_time.calendar)
indices = []
for tm in time_needed:
a = np.where(time_avail == tm)[0]
if len(a) == 0:
sys.stderr.write('Error: precipitation data is missing/incomplete - %s!\n'
% tm.strftime('%Y%m%d %H:%M:%S'))
sys.exit(200)
else:
print('Found %s' % tm.strftime('%Y%m%d %H:%M:%S'))
indices.append(a[0])
var_tp = ds_src.variables['tp']
tp_values_set = False
for idx in indices:
if not tp_values_set:
data = var_tp[idx, :, :]
tp_values_set = True
else:
data += var_tp[idx, :, :]
with Dataset(f_out, mode = 'w', format = 'NETCDF3_64BIT_OFFSET') as ds_dest:
# Dimensions
for name in ['latitude', 'longitude']:
dim_src = ds_src.dimensions[name]
ds_dest.createDimension(name, dim_src.size)
var_src = ds_src.variables[name]
var_dest = ds_dest.createVariable(name, var_src.datatype, (name,))
var_dest[:] = var_src[:]
var_dest.setncattr('units', var_src.units)
var_dest.setncattr('long_name', var_src.long_name)
ds_dest.createDimension('time', None)
var = ds_dest.createVariable('time', np.int32, ('time',))
time_units = 'hours since 1900-01-01 00:00:00'
time_cal = 'gregorian'
var[:] = date2num([d], units = time_units, calendar = time_cal)
var.setncattr('units', time_units)
var.setncattr('long_name', 'time')
var.setncattr('calendar', time_cal)
# Variables
var = ds_dest.createVariable(var_tp.name, np.double, var_tp.dimensions)
var[0, :, :] = data
var.setncattr('units', var_tp.units)
var.setncattr('long_name', var_tp.long_name)
# Attributes
ds_dest.setncattr('Conventions', 'CF-1.6')
ds_dest.setncattr('history', '%s %s'
% (datetime.now().strftime('%Y-%m-%d %H:%M:%S'),
' '.join(time.tzname)))
print('Done! Daily total precipitation saved in %s' % f_out)
我想要的是一个代码,它将遵循与上述数据相同的步骤,但假设我有一个包含一年每小时数据的输入文件并将其转换为一年每日数据。
结果应该是全年计算变量(如降水量等)的每日值。
示例:假设我有一个全年的降水量数据,每天 1 毫米/小时,那么全年会有 2928 个值。
我想要的是全年24mm/天,非闰年只有365个值。
示例输入数据集: 可以从此处下载数据的子集(2017 年 1 月 1 日和 2 日)https://www.dropbox.com/sh/0vdfn20p355st3i/AABKYO4do_raGHC34VnsXGPqa?dl=0。只需使用此之后的第二个脚本来检查代码。 {全年代码>10GB无法上传
提前致谢
xarray resample 正是适合您的工具。它将 netCDF 数据在一行中从一种时间分辨率(例如每小时)转换为另一种时间分辨率(例如每天)。使用您的示例数据文件,我们可以使用以下代码创建每日均值:
import xarray as xr
ds = xr.open_dataset('./tp_20170101-20170102.nc')
tp = ds['tp'] # dimensions [time: 48, latitude: 721, longitude: 1440]
tp_daily = tp.resample(time='D').mean(dim='time') # dimensions (time: 2, latitude: 721, longitude: 1440)
您会看到 resample
命令接受一个时间代码,在本例中 'D'
表示每天,然后我们指定我们要使用.mean(dim='time')
当天的每小时数据。
例如,如果您想计算每日最大值而不是每日平均值,则可以将 .mean(dim='time')
替换为 .max(dim='time')
。您还可以从每小时转到每月(MS
或月开始)、每年(AS
或每年开始)等等。时间频率代码可以在 Pandas docs.
从命令行使用 CDO 的另一种快速方法是:
cdo daysum -shifttime,-1hour era5_hourly.nc era5_daily.nc
请注意,根据此处 answer/discussion: