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

时间:2016-07-30 12:34:59

标签: python arrays numpy

我有一个数组X:

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

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

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

对于这个例子,我希望得到如下结果:

[0,3,4]

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

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)

我发现this answer有类似的问题,但它仅适用于1d数组。

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

6 个答案:

答案 0 :(得分:20)

方法#1

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

np.where((X==searched_values[:,None]).all(-1))[1]

方法#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]

方法#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)]

请注意,此np.searchsorted方法假定searched_valuesX的每一行都匹配。

np.ravel_multi_index如何运作?

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

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

要了解此函数如何计算线性索引,请考虑X -

的第一行
In [77]: X
Out[77]: 
array([[4, 2],
       [9, 3],
       [8, 5],
       [3, 3],
       [5, 6]])

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

In [78]: dims
Out[78]: array([10,  7])

让我们创建二维网格,看看该映射是如何工作的,线性指数是用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]])

让我们从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]])

现在,要查看刚刚设置的元素的线性索引等值,让我们展平并使用np.where来检测1

In [83]: np.where(out.ravel())[0]
Out[83]: array([30])

如果考虑行主要排序,也可以计算。

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

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

因此,我们将得到与X的每个索引元组相对应的线性索引,即来自X的每一行。

选择np.ravel_multi_index的尺寸以形成唯一的线性指数

现在,将X的每一行视为n维网格的索引元组并将每个这样的元组转换为标量的思想背后的想法是使唯一的标量对应于唯一的元组,即{{{1}中的唯一行1}}。

让我们再看看X -

X

现在,正如上一节所讨论的,我们将每一行都视为索引元组。在每个这样的索引元组中,第一个元素将表示n-dim网格的第一个轴,第二个元素将是网格的第二个轴,依此类推,直到In [77]: X Out[77]: array([[4, 2], [9, 3], [8, 5], [3, 3], [5, 6]]) 中每行的最后一个元素。实质上,每列代表网格的一个维度或轴。如果我们要将X中的所有元素映射到相同的n-dim网格上,我们需要考虑这样一个建议的n-dim网格的每个轴的最大拉伸。假设我们正处理X中的正数,这样的延伸将是X + 1中每列的最大值。X是因为Python遵循+ 1索引。因此,例如 0-based将映射到建议网格的第10行。同样, X[1,0] == 9会转到该网格的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]) 形状的网格。沿着尺寸的更长的长度不会受到伤害,并且也会给我们独特的线性指数。

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

答案 1 :(得分:7)

另一种方法是将每行asvoid(下方)view用作void dtype。这将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]

答案 2 :(得分:1)

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

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

result = [[i for i,row in enumerate(X) if (s==row).all()] for s in S]

result = [i for s in S for i,row in enumerate(X) if (s==row).all()]

如果你想要一个平面列表(假设每个搜索值只有一个匹配)。

答案 3 :(得分:1)

numpy_indexed包(免责声明:我是其作者)包含有效执行此类操作的功能(也使用引擎下的搜索)。在功能方面,它充当list.index:

的向量化等价物
import numpy_indexed as npi
result = npi.indices(X, searched_values)

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

更新:使用与Rik X = [520000,28,28]和searching_values = [20000,28,28]相同的形状,它运行0.8064秒,使用missing = -1来检测并表示不存在的条目X

答案 4 :(得分:1)

这是一个非常快速的解决方案,使用numpy和hashlib可以很好地扩展。它可以在几秒钟内处理大尺寸矩阵或图像。我在我的CPU上在2秒内在520000 X(28 X 28)阵列和20000 X(28 X 28)上使用它

代码:

import numpy as np
import hashlib


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

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

#hash using sha1 appears to be efficient
xhash=[hashlib.sha1(row).digest() for row in X]
yhash=[hashlib.sha1(row).digest() for row in searched_values]

z=np.in1d(xhash,yhash)  

##Use unique to get unique indices to ind1 results
_,unique=np.unique(np.array(xhash)[z],return_index=True)

##Compute unique indices by indexing an array of indices
idx=np.array(range(len(xhash)))
unique_idx=idx[z][unique]

print('unique_idx=',unique_idx)
print('X[unique_idx]=',X[unique_idx])

输出:

unique_idx= [4 3 0]
X[unique_idx]= [[5 6]
 [3 3]
 [4 2]]

答案 5 :(得分:0)

另一种方法是像这样使用cdist中的scipy.spatial.distance函数:

np.nonzero(cdist(X, searched_values) == 0)[0]

基本上,我们获得X的行号,该行号与searched_values中的行的距离为零,这意味着它们是相等的。如果您将行视为坐标,这是有道理的。