如果您的查询数组足够密集,您可以反转计算:找到一个与matrix 大小相同的数组,它为每个位置提供同一行中下一个非零元素的索引。那么你的问题就变成了将query 应用于这个索引数组的问题之一,numpy 直接支持。
实际上找到左索引要容易得多,所以让我们从它开始。我们可以将matrix 转换成这样的索引数组:
r, c = np.nonzero(matrix)
left_ind = np.zeros(matrix.shape, dtype=int)
left_ind[r, c] = c
现在您可以使用 np.maximum 找到前一个非零元素的索引,类似于此答案中的方法:https://stackoverflow.com/a/48252024/2988730:
np.maximum.accumulate(left_ind, axis=1, out=left_ind)
现在您可以直接索引到ind 以获取之前的非零列索引:
left_ind[query[:, 0], query[:, 1]]
或
left_ind[tuple(query.T)]
现在要对正确的索引做同样的事情,您需要反转数组。但是随后您的索引不再上升,并且您可能会覆盖第一列中的任何零。为了解决这个问题,除了反转数组之外,您还需要反转索引的顺序:
right_ind = np.zeros(matrix.shape, dtype=int)
right_ind[r, c] = matrix.shape[1] - c
您也可以使用大于matrix.shape[1] 的任何数字作为常量。重要的是反向索引都大于零,因此np.maximum.accumulate 会覆盖零。现在您可以在反转数组上以相同的方式使用np.maximum.accumulate:
right_ind = matrix.shape[1] - np.maximum.accumulate(right_ind[:, ::-1], axis=1)[:, ::-1]
在这种情况下,我建议不要使用out=right_ind,因为right_ind[:, ::-1] 是同一个缓冲区的视图。该操作是缓冲的,但如果您的行大小足够大,您可能会无意中覆盖数据。
现在你可以像以前一样索引数组了:
right_ind[(*query.T,)]
在这两种情况下,您都需要与query 的第一列堆叠,因为那是行键:
>>> row, col = query.T
>>> np.stack((row, left_ind[row, col]), -1)
array([[0, 0],
[2, 0],
[1, 1],
[0, 0]])
>>> np.stack((row, right_ind[row, col]), -1)
array([[0, 3],
[2, 4],
[1, 4],
[0, 3]])
>>> np.stack((row, left_ind[row, col], right_ind[row, col]), -1)
array([[0, 0, 3],
[2, 0, 4],
[1, 1, 4],
[0, 0, 3]])
如果您计划一次或在整个程序中对数组中的大部分行进行采样,这将帮助您加快速度。另一方面,如果您只需要访问一个小子集,则可以将此技术仅应用于您需要的行。