2

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

在阅读了一些关于数组广播的 文档后,我仍然不太确定这样做的正确方法是什么。

示例: 假设我有一个二维数组,每个玩家(行)在每场比赛(列)中得分。

# 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

有没有一种不那么老套、更惯用的 numpy 方式来写这个?

4

2 回答 2

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

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])
于 2016-09-01T17:04:10.070 回答
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(common)

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

于 2016-09-01T20:13:23.203 回答