广播2d数组的列与多列的比较

时间:2016-09-01 16:58:38

标签: python arrays numpy numpy-broadcasting

在2d ndarray中将一列与其他列进行比较的正确numpy语法是什么?

在阵列广播上阅读some docs后,我仍然不太确定这样做的正确方法是什么。

示例:假设我在每个游戏(列)中有每个玩家(行)得分的2d目标数组。

# goals = number of goals scored by ith player in jth game (NaN if player did not play)
                           # column = game
goals = np.array([ [np.nan, 0,      1],   # row = player
                   [     1, 2,      0],
                   [     0, 0, np.nan],
                   [np.nan, 1,      1],
                   [     0, 0,      1] ])

我想知道,在最后一场比赛中,玩家是否通过比以往任何一场比赛得分更多的目标获得了个人记录,而忽略了她没有出现的比赛(表示为nan)。我希望True仅适用于阵列中的第一个和最后一个玩家。

只需撰写goals[:,2] > goals[:,:2]即可返回ValueError: operands could not be broadcast together with shapes (5,) (5,2)

我尝试了什么:我知道我可以使用(5,)手动将(5,2)拉伸到np.newaxis。所以这有效:

with np.errstate(invalid='ignore'):
  personalBest= ( np.isnan(goals[:,:2]) | 
                  (goals[:,2][:,np.newaxis] > goals[:,:2] ) 
                 ).all(axis=1)

print(personalBest) # returns desired solution

有没有更少的hacky,更具惯用性的numpy方式来写这个?

2 个答案:

答案 0 :(得分:2)

你可以这样做 -

np.flatnonzero((goals[:,None,-1] > goals[:,:-1]).any(1))

让我们逐步完成它。

步骤1:我们在最后一列切片版本上引入了一个新轴,使其保持为2D,最后一个轴为单个维度/轴。我们的想法是将每个元素与该行中除元素本身之外的所有元素进行比较:

In [3]: goals[:,None,-1]
Out[3]: 
array([[  1.],
       [  0.],
       [ nan],
       [  1.],
       [  1.]])

In [4]: goals[:,None,-1].shape # Check the shapes for broadcasting alignment
Out[4]: (5, 1)

In [5]: goals.shape
Out[5]: (5, 3)

步骤2:接下来,我们实际上是对跳过最后一列本身的数组的所有列进行比较,因为它是之前获得的切片版本的一部分 - < / p>

In [7]: goals[:,None,-1] > goals[:,:-1]
Out[7]: 
array([[False,  True],
       [False, False],
       [False, False],
       [False, False],
       [ True,  True]], dtype=bool)

步骤3:然后,我们正在检查每行是否有任何匹配 -

In [8]: (goals[:,None,-1] > goals[:,:-1]).any(axis=1)
Out[8]: array([ True, False, False, False,  True], dtype=bool)

步骤4:最后,使用np.flatnonzero获取匹配的索引 -

In [9]: np.flatnonzero((goals[:,None,-1] > goals[:,:-1]).any(axis=1))
Out[9]: array([0, 4])

答案 1 :(得分:2)

只关注newaxis位:

In [332]: goals = np.arange(12).reshape(3,4)
In [333]: goals[:,2]>goals[:,:2]
...
ValueError: operands could not be broadcast together with shapes (3,) (3,2)

所以我们的目标是制作第一个形状数组(3,1),以便它可以与(3,2)进行广播:

我们可以使用列表或切片进行索引:goals[:,2:3]也适用

In [334]: goals[:,[2]]>goals[:,:2]
Out[334]: 
array([[ True,  True],
       [ True,  True],
       [ True,  True]], dtype=bool)

我们可以明确添加newaxis(常见)

In [335]: goals[:,2][:,None]>goals[:,:2]
Out[335]: 
array([[ True,  True],
       [ True,  True],
       [ True,  True]], dtype=bool)

我们可以将两个索引操作结合起来(这不常见)

In [336]: goals[:,2,None]>goals[:,:2]
Out[336]: 
array([[ True,  True],
       [ True,  True],
       [ True,  True]], dtype=bool)

我们可以明确地重塑:

In [339]: goals[:,2].reshape(-1,1)>goals[:,:2]
Out[339]: 
array([[ True,  True],
       [ True,  True],
       [ True,  True]], dtype=bool)

我认为执行时间差异不大。这些都是很好的numpy代码。

======

如果2个阵列是(3,)和(2,3),我们就不需要这些了。 numpy广播自动将第一个扩展到(1,3)。实际上x[None,:]是自动的,但x[:,None]不是。