实际问题出现在某些机器学习应用程序中,数据有点复杂。所以这是一个能够捕捉问题本质的MWE:
我有两个阵列如下:
L = np.arange(12).reshape(4,3)
M = np.arange(12).reshape(6,2)
现在,我想找到L
中的行R,这样M
中存在一些由R中除了最后一个元素之外的所有元素组成的行。
从上面的示例代码中,L
和M
如下所示:
array([[ 0, 1, 2], # L
[ 3, 4, 5],
[ 6, 7, 8],
[ 9, 10, 11]])
array([[ 0, 1], # M
[ 2, 3],
[ 4, 5],
[ 6, 7],
[ 8, 9],
[10, 11]])
我想从这些L
中标记的行,作为一个numpy数组:
array([[ 0, 1, 2],
[ 6, 7, 8]])
如果我将L
和M
表示为python列表,我会这样做:
L = [[0, 1, 2], [3, 4, 5], [6, 7, 8], [9, 10, 11]]
M = [[0, 1], [2, 3], [4, 5], [6, 7], [8, 9], [10, 11]]
answer = [R for R in L if R[:-1] in M]
现在,我知道我可以在numpy中使用类似的列表理解并将结果转换为数组,numpy就像它一样棒,可能有更优雅的方式来做我不知道的事情。
我试着调查np.where
(以获得所需的索引,然后我可以将L替换为L),但这似乎不能满足我的需要。
我很感激任何帮助
答案 0 :(得分:4)
好的,我想我明白了。诀窍是向M
添加另一个维度,然后您可以使用广播:
M.shape += (1,)
E = np.all(L[:,:-1].T == M, 1)
你得到一个6x4布尔矩阵E
,它给你比较L的所有行和M的所有行的结果。
从这里很容易完成:
result = L[np.any(E,0)]
通过这种方式,解决方案得以简化,您不需要任何lambda函数或"隐式循环" (例如np.apply_along_axis()
)。
是的,numpy矢量化很漂亮(但有时你必须认为很抽象)......
答案 1 :(得分:3)
与Bitwise的答案非常相似:
def fn(a):
return lambda b: np.all(a==b, axis=1)
matches = np.apply_along_axis(fn(M), 1, L[:,:2])
result = L[np.any(matches, axis=1)]
引擎盖下发生的事情是这样的(我将使用Bitwise的例子,这更容易证明):
>>> M
array([[ 0, 1],
[ 2, 3],
[ 4, 5],
[ 6, 7],
[ 8, 9],
[10, 11]])
>>> M.shape+=(1,)
>>> M
array([[[ 0],
[ 1]],
[[ 2],
[ 3]],
[[ 4],
[ 5]],
[[ 6],
[ 7]],
[[ 8],
[ 9]],
[[10],
[11]]])
这里我们为M数组添加了另一个维度,现在是(6,2,1)。
>>> L2 = L[:,:-1].T
然后我们摆脱2的最后一列,并转置数组,使维度为(2,4)
这就是魔术,M和L2现在可以播放到维度阵列(6,2,4)。
正如numpy的医生所说:
如果一组数组被称为“可广播”到相同的形状 上述规则产生有效结果,即下列之一 真:
The arrays all have exactly the same shape. The arrays all have the same number of dimensions and the length of each dimensions is either a common length or 1. The arrays that have too few dimensions can have their shapes prepended with a dimension of length 1 to satisfy property 2.
实施例
如果a.shape是(5,1),b.shape是(1,6),c.shape是(6,),d.shape是 ()所以d是标量,然后a,b,c和d都可以播放到 尺寸(5,6);和
a acts like a (5,6) array where a[:,0] is broadcast to the other columns, b acts like a (5,6) array where b[0,:] is broadcast to the other rows, c acts like a (1,6) array and therefore like a (5,6) array where c[:] is broadcast to every row, and finally, d acts like a (5,6) array where the single value is repeated.
M [:,:0]将重复4次以填充3 dim,L2将被添加到新的维度并重复6次以填充它。
>>> B = np.broadcast_arrays(L2,M)
>>> B
[array([[[ 0, 3, 6, 9],
[ 1, 4, 7, 10]],
[[ 0, 3, 6, 9],
[ 1, 4, 7, 10]],
[[ 0, 3, 6, 9],
[ 1, 4, 7, 10]],
[[ 0, 3, 6, 9],
[ 1, 4, 7, 10]],
[[ 0, 3, 6, 9],
[ 1, 4, 7, 10]],
[[ 0, 3, 6, 9],
[ 1, 4, 7, 10]]]),
array([[[ 0, 0, 0, 0],
[ 1, 1, 1, 1]],
[[ 2, 2, 2, 2],
[ 3, 3, 3, 3]],
[[ 4, 4, 4, 4],
[ 5, 5, 5, 5]],
[[ 6, 6, 6, 6],
[ 7, 7, 7, 7]],
[[ 8, 8, 8, 8],
[ 9, 9, 9, 9]],
[[10, 10, 10, 10],
[11, 11, 11, 11]]])]
我们现在可以按元素进行比较:
>>> np.equal(*B)
array([[[ True, False, False, False],
[ True, False, False, False]],
[[False, False, False, False],
[False, False, False, False]],
[[False, False, False, False],
[False, False, False, False]],
[[False, False, True, False],
[False, False, True, False]],
[[False, False, False, False],
[False, False, False, False]],
[[False, False, False, False],
[False, False, False, False]]], dtype=bool)
行到行(轴= 1):
>>> np.all(np.equal(*B), axis=1)
array([[ True, False, False, False],
[False, False, False, False],
[False, False, False, False],
[False, False, True, False],
[False, False, False, False],
[False, False, False, False]], dtype=bool)
聚合L's:
>>> C = np.any(np.all(np.equal(*B), axis=1), axis=0)
>>> C
array([ True, False, True, False], dtype=bool)
这为你提供了应用于L的布尔掩码。
>>> L[C]
array([[0, 1, 2],
[6, 7, 8]])
apply_along_axis
将利用相同的功能,但减少L's维度而不是增加M(因此添加隐式循环)。
答案 2 :(得分:0)
>>> import hashlib
>>> fn = lambda xs: hashlib.sha1(xs).hexdigest()
>>> m = np.apply_along_axis(fn, 1, M)
>>> l = np.apply_along_axis(fn, 1, L[:,:-1])
>>> L[np.in1d(l, m)]
array([[0, 1, 2],
[6, 7, 8]])
答案 3 :(得分:0)
>>> print np.array([row for row in L if row[:-1] in M])
[[0 1 2]
[6 7 8]]