方法一
一种方法是使用NumPy广播
,代码如下 -
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]
第三种方法
另一种使用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
匹配的值。
该函数给出线性索引等效的数值。它接受一个 2D
数组的n维索引,设置为列,并将该 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]])
我们将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维网格的索引元组,并将每个这样的元组转换为标量的背后思想是使唯一的标量与唯一的元组(即X
中的唯一行)对应。
让我们再看一下X
-
In [77]: X
Out[77]:
array([[4, 2],
[9, 3],
[8, 5],
[3, 3],
[5, 6]])
现在,正如前面讨论的那样,我们将每一行视为索引元组。在每个这样的索引元组中,第一个元素表示n维网格的第一轴,第二个元素是网格的第二轴,以此类推,直到X
中每一行的最后一个元素。实质上,每一列表示网格的一个维度或轴。如果我们要将X
中的所有元素映射到相同的n维网格上,就需要考虑这样一个拟议的n维网格中每个轴的最大伸展。假设我们正在处理X
中的正数,这样一个伸展就是X
中每列的最大值+1。这里的+1
是因为Python遵循基于0的索引。因此,例如X [1,0] == 9
会被映射到建议网格的第10行。同样,X [4,1] == 6
会进入该网格的第7
列。
因此,对于我们的示例情况,我们有 -
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
。
append
的for
循环,那就更简单了。 - Julien