【问题标题】:numpy broadcast from first dimension来自第一维的 numpy 广播
【发布时间】:2014-05-01 10:53:40
【问题描述】:

在 NumPy 中,是否有一种 easy 方法来广播两个维度数组,例如(x,y)(x,y,z)? NumPy 广播通常匹配最后一个维度的维度,因此通常的广播将不起作用(它需要第一个数组具有维度 (y,z))。

背景:我正在处理图像,其中一些是 RGB(形状 (h,w,3)),其中一些是灰度(形状 (h,w))。我生成形状为(h,w) 的alpha 蒙版,我想通过mask * im 将蒙版应用于图像。由于上述问题,这不起作用,所以我最终不得不做例如

mask = mask.reshape(mask.shape + (1,) * (len(im.shape) - len(mask.shape)))

这是丑陋的。代码的其他部分对向量和矩阵进行操作,这也遇到了同样的问题:尝试执行m + v 失败,其中m 的形状为(x,y)v 的形状为(x,)。可以使用例如atleast_3d,但是我必须记住我真正想要的维度。

【问题讨论】:

    标签: python numpy


    【解决方案1】:

    很简单,您只需在较小数组的末尾添加一个单一维度即可。例如,如果 xyz_array 的形状为 (x,y,z) 而 xy_array 的形状为 (x,y),则可以这样做

    xyz_array + np.expand_dims(xy_array, xy_array.ndim)
    

    【讨论】:

      【解决方案2】:

      numpy 函数通常有代码块来检查尺寸,将数组重新整形为兼容的形状,所有这些都在涉及加法或乘法的核心业务之前。他们可能会重塑输出以匹配输入。所以滚动你自己的做类似的操作并没有错。

      不要轻易放弃将变量3 维度旋转到维度开头的想法。这样做利用了numpy 在开始时自动添加维度的事实。

      对于逐个元素的乘法,einsum 非常强大。

      np.einsum('ij...,ij...->ij...',im,mask)
      

      将处理 immask 是 2 或 3 维的任意混合的情况(假设第一个 2 始终兼容。不幸的是,这并不适用于加法或其他操作。

      不久前,我用纯 Python 版本模拟了 einsum。为此,我使用了np.lib.stride_tricks.as_stridednp.nditer。如果您想在混合和匹配维度方面发挥更大的作用,请查看这些函数。

      【讨论】:

        【解决方案3】:

        为什么不只是装饰-处理-取消装饰:

        def flipflop(func):
            def wrapper(a, mask):
                if len(a.shape) == 3:
                    mask = mask[..., None]
                b = func(a, mask)
                return np.squeeze(b)
            return wrapper
        
        @flipflop
        def f(x, mask):
            return x * mask
        

        然后

        >>> N = 12
        >>> gs = np.random.random((N, N))
        >>> rgb = np.random.random((N, N, 3))
        >>> 
        >>> mask = np.ones((N, N))
        >>> 
        >>> f(gs, mask).shape
        (12, 12)
        >>> f(rgb, mask).shape
        (12, 12, 3)
        

        【讨论】:

          【解决方案4】:

          如何使用转置:

          (a.T + c.T).T
          

          【讨论】:

          • 对我来说有点神秘。
          • 这个答案不清楚。符号ac 不会出现在问题中。这需要更多解释。
          • numpy 会自动广播,如果第一维是麻烦制造者,即 (y, z) @ (x, y, z) 将被广播到 (x, y, z) @ (x, y, z )。但是由于第一个数组是 (x, y) 而不是 (y, z),所以必须广播最后一个维度,但 numpy 并没有这样做。因此,我们采用转置结果: (y, x) @ (z, y, x) = (z, y, x) 并再次转置: (x, y, z) Best greetings
          【解决方案5】:

          换个角度:如果你经常遇到这种模式,创建一个实用函数来强制右播可能会很有用:

          def right_broadcasting(arr, target):
              return arr.reshape(arr.shape + (1,) * (target.ndim - arr.ndim))
          

          虽然如果只有两种类型的输入(已经有 3 个暗淡或只有 2 个),我认为最好使用单个 if 语句。

          【讨论】:

            【解决方案6】:

            使用 np.newaxis 进行索引会在该位置创建一个新轴。即

            xyz = #some 3d array
            xy = #some 2d array
            xyz_sum = xyz + xy[:,:,np.newaxis]
            or
            xyz_sum = xyz + xy[:,:,None]
            

            以这种方式编制索引会在此位置创建一个形状为 1、步幅为 0 的轴。

            【讨论】:

            • 是的,但我仍然需要确定目标是 2 轴还是 3 轴。 (实际上我最初尝试过,但它在灰度图像上失败了,分配了 1024*1024*1024 大小的数组并炸毁了我的内存)
            • 在这种情况下,id 在相关代码前面放置一行,以将维数过低的数组“向上转换”到正确的广播模式;像 arr = arr if arr.ndim==3 else arr[...,None]。它仍然是一行额外的代码,但我认为您无法避免这种情况,而且至少它比基于重塑的解决方案更具人类可读性。
            • 在拒绝这种方法之前,您应该尝试了解它产生1024*1024*1024 的原因。我的猜测是它试图做yz[None,:,:]+xy[:,:,None],其中尺寸为1024
            猜你喜欢
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 2019-09-03
            • 1970-01-01
            • 2013-07-08
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            相关资源
            最近更新 更多