【问题标题】:Applying a Numba guvectorize function over time dimension of an 3D-Array with Xarray's apply_ufunc使用 Xarray 的 apply_ufunc 在 3D 数组的时间维度上应用 Numba guvectorize 函数
【发布时间】:2020-05-26 09:05:12
【问题描述】:

我在让它正常工作时遇到了一些问题,我也愿意接受其他建议,因为我不能 100% 确定我是否走对了这条路。

这是一些简单的虚拟数据:

times = pd.date_range(start='2012-01-01',freq='1W',periods=25)
x = np.array([range(0,20)]).squeeze()
y = np.array([range(20,40)]).squeeze()

data = np.random.randint(3, size=(25,20,20))

ds = xr.DataArray(data, dims=['time', 'y', 'x'], coords = {'time': times, 'y': y, 'x': x})

对于每个 x,y 坐标,我想随时间返回最长的 1 或 2 序列。所以我的输入数组是 3D(时间,x,y),我的输出是 2D(x,y)。 'seq_gufunc' 中的代码受到this thread 的启发。 我的实际数据集要大得多(使用土地利用类而不是 1s、2s 等),这只是更大工作流程的一小部分,我还使用 dask 进行并行处理。所以最后这应该快速有效地运行,这就是为什么我最终试图弄清楚如何让 numba 的 @guvectorize 和 Xarray 的 apply_ufunc 一起工作:


@guvectorize(
    "(int64[:], int64[:])",
    "(n) -> (n)", target='parallel', nopython=True
)
def seq_gufunc(x, out):

    f_arr = np.array([False])

    bool_stack = np.hstack((f_arr, (x == 1) | (x == 2), f_arr))

    # Get start, stop index pairs for sequences
    idx_pairs = np.where(np.diff(bool_stack))[0].reshape(-1, 2)

    # Get length of longest sequence
    longest_seq = np.max(np.diff(idx_pairs))

    out[:] = longest_seq


## Input for dim would be: 'time' 
def apply_seq_gufunc(data, dim):

    return xr.apply_ufunc(seq_gufunc,
                          data,
                          input_core_dims=[[dim]],
                          exclude_dims=set((dim,)), 
                          dask="allowed") 

可能存在一些非常明显的错误,希望有人能指出。我很难理解后台实际发生了什么,以及我应该如何设置 @guvectorize 的布局字符串和 apply_ufunc 的参数,以便它做我想要的。


编辑2: 这是有效的解决方案。有关apply_ufuncguvectorize 的参数的更多信息,请参阅@OriolAbril 的答案。还必须实现 if...else... 子句,以防没有匹配的值,并避免引发 ValueError 。

@guvectorize(
    "(int64[:], int64[:])",
    "(n) -> ()", nopython=True
)
def seq_gufunc(x, out):

    f_arr = np.array([False])
    bool_stack = np.hstack((f_arr, (x == 1) | (x == 2), f_arr))

    if np.sum(bool_stack) == 0:
        longest_seq = 0

    else:
        # Get start, stop index pairs for sequences
        idx_pairs = np.where(np.diff(bool_stack))[0].reshape(-1, 2)

        # Get length of longest sequence
        longest_seq = np.max(np.diff(idx_pairs))   

    out[:] = longest_seq


def apply_seq_gufunc(data, dim):

    return xr.apply_ufunc(seq_gufunc,
                          data,
                          input_core_dims=[[dim]],
                          dask="parallelized",
                          output_dtypes=['uint8']
                         )

【问题讨论】:

  • 不能解决问题,但可能对您来说仍然很有趣:guvectorize 不能与 dask.Client 一起使用,以防您打算使用集群左右。至少它没有(不确定他们是否适应了它 - 我需要在某个地方搜索关于它的对话)
  • 嗨,瓦尔!我已经实现了另一个@guvectorize 函数,最初通过dask.Client 使用本地集群,直到发生一些随机段错误。现在我使用.compute(scheduler='single-threaded')作为临时解决方案,似乎(?)可以工作。但很高兴知道究竟是什么导致了这些段错误。有一阵子我发疯了。
  • 是的,我花了一整天的时间在谷歌上搜索可能出了什么问题,直到我在某个地方找到了一个小对话,guvectorize 函数不能传递给调度程序和工作人员......我希望我记得这个线程在哪里,也许它会回到我的脑海中。无论如何,在一台机器上使用 dask 可以正常工作,但你会坚持下去。

标签: python numpy numba python-xarray numpy-ufunc


【解决方案1】:

我会向您指出How to apply a xarray u_function over NetCDF and return a 2D-array (multiple new variables) to the DataSet,近期目标不一样,但详细的描述和示例应该可以澄清问题。

特别是,您使用 time 作为 input_core_dims 是正确的(以确保将其移动到最后一个维度)并且它被正确格式化为列表列表,但是,您不需要 @ 987654325@ 但output_core_dims==[["time"]]

输出与输入具有相同的形状,但是,正如上面链接中所解释的,apply_ufunc 期望它与广播的暗淡具有相同的形状。需要output_core_dims 才能获得apply_ufunc 以期望输出带有暗淡y, x, time

【讨论】:

  • 感谢您的回答。你确定output_core_dims 吗?因为当我像这样使用apply_ufunc 时:xr.apply_ufunc(seq_gufunc, data, input_core_dims=[[dim]], dask="allowed") 那么它将返回一个形状为(20, 20) 的数组(这就是我想要的)。但如果我还设置了output_core_dims=[["time"]],它将返回一个形状为(20, 20, 25) 的数组。但即使我减少了数组,我还是得到了一些奇怪的数字。
  • 经过一番挖掘,我发现我的函数seq_gufunc 一次接收整个数组,形状为(20, 20, 25),而不是形状(1, 1, 25) 中每个x,y 坐标的一个数组,我预期的。我还对包含在已编辑的原始帖子中的代码进行了一些小的更改。
  • 要在一维基础上应用函数(每个 (1,1,25) 块是一维数组)并让 guvectorize 在额外维度上广播,您必须使用 (n) -> (),使用 (m, m, n) -> (m, m) 是实际上阻​​止了所需的广播。然后,您必须使用 time 作为输入核心暗淡,而不使用任何作为输出核心暗淡。
  • 非常感谢!在关注this guide 之后,我意识到昨天使用(m, m, n) -> (m, m) 所犯的错误。但仍然无法弄清楚如何得到一个 2D 数组而不是 3D。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2021-06-27
  • 1970-01-01
相关资源
最近更新 更多