如何获得XArray数据集中的最大间隔时间

时间:2018-10-30 08:18:29

标签: dataset time-series pixel python-xarray date-difference

我有一个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')

此代码有效,但是处理时间太长。我想知道是否有更简单的方法可以做到这一点。 谢谢

1 个答案:

答案 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()

steadily increasing line

接下来,创建一个类似的数组,但每个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()

line with slope 1 but holding constant for NaN blocks

现在,您可以将这两个数相减得到一个数组,其中每个单元格中的值是一个计数器,其中包含该块中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()

increasing for each NaN block, back to 0 for each non-NaN value

您可以很容易地计算出最大值:

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='.')

scatter_of_NaN_durations

可以对该值求平均值以找到平均持续时间:

In [19]: mean_nan_duration = nan_block_length_peaks_only.mean(dim='time')

与遍历数据集中的每个单元格和时间段相比,这应该具有显着的性能优势,主要是因为它依赖于编译的矢量化xarray函数,而不是python循环。也可以使用dask对整个数据集进行此计算,这可能会带来进一步的收益,具体取决于您的设置。