如何在二维数组中使用numpy.searchsorted进行向量化

4

我有一个二维数组(a)来查找,还有一个数组(v)用于查找应插入元素的索引:

import numpy as np

# [EDIT] Add more records which contain NaNs
a = np.array(
[[0., 923.9943, 996.8978, 1063.9064, 1125.639, 1184.3985, 1259.9854, 1339.6107, 1503.4462, 2035.6527],
 [0., 1593.6196, 1885.2442, 2152.956, 2419.0038, 2843.517, 3551.225, 5423.009, 18930.8694, 70472.4002],
 [0., 1593.6196, 1885.2442, 2152.956, 2419.0038, 2843.517, 3551.225, 5423.009, 18930.8694, 70472.4002],
 [0., 1084.8388, 1132.6918, 1172.2278, 1215.7986, 1259.062, 1334.4778, 1430.738, 1650.4502, 3966.1578],
 [0., 1084.8388, 1132.6918, 1172.2278, 1215.7986, 1259.062, 1334.4778, 1430.738, 1650.4502, 3966.1578],
 [np.nan, np.nan, np.nan, np.nan, np.nan, np.nan, np.nan, np.nan, np.nan, np.nan],
 [0., 923.9943, 996.8978, 1063.9064, 1125.639, 1184.3985, 1259.9854, 1339.6107, 1503.4462, 2035.6527],
 [0., 1593.6196, 1885.2442, 2152.956, 2419.0038, 2843.517, 3551.225, 5423.009, 18930.8694, 70472.4002],
 [0., 1593.6196, 1885.2442, 2152.956, 2419.0038, 2843.517, 3551.225, 5423.009, 18930.8694, 70472.4002],
 [0., 1084.8388, 1132.6918, 1172.2278, 1215.7986, 1259.062, 1334.4778, 1430.738, 1650.4502, 3966.1578],
 [0., 1084.8388, 1132.6918, 1172.2278, 1215.7986, 1259.062, 1334.4778, 1430.738, 1650.4502, 3966.1578]])

v = np.array([641.954, 56554.498, 168078.307, 1331.692, 2233.327, 1120.03, 641.954, 56554.498, 168078.307, 1331.692, 2233.327])

这是我想要得到的结果:

[1, 9, 10, 6, 9, 0, 1, 9, 10, 6, 9]

很明显,使用 for 循环我可以像这样索引数组 a 和 v:
for i, _ in enumerate(a):
    print(np.searchsorted(a[i], v[i]))

有没有更高效的矢量化方法来完成这个任务?


那么,它可以整行都是NaN吗?它也可以只有几个元素而不是整行都是NaN吗? - Divakar
@Divakar,一行中几个NaN的可能性不存在。如果存在NaN,则整个行都是NaN。 - kokon
考虑使用Jax的vmap。 - Leo Ware
1个回答

6

numpy向量搜索的启发,这里介绍一种适用于2D1D数组的方法:

def searchsorted2d(a,b):
    # Inputs : a is (m,n) 2D array and b is (m,) 1D array.
    # Finds np.searchsorted(a[i], b[i])) in a vectorized way by
    # scaling/offsetting both inputs and then using searchsorted

    # Get scaling offset and then scale inputs
    s = np.r_[0,(np.maximum(a.max(1)-a.min(1)+1,b)+1).cumsum()[:-1]]
    a_scaled = (a+s[:,None]).ravel()
    b_scaled = b+s

    # Use searchsorted on scaled ones and then subtract offsets
    return np.searchsorted(a_scaled,b_scaled)-np.arange(len(s))*a.shape[1]

给定示例的输出 -

In [101]: searchsorted2d(a,v)
Out[101]: array([ 1,  9, 10,  6,  9])

所有行都是NaN的情况

为了使其适用于所有行都是NaN的情况,我们需要进行以下几个步骤 -

valid_mask = ~np.isnan(a).any(1)
out = np.zeros(len(a), dtype=int)
out[valid_mask] = searchsorted2d(a[valid_mask],v[valid_mask])

非常感谢。这是一个很好的想法,并且在样本中运行良好,但是在包含NaN的实际数据中,出现了一些问题。您能给我一些处理这个问题的想法吗? - kokon
@kokon 请发布样本数据、期望输出以及最好包含循环的可行解决方案。 - Divakar
由于此评论中的字符数量有限,因此我已更新原帖中的示例以包括NaN记录。目前使用您的解决方案,在NaN记录之后的数据返回负数,这与预期输出不同:array([ 1, 9, 10, 6, 9, 0, -10, -20, -30, -40, -50])。 - kokon

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接