将单个值分配给 xarray 数据集的更快方法

Faster way to assign individual value to a xarray dataset

我意识到将单个值分配给 xarray 数据集比对 numpy 数组执行相同的操作要花费更多的时间。有什么方法可以加速吗?

以下是我通过交替取消循环中每一行的注释测试的所有内容:

import numpy as np
import xarray as xr
import datetime

levels = np.arange(0,3)
simNames = ['9airports_filter0dot7_v22']
airportList = ['Windhoek', 'Atlanta', 'Taipei']
numb_variables = 11

emptyDA = xr.DataArray(np.nan, 
                       coords = [simNames, airportList, np.arange(0, 20428), levels], 
                       dims   = ['simName', 'airport', 'profnum'           , 'level'])

ds = xr.Dataset({ 'iasi': emptyDA.copy(), 'IM':   emptyDA.copy(), 'IMS': emptyDA.copy(), 'err': emptyDA.copy(), 
                 'sigma': emptyDA.copy(), 'temp': emptyDA.copy(), 'dfs': emptyDA.copy(), 'ocf': emptyDA.copy(), 
                 'rcf':   emptyDA.copy(), 'time': emptyDA.copy().astype(dtype="datetime64[ns]"), 'surfPres': emptyDA.copy() })

mat3D = np.empty( shape=( len(airportList), 20428, len(levels) ) ) # 20428 is needed for the 9 airports
mat3D[:] = np.nan

mat4D = np.empty( shape=( 1, len(airportList), 20428, len(levels) ) ) # 20428 is needed for the 9 airports
mat4D[:] = np.nan

mat5D = np.empty( shape=( numb_variables, 1, len(airportList), 20428, len(levels) ) ) # 20428 is needed for the 9 airports
mat5D[:] = np.nan

begin_time = datetime.datetime.now()

for i in range(10000):
    ds['iasi'][0, 0, 0, 0] = 3.1416                                           # 1.08 sec
    # ds['iasi'].loc['9airports_filter0dot7_v22', 'Windhoek', 0, 0] = 3.1416    # 1.97 sec
    # ds['iasi'][0, 0, 0, 0].data = 3.1416                                      # 0.85 sec
    # ds['iasi'][0, 0, 0, 0].values = 3.1416                                    # 0.85 sec
    # ds.iasi[0, 0, 0, 0].values = 3.1416                                       # 0.88 sec
    # a = 3.1416                                                                # 0.0003
    # mat4D[0, 0, 0, 0] = 3.1416                                                # 0.0008 sec
    # mat3D[0, 0, 0] = 3.1416                                                   # 0.0008 sec
    # mat5D[0, 0, 0, 0, 0] = 3.1416                                             # 0.0009 sec

print(datetime.datetime.now() - begin_time)

如果您要使用位置索引来引用数据,您绝对可以在 numpy 中进行索引,例如ds['iasi'].data[0, 0, 0, 0] = 3.1416。但是你应该有的一般假设是 xarray 与 numpy 相比具有更差的性能,因为你在 xarray 中执行的每个操作都是一堆 python 开销,加上任何 pandas 基于你正在做的索引,在相应的 numpy 操作之上。

也就是说,xarray 努力工作,只在底层数组操作之上添加适度的惩罚。重要的是,在执行小操作时,开销会最明显。数组越大,xarray 的性能就越接近 numpy,因为大部分工作将在数组本身上进行,而不是通过索引、元数据管理和 xarray 在顶部添加的其他元素来提示它。

为了说明这一点,我设置了一个并排比较,我们首先初始化一个(非常大的)空 numpy 数组,然后逐步填充第一个 1,然后是 10,然后是 100,然后是 1000,等等. 元素,直到我们用零填充整个 (1000, 1000, 100) 数组:

In [4]: times = []
   ...: for sim in range(40):
   ...:     for i in range(1, 9):
   ...:         arr = np.empty(shape=(1000, 1000, 100), dtype='float64')
   ...:         # progressively fill in 10^i cells at each iteration
   ...:         start = time.time()
   ...:         arr.flat[:int(10**i)] = 0
   ...:         end = time.time()
   ...:         times.append([i, end - start])

将此与 xarray 中的类似操作进行比较:

In [5]: times_xr = []
   ...: for sim in range(40):
   ...:     for i in range(1, 9):
   ...:         da = xr.DataArray(np.empty(shape=(1000, 1000, 100), dtype='float64'), dims=['x', 'y', 'z'])
   ...:         # progressively fill in 10^i cells at each iteration
   ...:         start = time.time()
   ...:         da.data.flat[:int(10**i)] = 0
   ...:         end = time.time()
   ...:         times_xr.append([i, end - start])

我只对数据填充步骤进行了计时,以便与您问题中的情况进行比较,但请注意 (a) 填充数组的时间与第一个写入的块大小的线性比例远小于线性比例几个数量级,并且 (b) xarray 性能跟踪 numpy 相当好,尽管前几个订单有相当大的差距:

In [8]: data = pd.concat([
   ...:     pd.DataFrame(times, columns=['fill_order', 'time']).assign(package='numpy'),
   ...:     pd.DataFrame(times_xr, columns=['fill_order', 'time']).assign(package='xarray'),
   ...: ], axis=0)
   ...: ax = sns.lineplot(data=data, x='fill_order', y='time', hue='package')
   ...: ax.set_yscale('log')
   ...: ax.set_xlabel('log10 fill size')
   ...: ax.set_ylabel('time (s)')