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

wqlqzqxt  于 2022-11-10  发布在  其他
关注(0)|答案(8)|浏览(132)

我有一个数组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解决了类似的问题,但它只适用于一维阵列。
有没有办法用更简单的方式做我想做的事?

ghhaqwfi

ghhaqwfi1#

方法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方法假设X中的searched_values中的每一行都有匹配。

np.ravel_multi_index如何工作?

此函数给出了线性指数等效数。它接受由n-dimensional indices组成的2D数组,该数组被设置为列和n维网格本身的形状,这些索引将被Map到该n维网格上,并计算等价的线性索引。
让我们使用手头问题的输入。以输入X为例,注意它的第一行。由于我们正在尝试将X的每一行转换为它的线性索引等效项,并且由于np.ravel_multi_index假设每一列都是一个索引元组,因此在提供给该函数之前,我们需要转置X。因为在本例中,X中每行的元素数是2,所以要Map到的n维网格将是2D。如果X中每行有3个元素,则将使用3D网格进行Map等。
要了解此函数如何计算线性指数,请考虑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])

让我们创建2维网格,看看Map是如何工作的,并使用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维网格的索引元组并将每个这样的元组转换为标量背后的想法是具有对应于唯一元组的唯一标量,即X中的唯一行。
让我们再来看看X-

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

现在,正如上一节所讨论的,我们将每一行视为索引元组。在每个这样的索引元组中,第一个元素将表示n维网格的第一个轴,第二个元素将是该网格的第二个轴,依此类推,直到X中每行的最后一个元素。本质上,每一列代表网格的一个维度或轴。如果我们要将X中的所有元素Map到相同的n维网格上,我们需要考虑这种建议的n维网格的每个轴的最大拉伸。假设我们处理的是X中的正数,这样的伸长量将是X+1中每列的最大值。因此,例如,X[1,0] == 9将Map到建议栅格的第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])

因此,对于我们的样例,我们至少需要一个形状为(10,7)的网格。维度上更多的长度不会有什么坏处,还会给我们提供唯一的线性指数。
结束语:这里需要注意的一件重要事情是,如果我们在X中有负数,我们需要沿着X中的每一列添加适当的偏移量,以便在使用np.ravel_multi_index之前使这些索引元组成为正数。

fcwjkofz

fcwjkofz2#

另一种选择是使用asvoid(下面)到view每行作为void dtype的单个值。这会将二维数组减少为一维数组,从而允许您照常使用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]
guz6ccqo

guz6ccqo3#

numpy_indexed包(免责声明:我是它的作者)包含了有效执行此类操作的功能(还在幕后使用了搜索排序)。就功能而言,它相当于向量化的list.index:

import numpy_indexed as npi
result = npi.indices(X, searched_values)

请注意,使用‘丢失’kwarg,您可以完全控制丢失物品的行为,它也适用于nd数组(fi;图像堆栈)。
更新:使用与@Rik X=[520000,28,28]searched_values=[20000,28,28]相同的形状,它在0.8064 secs中运行,使用Missing=-1来检测和表示X中不存在的条目。

w46czmvw

w46czmvw4#

这里有一个非常快速的解决方案,使用NumPy和hashlib可以很好地进行扩展。它可以在几秒钟内处理大尺寸矩阵或图像。我在520000 X(28 X 28)阵列和20000 X(28 X 28)阵列上使用它,在我的中央处理器上只用了2秒
代码:

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]]
ru9i0ody

ru9i0ody5#

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()]

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

kq0g1dla

kq0g1dla6#

另一种方法是使用scipy.spatial.distance中的cdist函数,如下所示:

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

基本上,我们得到X的行号,这些行号到searched_values的行距为零,这意味着它们是相等的。如果您将行视为坐标,这是有意义的。

kuarbcqp

kuarbcqp7#

我有类似的要求,下面的内容对我很管用:

np.argwhere(np.isin(X, searched_values).all(axis=1))
wqnecbli

wqnecbli8#

以下是我的想法:

def find_points(orig: np.ndarray, search: np.ndarray) -> np.ndarray:
    equals = [np.equal(orig, p).all(1) for p in search]
    exists = np.max(equals, axis=1)
    indices = np.argmax(equals, axis=1)
    indices[exists == False] = -1
    return indices

测试:

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

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

find_points(X, searched_values)

输出:

[0,3,4,-1]

相关问题