【问题标题】:broadcasting a comparison of a column of 2d array with multiple columns广播一列二维数组与多列的比较
【发布时间】:2016-09-01 16:58:38
【问题描述】:

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

在阅读了somedocs 上的数组广播后,我仍然不太确定这样做的正确方法是什么。

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

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

我尝试了什么:我知道我可以使用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 方式来写这个?

【问题讨论】:

  • 这不是 hacky。 np.newaxis 正是为了这样的目的而提供的。

标签: python arrays numpy array-broadcasting


【解决方案1】:

你可以这样做 -

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

【讨论】:

  • 很好的解释——这个答案把我指向了我读到的reference on array indexing*numpy.newaxis:* The newaxis object can be used in all slicing operations to create an axis of length one. :const: newaxis is an alias for ‘None’, and ‘None’ can be used in place of this with the same result.
  • @C8H10N4O2 非常好!所以,按照这个理论,我们也可以做到goals[:,-1,None],但是随着切片,最后一个轴消失了,所以实际上它变成了goals[:,None,-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 代码。

========

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

【讨论】:

    猜你喜欢
    • 2013-05-19
    • 2018-04-02
    • 1970-01-01
    • 2019-03-02
    • 2022-01-19
    • 2013-09-27
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多