Numpy 广播到第 4 维:... vs. : vs None

Numpy broadcasting to the 4th dimension: ... vs. : vs None

在蒙特卡洛模拟中,我有以下 7 张扑克牌,供 2 名玩家和 3 种不同的蒙特卡洛运行。

self.cards:

array([[[  6.,  12.],
    [  1.,   6.],
    [  3.,   3.],
    [  8.,   8.],
    [  1.,   1.],
    [  4.,   4.],
    [  2.,   2.]],

   [[  6.,   7.],
    [  1.,   1.],
    [  3.,   3.],
    [  2.,   2.],
    [ 12.,  12.],
    [  5.,   5.],
    [ 10.,  10.]],

   [[  6.,   3.],
    [  1.,  11.],
    [  2.,   2.],
    [  6.,   6.],
    [ 12.,  12.],
    [  6.,   6.],
    [  7.,   7.]]])

对应的花色是:

self.suits

array([[[ 2.,  1.],
    [ 1.,  2.],
    [ 2.,  2.],
    [ 2.,  2.],
    [ 1.,  1.],
    [ 2.,  2.],
    [ 2.,  2.]],

   [[ 2.,  0.],
    [ 1.,  3.],
    [ 2.,  2.],
    [ 0.,  0.],
    [ 1.,  1.],
    [ 1.,  1.],
    [ 1.,  1.]],

   [[ 2.,  2.],
    [ 1.,  0.],
    [ 3.,  3.],
    [ 2.,  2.],
    [ 1.,  1.],
    [ 1.,  1.],
    [ 1.,  1.]]])

现在我想 'merge' 数组,将卡片数组扩展到第 4 维,大小为 4: 0 包含所有花色==1, 1 所有花色==2 , 2 所有花色 ==3 和 3 所有花色 ==4

我可以轻松创建 4 个不同的数组:

club_cards=(self.suits == 1) * self.cards 
diamond_cards=(self.suits == 2) * self.cards 
heart_cards=(self.suits == 3) * self.cards 
spade_cards=(self.suits == 4) * self.cards

然后将它们堆叠在一起:

stacked_array=np.stack((club_cards,diamond_cards, heart_cards, spade_cards),axis=0)

预期结果的形状为 (4, 3, 8, 2)

array([[[[  1.,  12.],
         [  1.,   1.],
         [  0.,   0.],
         [  0.,   0.],
         [  0.,   0.],
         [  0.,   0.],
         [  0.,   0.],
         [-11.,   0.]],

        [[ 12.,  12.],
         [ 10.,  10.],
         [  5.,   5.],
         [  1.,   0.],
         [  0.,   0.],
         [  0.,   0.],
         [  0.,   0.],
         [  0.,   0.]],

        [[ 12.,  12.],
         [  7.,   7.],
         [  6.,   6.],
         [  1.,   0.],
         [  0.,   0.],
         [  0.,   0.],
         [  0.,   0.],
         [  0.,   0.]]],


       [[[  8.,   8.],
         [  6.,   6.],
         [  4.,   4.],
         [  3.,   3.],
         [  2.,   2.],
         [  0.,   0.],
         [  0.,   0.],
         [ -4.,  -4.]],

        [[  6.,   3.],
         [  3.,   0.],
         [  0.,   0.],
         [  0.,   0.],
         [  0.,   0.],
         [  0.,   0.],
         [  0.,   0.],
         [ -6.,  -9.]],

        [[  6.,   6.],
         [  6.,   3.],
         [  0.,   0.],
         [  0.,   0.],
         [  0.,   0.],
         [  0.,   0.],
         [  0.,   0.],
         [ -6.,  -6.]]],


       [[[  0.,   0.],
         [  0.,   0.],
         [  0.,   0.],
         [  0.,   0.],
         [  0.,   0.],
         [  0.,   0.],
         [  0.,   0.],
         [-12., -12.]],

        [[  0.,   1.],
         [  0.,   0.],
         [  0.,   0.],
         [  0.,   0.],
         [  0.,   0.],
         [  0.,   0.],
         [  0.,   0.],
         [-12., -11.]],

        [[  2.,   2.],
         [  0.,   0.],
         [  0.,   0.],
         [  0.,   0.],
         [  0.,   0.],
         [  0.,   0.],
         [  0.,   0.],
         [-10., -10.]]],


       [[[  0.,   0.],
         [  0.,   0.],
         [  0.,   0.],
         [  0.,   0.],
         [  0.,   0.],
         [  0.,   0.],
         [  0.,   0.],
         [-12., -12.]],

        [[  0.,   0.],
         [  0.,   0.],
         [  0.,   0.],
         [  0.,   0.],
         [  0.,   0.],
         [  0.,   0.],
         [  0.,   0.],
         [-12., -12.]],

        [[  0.,   0.],
         [  0.,   0.],
         [  0.,   0.],
         [  0.,   0.],
         [  0.,   0.],
         [  0.,   0.],
         [  0.,   0.],
         [-12., -12.]]]])

虽然这在上述情况下可能有意义,但并非总是可行,尤其是当需要堆叠超过 4 个情况时,这引出了我的问题:

我如何通过广播来做到这一点?下面是我的具体问题:

我已经尝试了一些东西。

  1. 让我们专注于第一步得到花色的布尔值==np.arange(4)(第二步只是与需要广播的牌相乘与西装相同)。我的理解是我们想为 suits 数组添加一个维度,所以我们不应该用 3 点符号来表示这一点:self.suits[...,:,:,:]==np.arange(4)?相反,以下内容似乎几乎可以工作:self.suits[:,:,:,None]==np.arange(4)(除了它在错误的位置添加了维度)。以下内容也不起作用:self.suits[None,:,:,:]==np.arange(4)。如何在第一维扩展数组,使结果与上述堆栈中的结果相同?

  2. 什么情况下需要...,什么时候需要None?我希望使用 ...,因为这表示需要根据需要扩展此维度(在本例中为 4)?为什么这看起来不正确,而是使用 None?

您正在沿 axis=0 堆叠个人卡片结果。因此,当移植到基于广播的解决方案时,我们可以在 4D 数组中创建这些标量 1, 2, 3, 4 的范围数组,除第一个轴外,所有轴都是单一维度(长度 = 1 的暗淡)。可能有不同的方法来创建这样的 4D 数组。一种方法是:np.arange(1,5)[:,None,None,None],我们用 np.arange 创建一个 1D 数组,然后简单地添加三个单例 dims 作为最后三个 np.newaxis/None

我们将此 4D 数组与 b 进行相等比较,这将允许内部 broadcastingb 元素沿着最后三个维度。然后,我们将它与 a 相乘,就像在原始代码中所做的那样,得到所需的输出。

因此,实施将是 -

out = a*(b == np.arange(1,5)[:,None,None,None]) 

When/how 使用 ...(省略号):

我们使用 ...(省略号),当尝试将新轴添加到多维数组中时,我们不想为每个维度指定 colons。因此,要使 a 成为一个 4D 数组,最后一个 dim 是一个单例,我们会这样做:a[:,:,:,None]。打字太多了!所以,我们在那里使用 ... 来帮助我们:a[...,None]。请注意,无论维数如何,都会使用此 ... 符号。因此,如果 a 是一个 5D 数组,并且要在其中添加一个新轴作为最后一个轴,我们将执行 a[:,:,:,:,:,None] 或简单地使用省略号:a[...,None]。不错吧!