广播二维数组的一列与多列的比较

broadcasting a comparison of a column of 2d array with multiple columns

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

阅读 some docs 数组广播后,我仍然不太确定正确的方法是什么。

示例: 假设我有一个二维数组,其中包含每场比赛(列)中每个球员(行)的进球。

# 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] returns ValueError: operands could not be broadcast together with shapes (5,) (5,2)

我试过的方法: 我知道我可以用 np.newaxis 手动将 (5,) 拉伸成 (5,2)。所以这有效:

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

print(personalBest) # returns desired solution

有没有更简单、更惯用的 numpy 方式来写这个?

你可以这样做 -

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

让我们逐步完成。

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

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 步: 接下来,我们实际对数组的所有列执行比较,跳过最后一列本身,因为这是之前获得的切片版本的一部分 -

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

只关注 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] 不是。