在numpy数组中查找多个值的行索引

Oct*_*lus 14 python arrays numpy

我有一个数组X:

X = np.array([[4,  2],
              [9,  3],
              [8,  5],
              [3,  3],
              [5,  6]])
Run Code Online (Sandbox Code Playgroud)

我希望在这个数组中找到几个值的行的索引:

searched_values = np.array([[4, 2],
                            [3, 3],
                            [5, 6]])
Run Code Online (Sandbox Code Playgroud)

对于这个例子,我想要一个结果,如:

[0,3,4]
Run Code Online (Sandbox Code Playgroud)

我有一个代码这样做,但我认为它过于复杂:

X = np.array([[4,  2],
              [9,  3],
              [8,  5],
              [3,  3],
              [5,  6]])

searched_values = np.array([[4, 2],
                            [3, 3],
                            [5, 6]])

result = []

for s in searched_values:
    idx = np.argwhere([np.all((X-s)==0, axis=1)])[0][1]
    result.append(idx)

print(result)
Run Code Online (Sandbox Code Playgroud)

我找到了类似问题的答案,但它仅适用于1d阵列.

有没有办法以更简单的方式做我想做的事情?

Div*_*kar 22

方法#1

一种方法是使用NumPy broadcasting,像这样 -

np.where((X==searched_values[:,None]).all(-1))[1]
Run Code Online (Sandbox Code Playgroud)

方法#2

一种内存有效的方法是将每一行转换为线性索引等价物,然后使用np.in1d,如下所示 -

dims = X.max(0)+1
out = np.where(np.in1d(np.ravel_multi_index(X.T,dims),\
                    np.ravel_multi_index(searched_values.T,dims)))[0]
Run Code Online (Sandbox Code Playgroud)

方法#3

使用np.searchsorted和使用相同的转换为线性索引等价的哲学的另一种内存有效方法就像这样 -

dims = X.max(0)+1
X1D = np.ravel_multi_index(X.T,dims)
searched_valuesID = np.ravel_multi_index(searched_values.T,dims)
sidx = X1D.argsort()
out = sidx[np.searchsorted(X1D,searched_valuesID,sorter=sidx)]
Run Code Online (Sandbox Code Playgroud)

请注意,这种np.searchsorted方法假定存在一个匹配,从各行searched_valuesX.


np.ravel_multi_index工作怎么样?

此函数为我们提供线性索引等效数字.它接受一个2D数组n-dimensional indices,设置为列和n维网格本身的形状,在这些网格上将映射这些索引,并计算等效线性索引.

让我们使用我们对手头问题的输入.以输入为例X,记下第一行.因为,我们试图将每一行转换X为它的线性索引等价,并且因为np.ravel_multi_index假设每列作为一个索引元组,我们需要X在进入函数之前进行转置.因为,X在这种情况下每行的元素数量是2,要映射到的n维网格将是2D.每行有3个元素X,它将是3D用于映射的网格等等.

要了解此函数如何计算线性指数,请考虑第一行X-

In [77]: X
Out[77]: 
array([[4, 2],
       [9, 3],
       [8, 5],
       [3, 3],
       [5, 6]])
Run Code Online (Sandbox Code Playgroud)

我们将n维网格的形状设为dims-

In [78]: dims
Out[78]: array([10,  7])
Run Code Online (Sandbox Code Playgroud)

让我们创建二维网格,看看该映射是如何工作的,线性索引是用以下方法计算的np.ravel_multi_index:

In [79]: out = np.zeros(dims,dtype=int)

In [80]: out
Out[80]: 
array([[0, 0, 0, 0, 0, 0, 0],
       [0, 0, 0, 0, 0, 0, 0],
       [0, 0, 0, 0, 0, 0, 0],
       [0, 0, 0, 0, 0, 0, 0],
       [0, 0, 0, 0, 0, 0, 0],
       [0, 0, 0, 0, 0, 0, 0],
       [0, 0, 0, 0, 0, 0, 0],
       [0, 0, 0, 0, 0, 0, 0],
       [0, 0, 0, 0, 0, 0, 0],
       [0, 0, 0, 0, 0, 0, 0]])
Run Code Online (Sandbox Code Playgroud)

让我们设置第一个索引元组X,即从X网格的第一行-

In [81]: out[4,2] = 1

In [82]: out
Out[82]: 
array([[0, 0, 0, 0, 0, 0, 0],
       [0, 0, 0, 0, 0, 0, 0],
       [0, 0, 0, 0, 0, 0, 0],
       [0, 0, 0, 0, 0, 0, 0],
       [0, 0, 1, 0, 0, 0, 0],
       [0, 0, 0, 0, 0, 0, 0],
       [0, 0, 0, 0, 0, 0, 0],
       [0, 0, 0, 0, 0, 0, 0],
       [0, 0, 0, 0, 0, 0, 0],
       [0, 0, 0, 0, 0, 0, 0]])
