【问题标题】:python numpy roll with paddingpython numpy roll带填充
【发布时间】:2011-02-16 04:48:54
【问题描述】:

我想在 python 中滚动一个 2D numpy,除了我想用零填充末端而不是滚动数据,就像它是周期性的一样。

具体如下代码

import numpy as np

x = np.array([[1, 2, 3], [4, 5, 6]])

np.roll(x, 1, axis=1)

返回

array([[3, 1, 2],[6, 4, 5]])

但我更喜欢的是

array([[0, 1, 2], [0, 4, 5]])

我可以通过一些尴尬的修饰来做到这一点,但我希望有一种方法可以通过快速的内置命令来做到这一点。

谢谢

【问题讨论】:

    标签: python arrays numpy


    【解决方案1】:

    在版本 1.7.0 numpy.pad 中有一个新的 numpy 函数,可以在一行中执行此操作。 Pad 似乎非常强大,可以做的不仅仅是简单的“滚动”。此答案中使用的元组 ((0,0),(1,0)) 表示要填充的矩阵的“边”。

    import numpy as np
    x = np.array([[1, 2, 3],[4, 5, 6]])
    
    print np.pad(x,((0,0),(1,0)), mode='constant')[:, :-1]
    

    给予

    [[0 1 2]
     [0 4 5]]
    

    【讨论】:

    • 如果不明显,这里移动 5 个元素: print np.pad(x,((0,0),(5,0)), mode='constant')[:, :-5]
    • 哇,4 年前的帖子太棒了!
    • 请注意,np.roll 将允许负值,但如果您尝试发送负数,np.pad 将给您ValueError: cannot contain negative values。所以功能不太一样。
    • @sh37211 表示“负”转变:np.pad(x,((0,0),(0,1)), mode='constant')[:, 1:])
    【解决方案2】:

    我认为您不会找到一种更简单的内置方法来执行此操作。修饰对我来说似乎很简单:

    y = np.roll(x,1,axis=1)
    y[:,0] = 0
    

    如果你想让它更直接,那么也许你可以将 roll 函数复制到一个新函数中,然后将其更改为你想要的。 roll() 函数在site-packages\core\numeric.py 文件中。

    【讨论】:

    • 我希望在一行中执行此操作,因为我需要在不同方向多次执行此操作并且我无法破坏 y,但您的建议可能是最好的解决方案。感谢您的帮助。
    • 这应该是公认的答案。而且我认为 np.roll 应该使用关键字参数本地完成。 np.pad 不接受负值,它无论如何都会改变数组的形状,这对我来说太过分了。
    【解决方案3】:

    我刚刚写了以下内容。它可以通过避免zeros_like 并直接计算zeros 的形状来得到更优化。

    import numpy as np
    def roll_zeropad(a, shift, axis=None):
        """
        Roll array elements along a given axis.
    
        Elements off the end of the array are treated as zeros.
    
        Parameters
        ----------
        a : array_like
            Input array.
        shift : int
            The number of places by which elements are shifted.
        axis : int, optional
            The axis along which elements are shifted.  By default, the array
            is flattened before shifting, after which the original
            shape is restored.
    
        Returns
        -------
        res : ndarray
            Output array, with the same shape as `a`.
    
        See Also
        --------
        roll     : Elements that roll off one end come back on the other.
        rollaxis : Roll the specified axis backwards, until it lies in a
                   given position.
    
        Examples
        --------
        >>> x = np.arange(10)
        >>> roll_zeropad(x, 2)
        array([0, 0, 0, 1, 2, 3, 4, 5, 6, 7])
        >>> roll_zeropad(x, -2)
        array([2, 3, 4, 5, 6, 7, 8, 9, 0, 0])
    
        >>> x2 = np.reshape(x, (2,5))
        >>> x2
        array([[0, 1, 2, 3, 4],
               [5, 6, 7, 8, 9]])
        >>> roll_zeropad(x2, 1)
        array([[0, 0, 1, 2, 3],
               [4, 5, 6, 7, 8]])
        >>> roll_zeropad(x2, -2)
        array([[2, 3, 4, 5, 6],
               [7, 8, 9, 0, 0]])
        >>> roll_zeropad(x2, 1, axis=0)
        array([[0, 0, 0, 0, 0],
               [0, 1, 2, 3, 4]])
        >>> roll_zeropad(x2, -1, axis=0)
        array([[5, 6, 7, 8, 9],
               [0, 0, 0, 0, 0]])
        >>> roll_zeropad(x2, 1, axis=1)
        array([[0, 0, 1, 2, 3],
               [0, 5, 6, 7, 8]])
        >>> roll_zeropad(x2, -2, axis=1)
        array([[2, 3, 4, 0, 0],
               [7, 8, 9, 0, 0]])
    
        >>> roll_zeropad(x2, 50)
        array([[0, 0, 0, 0, 0],
               [0, 0, 0, 0, 0]])
        >>> roll_zeropad(x2, -50)
        array([[0, 0, 0, 0, 0],
               [0, 0, 0, 0, 0]])
        >>> roll_zeropad(x2, 0)
        array([[0, 1, 2, 3, 4],
               [5, 6, 7, 8, 9]])
    
        """
        a = np.asanyarray(a)
        if shift == 0: return a
        if axis is None:
            n = a.size
            reshape = True
        else:
            n = a.shape[axis]
            reshape = False
        if np.abs(shift) > n:
            res = np.zeros_like(a)
        elif shift < 0:
            shift += n
            zeros = np.zeros_like(a.take(np.arange(n-shift), axis))
            res = np.concatenate((a.take(np.arange(n-shift,n), axis), zeros), axis)
        else:
            zeros = np.zeros_like(a.take(np.arange(n-shift,n), axis))
            res = np.concatenate((zeros, a.take(np.arange(n-shift), axis)), axis)
        if reshape:
            return res.reshape(a.shape)
        else:
            return res
    

    【讨论】:

    • 谢谢,它看起来很有用。但是,我正在考虑您的建议,它似乎比贾斯汀的原始建议慢了大约两倍(根据 cProfile 的说法,随机 (1e4 x 1e4) 阵列上的 1.8 秒对 0.8 秒)。看起来串联调用导致了双倍的执行时间。
    【解决方案4】:
    import numpy as np
    
    def shift_2d_replace(data, dx, dy, constant=False):
        """
        Shifts the array in two dimensions while setting rolled values to constant
        :param data: The 2d numpy array to be shifted
        :param dx: The shift in x
        :param dy: The shift in y
        :param constant: The constant to replace rolled values with
        :return: The shifted array with "constant" where roll occurs
        """
        shifted_data = np.roll(data, dx, axis=1)
        if dx < 0:
            shifted_data[:, dx:] = constant
        elif dx > 0:
            shifted_data[:, 0:dx] = constant
    
        shifted_data = np.roll(shifted_data, dy, axis=0)
        if dy < 0:
            shifted_data[dy:, :] = constant
        elif dy > 0:
            shifted_data[0:dy, :] = constant
        return shifted_data
    

    此函数适用于二维数组,并用您选择的常数替换滚动值。

    【讨论】:

    • 看起来很有用。但是如果 dx 是正数,为什么要打电话给np.abs(dx)?那不是和dx一样吗?
    • 好点。不需要它,尤其是那里的 if 语句。我只是偏执。 :]
    【解决方案5】:

    有点晚了,但感觉像是在一行中做你想做的事的快速方法。如果包裹在一个智能函数中也许效果最好(下面的示例仅用于水平轴):

    import numpy
    
    a = numpy.arange(1,10).reshape(3,3)  # an example 2D array
    
    print a
    
    [[1 2 3]
     [4 5 6]
     [7 8 9]]
    
    shift = 1
    a = numpy.hstack((numpy.zeros((a.shape[0], shift)), a[:,:-shift]))
    
    print a
    
    [[0 1 2]
     [0 4 5]
     [0 7 8]]
    

    【讨论】:

    • 请注意:这不适用于shift = 0,因为a[:,:-shift]。如果将移位过程放在通用函数中,这可能很重要。
    【解决方案6】:

    详细说明 Hooked 的答案(因为我花了几分钟才理解它)

    下面的代码首先在上、下、左、右边缘填充一定数量的零,然后选择填充后的原始矩阵。完全没用的代码,但有助于理解np.pad

    import numpy as np
    x = np.array([[1, 2, 3],[4, 5, 6]])
    y = np.pad(x,((1,3),(2,4)), mode='constant')[1:-3,2:-4]
    
    print np.all(x==y)
    

    现在向上移动 2 并向右移动 1 个位置,应该这样做

    print np.pad(x,((0,2),(1,0)), mode='constant')[2:0,0:-1]
    

    【讨论】:

      【解决方案7】:

      您也可以使用 numpy 的 triu 和 scipy.linalg 的 circant。制作矩阵的循环版本。然后,选择从第一个对角线开始的上三角形部分(triu 中的默认选项)。行索引将对应于您想要的填充零的数量。

      如果您没有 scipy,您可以通过制作 (n-1) X (n-1) 单位矩阵并在其顶部堆叠一行 [0 0 ... 1] 来生成 nXn 循环矩阵它右侧的列 [1 0 ... 0]。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 2017-04-19
        • 1970-01-01
        • 2016-06-15
        • 2017-01-03
        • 2017-10-05
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多