我有一个xarray数据集,每个变量有3个维度(纬度,经度,时间)。我的时间是lat的720,lon的1440和13140。对于每个像素,我在时间序列中都有一些间隔,并且我试图知道什么是间隔的最大时间和平均时间。由于它是一个大型数据集,因此我尝试按年进行处理。
1年的数据集的输出(在代码中合并命名):
#the dataset for 1 year:
<xarray.Dataset>
Dimensions: (lat: 720, lon: 1440, time: 365)
Coordinates:
* lat (lat) float32 89.875 89.625 89.375 89.125 88.875 88.625 ...
* lon (lon) float32 -179.875 -179.625 -179.375 -179.125 ...
* time (time) datetime64[ns] 1981-04-06 1981-01-18 1981-09-29 ...
Data variables:
t0 (time, lat, lon) datetime64[ns] dask.array<shape=(365, 720, 1440), chunksize=(1, 720, 1440)>
sm (time, lat, lon) float32 dask.array<shape=(365, 720, 1440), chunksize=(1, 720, 1440)>
我每年都尝试使用此代码进行循环:
# create dataset of nan to then fill it with the values
var=np.zeros((36,720,1440))*np.NaN
lat = combined.lat.values
lon = combined.lon.values
time_na = time # each year
diff_day = xr.Dataset(
data_vars={'max': (('time','lat', 'lon'), var),'mean': (('time','lat', 'lon'), var)},
coords={'time': time_na, 'lat': lat, 'lon':lon})
for t,name in tqdm(enumerate(filenames)): #loop for each year
filename_year = glob(name+'/*.nc') # read all the files for the year
combined = xr.open_mfdataset(filename_year,concat_dim='time',autoclose =True, decode_times=True)
combined = combined.sortby(combined['time'],ascending=True) # otherwise the time is not montonic
# calculation pixel by pixel
for i in range(len(combined.lat)):
for j in range(len(combined.lon)):
if len(combined.time.values[np.isfinite(combined.sm.values[:,i,j])])>1 : # avoid cases where it's a list of nan
# the idea is to make the diff of time between finite (not finite values correspond to the gap) values.
diff_day['max'].loc[t,i,j] = np.diff(combined.time.values[np.isfinite(combined.sm.values[:,i,j])]).astype('timedelta64[D]').max()/ np.timedelta64(1, 'D')
diff_day['mean'].loc[t,i,j] = np.diff(combined.time.values[np.isfinite(combined.sm.values[:,i,j])]).astype('timedelta64[D]').mean()/ np.timedelta64(1, 'D')
此代码有效,但是处理时间太长。我想知道是否有更简单的方法可以做到这一点。 谢谢
答案 0 :(得分:0)
如果您希望获得NaN值的平均值,那么可以使用da.isnull().mean(dim='time')
之类的简单方法来解决。但是,获得NaN连续块的平均长度和最大长度是比简单的xarray程序问题更为复杂的算法问题。
我敢肯定有很多方法可以做到这一点,但是我想到的是这样的:
首先,构造一个与数据形状相同的数组,并沿时间维度简单地增加:
In [10]: arange = xr.ones_like(da) * np.arange(len(da.time))
在我为此制作的玩具数据中,每个单元格的时间序列如下:
In [11]: arange.isel(lat=0, lon=0).plot()
接下来,创建一个类似的数组,但每个NaN块的周期保持不变:
In [12]: cumulative_nans = (arange.where(da.notnull()).ffill(dim='time')).fillna(0)
在每个单元格中,此数组具有每个NaN块的阶梯:
In [13]: cumulative_nans.isel(lat=0, lon=0).plot()
现在,您可以将这两个数相减得到一个数组,其中每个单元格中的值是一个计数器,其中包含该块中NaN的累积数量:
In [14]: time_series_of_cumulative_nan_blocks = (arange - cumulative_nans)
在每个单元格中:
In [15]: time_series_of_cumulative_nan_blocks.isel(lat=0, lon=0).plot()
您可以很容易地计算出最大值:
In [16]: max_nan_duration = time_series_of_cumulative_nan_blocks.max(dim='time')
意思是更难。我们可以使用从一个时间步长到下一个时间步长的变化来过滤数据,使其仅包含下一个单元格下降的点,例如,我们达到峰值的点:
In [17]: nan_block_length_peaks_only = (
time_series_of_cumulative_nan_blocks
.where(
time_series_of_cumulative_nan_blocks
.diff(dim='time', label='lower')
< 0)
在每个单元格中,这将第三个数字限制为一组点:
In [18]: nan_block_length_peaks_only.isel(lat=0, lon=0).plot(marker='.')
可以对该值求平均值以找到平均持续时间:
In [19]: mean_nan_duration = nan_block_length_peaks_only.mean(dim='time')
与遍历数据集中的每个单元格和时间段相比,这应该具有显着的性能优势,主要是因为它依赖于编译的矢量化xarray函数,而不是python循环。也可以使用dask对整个数据集进行此计算,这可能会带来进一步的收益,具体取决于您的设置。