Run Code Online (Sandbox Code Playgroud)

现在,为了看到刚刚设置的元素的线性索引等价,让我们展平并np.where用来检测它1.

In [83]: np.where(out.ravel())[0]
Out[83]: array([30])
Run Code Online (Sandbox Code Playgroud)

如果考虑行主要排序,也可以计算这个.

让我们使用np.ravel_multi_index并验证那些线性指数 -

In [84]: np.ravel_multi_index(X.T,dims)
Out[84]: array([30, 66, 61, 24, 41])
Run Code Online (Sandbox Code Playgroud)

因此,我们将具有对应于每个索引元组的线性索引X,即来自每个索引元组X.

选择尺寸np.ravel_multi_index以形成独特的线性指数

现在,将每行X作为n维网格的索引元组并将每个这样的元组转换为标量的思想背后的想法是具有对应于唯一元组的唯一标量,即唯一的行X.

让我们再看看X-

In [77]: X
Out[77]: 
array([[4, 2],
       [9, 3],
       [8, 5],
       [3, 3],
       [5, 6]])
Run Code Online (Sandbox Code Playgroud)

现在,正如上一节所讨论的,我们将每一行都视为索引元组.在每个这样的索引元组内,第一个元素将表示n-dim网格的第一个轴,第二个元素将是网格的第二个轴,依此类推,直到每行的最后一个元素为止X.实质上,每列代表网格的一个维度或轴.如果我们要将所有元素映射X到相同的n-dim网格上,我们需要考虑这种建议的n-dim网格的每个轴的最大拉伸.假设我们正在处理正数X,那么这样的延伸将是X+ 1 中每列的最大值.这+ 1是因为Python遵循0-based索引.因此,例如,X[1,0] == 9将映射到建议网格的第10行.同样,X[4,1] == 6将转到7th该网格.

所以,对于我们的样本案例,我们有 -

In [7]: dims = X.max(axis=0) + 1 # Or simply X.max(0) + 1

In [8]: dims
Out[8]: array([10,  7])
Run Code Online (Sandbox Code Playgroud)

因此,我们需要一个至少(10,7)为我们的样本案例的形状的网格.沿着尺寸的更长的长度不会受到伤害,并且也会给我们独特的线性指数.

结束语:这里需要注意的一件重要事情是,如果我们有负数X,我们需要在每列中添加适当的偏移量,X以便在使用之前将这些索引元组作为正数np.ravel_multi_index.

  • @MaxU 看看在 `np.ravel_multi_index` 上添加的部分是否有意义!:) (2认同)
  • @Divakar,太完美了!非常感谢!最后我明白了为什么他们在文档的例子中使用了 `(7,6)`。 (2认同)
  • @Divakar,谢谢!在你用零做了一个例子之后已经很清楚了,但现在它非常清晰...... ;) 很遗憾我不能第二次投票 (2认同)

unu*_*tbu 7

另一种方法是使用asvoid(下面)view每行作为dtype 的单个void.这将2D阵列缩减为1D阵列,因此允许您np.in1d像往常一样使用:

import numpy as np

def asvoid(arr):
    """
    Based on http://stackoverflow.com/a/16973510/190597 (Jaime, 2013-06)
    View the array as dtype np.void (bytes). The items along the last axis are
    viewed as one value. This allows comparisons to be performed which treat
    entire rows as one value.
    """
    arr = np.ascontiguousarray(arr)
    if np.issubdtype(arr.dtype, np.floating):
        """ Care needs to be taken here since
        np.array([-0.]).view(np.void) != np.array([0.]).view(np.void)
        Adding 0. converts -0. to 0.
        """
        arr += 0.
    return arr.view(np.dtype((np.void, arr.dtype.itemsize * arr.shape[-1])))

X = np.array([[4,  2],
              [9,  3],
              [8,  5],
              [3,  3],
              [5,  6]])

searched_values = np.array([[4, 2],
                            [3, 3],
                            [5, 6]])

idx = np.flatnonzero(np.in1d(asvoid(X), asvoid(searched_values)))
print(idx)
# [0 3 4]
Run Code Online (Sandbox Code Playgroud)


Eel*_*orn 7

numpy_indexed包(免责声明:我是它的作者)包含的功能用于有效地执行这样的操作(也使用引擎盖下searchsorted)。在功能方面,它相当于 list.index 的矢量化等价物:

import numpy_indexed as npi
result = npi.indices(X, searched_values)
Run Code Online (Sandbox Code Playgroud)

请注意,使用 'missing' kwarg,您可以完全控制丢失项目的行为,它也适用于 nd-arrays(fi;图像堆栈)。

更新:使用与@RikX=[520000,28,28]和相同的形状searched_values=[20000,28,28],它在 中运行0.8064 secs,使用 missing=-1 来检测和表示 X 中不存在的条目。