在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((Xs)==0, axis=1)])[0][1] result.append(idx) print(result)
我发现这个答案是类似的问题,但它只适用于1D阵列。
有没有办法以更简单的方式做我想要的?
方法#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(XT,dims),\ np.ravel_multi_index(searched_values.T,dims)))[0]
方法#3
另一种使用np.searchsorted
并且具有相同转换为线性索引等效原理的高效存储方法就像所谓的“
dims = X.max(0)+1 X1D = np.ravel_multi_index(XT,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维网格本身的形状,并计算等效的线性索引。
让我们用手中的问题来解决问题。 以输入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维网格的形状,
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
并使用np.where
来检测1
。
In [83]: np.where(out.ravel())[0] Out[83]: array([30])
如果考虑行主排序,这也可以被计算。
我们使用np.ravel_multi_index
并验证这些线性索引 –
In [84]: np.ravel_multi_index(XT,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-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])
因此,我们需要一个至少具有(10,7)
形状的网格作为示例。 维度上的更多长度不会受到伤害,也会给我们提供独特的线性索引。
结束语:这里需要注意的一点是,如果我们在X
有负数,那么在使用np.ravel_multi_index
之前,需要在X
每一列上添加适当的偏移量,以使这些索引元组成为正数。
另一种选择是使用asvoid
(在下面)将每行视为void
asvoid
的单个值。 这将二维数组减少为一维数组,因此可以像往常一样使用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]
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()]
如果你想要一个平面列表(假设每个搜索值只有一个匹配)。
这是一个非常快速的解决方案,使用numpy和hashlib可以很好地扩展。 它可以在几秒钟内处理大尺寸矩阵或图像。 我在2秒内在CPU上使用了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]]
numpy_indexed包(免责声明:我是它的作者)包含有效地执行这些操作的功能(也使用搜索引擎盖下)。 就功能而言,它充当了list.index的矢量化等价物:
import numpy_indexed as npi result = npi.indices(X, searched_values)
请注意,使用'缺少'kwarg,你可以完全控制丢失项目的行为,它也适用于nd数组(fi;堆栈图像)。
更新:使用与Rik X = [520000,28,28]和Searches_values = [20000,28,28]相同的形状,运行时间为0.8064秒,使用missing = -1来检测并表示不存在于X中的条目。