几个维度的Numpy卷

Jib*_*iby 9 python arrays numpy

我需要通过位移的3D矢量来移动3D阵列用于算法.截至目前我正在使用这个(非常难看)方法:

shiftedArray = np.roll(np.roll(np.roll(arrayToShift, shift[0], axis=0)
                                     , shift[1], axis=1),
                             shift[2], axis=2)  
Run Code Online (Sandbox Code Playgroud)

哪个有效,但意味着我打3个卷!(根据我的分析,我的算法时间的58%用于这些)

来自Numpy.roll的文档:

参数:
shift:int

axis:int,可选

在参数中没有提到类似数组......所以我不能进行多维滚动?

我以为我可以调用这种功能(听起来像Numpy的事情):

np.roll(arrayToShift,3DshiftVector,axis=(0,1,2))
Run Code Online (Sandbox Code Playgroud)

也许我的阵列的扁平版本重塑了?但那我该如何计算换档向量?这种转变真的一样吗?

我很惊讶地发现,这个不容易解决,因为我认为这将是一个很常见的事(好吧,不常见的,但是......)

那么我们如何 - 相对地 - 通过N维向量有效地移动ndarray?


注意:这个问题在2015年被问到,当numpy的roll方法不支持此功能时.

War*_*ser 5

从理论上讲,使用scipy.ndimage.interpolation.shift@Ed Smith所描述的应该可行,但由于一个开放的bug(https://github.com/scipy/scipy/issues/1323),它不会给出相当于多个调用的结果的np.roll.


更新:numpy.roll在numpy版本1.12.0中添加了"多卷"功能.这是一个二维示例,其中第一个轴滚动一个位置,第二个轴滚动三个位置:

In [7]: x = np.arange(20).reshape(4,5)

In [8]: x
Out[8]: 
array([[ 0,  1,  2,  3,  4],
       [ 5,  6,  7,  8,  9],
       [10, 11, 12, 13, 14],
       [15, 16, 17, 18, 19]])

In [9]: numpy.roll(x, [1, 3], axis=(0, 1))
Out[9]: 
array([[17, 18, 19, 15, 16],
       [ 2,  3,  4,  0,  1],
       [ 7,  8,  9,  5,  6],
       [12, 13, 14, 10, 11]])
Run Code Online (Sandbox Code Playgroud)

这使得下面的代码已经过时了.我会把它留给后人.


下面的代码定义了我调用的函数multiroll,它可以满足您的需求.这是一个将其应用于具有形状(500,500,500)的数组的示例:

In [64]: x = np.random.randn(500, 500, 500)

In [65]: shift = [10, 15, 20]
Run Code Online (Sandbox Code Playgroud)

使用多个调用来np.roll生成预期结果:

In [66]: yroll3 = np.roll(np.roll(np.roll(x, shift[0], axis=0), shift[1], axis=1), shift[2], axis=2)
Run Code Online (Sandbox Code Playgroud)

使用multiroll以下方法生成移位的数组:

In [67]: ymulti = multiroll(x, shift)
Run Code Online (Sandbox Code Playgroud)

验证我们是否获得了预期结果:

In [68]: np.all(yroll3 == ymulti)
Out[68]: True
Run Code Online (Sandbox Code Playgroud)

对于这个大小的数组,进行三次调用np.roll几乎比调用的三倍慢multiroll:

In [69]: %timeit yroll3 = np.roll(np.roll(np.roll(x, shift[0], axis=0), shift[1], axis=1), shift[2], axis=2)
1 loops, best of 3: 1.34 s per loop

In [70]: %timeit ymulti = multiroll(x, shift)
1 loops, best of 3: 474 ms per loop
Run Code Online (Sandbox Code Playgroud)

这是以下定义multiroll:

from itertools import product
import numpy as np


def multiroll(x, shift, axis=None):
    """Roll an array along each axis.

    Parameters
    ----------
    x : array_like
        Array to be rolled.
    shift : sequence of int
        Number of indices by which to shift each axis.
    axis : sequence of int, optional
        The axes to be rolled.  If not given, all axes is assumed, and
        len(shift) must equal the number of dimensions of x.

    Returns
    -------
    y : numpy array, with the same type and size as x
        The rolled array.

    Notes
    -----
    The length of x along each axis must be positive.  The function
    does not handle arrays that have axes with length 0.

    See Also
    --------
    numpy.roll

    Example
    -------
    Here's a two-dimensional array:

    >>> x = np.arange(20).reshape(4,5)
    >>> x 
    array([[ 0,  1,  2,  3,  4],
           [ 5,  6,  7,  8,  9],
           [10, 11, 12, 13, 14],
           [15, 16, 17, 18, 19]])

    Roll the first axis one step and the second axis three steps:

    >>> multiroll(x, [1, 3])
    array([[17, 18, 19, 15, 16],
           [ 2,  3,  4,  0,  1],
           [ 7,  8,  9,  5,  6],
           [12, 13, 14, 10, 11]])

    That's equivalent to:

    >>> np.roll(np.roll(x, 1, axis=0), 3, axis=1)
    array([[17, 18, 19, 15, 16],
           [ 2,  3,  4,  0,  1],
           [ 7,  8,  9,  5,  6],
           [12, 13, 14, 10, 11]])

    Not all the axes must be rolled.  The following uses
    the `axis` argument to roll just the second axis:

    >>> multiroll(x, [2], axis=[1])
    array([[ 3,  4,  0,  1,  2],
           [ 8,  9,  5,  6,  7],
           [13, 14, 10, 11, 12],
           [18, 19, 15, 16, 17]])

    which is equivalent to:

    >>> np.roll(x, 2, axis=1)
    array([[ 3,  4,  0,  1,  2],
           [ 8,  9,  5,  6,  7],
           [13, 14, 10, 11, 12],
           [18, 19, 15, 16, 17]])

    """
    x = np.asarray(x)
    if axis is None:
        if len(shift) != x.ndim:
            raise ValueError("The array has %d axes, but len(shift) is only "
                             "%d. When 'axis' is not given, a shift must be "
                             "provided for all axes." % (x.ndim, len(shift)))
        axis = range(x.ndim)
    else:
        # axis does not have to contain all the axes.  Here we append the
        # missing axes to axis, and for each missing axis, append 0 to shift.
        missing_axes = set(range(x.ndim)) - set(axis)
        num_missing = len(missing_axes)
        axis = tuple(axis) + tuple(missing_axes)
        shift = tuple(shift) + (0,)*num_missing

    # Use mod to convert all shifts to be values between 0 and the length
    # of the corresponding axis.
    shift = [s % x.shape[ax] for s, ax in zip(shift, axis)]

    # Reorder the values in shift to correspond to axes 0, 1, ..., x.ndim-1.
    shift = np.take(shift, np.argsort(axis))

    # Create the output array, and copy the shifted blocks from x to y.
    y = np.empty_like(x)
    src_slices = [(slice(n-shft, n), slice(0, n-shft))
                  for shft, n in zip(shift, x.shape)]
    dst_slices = [(slice(0, shft), slice(shft, n))
                  for shft, n in zip(shift, x.shape)]
    src_blks = product(*src_slices)
    dst_blks = product(*dst_slices)
    for src_blk, dst_blk in zip(src_blks, dst_blks):
        y[dst_blk] = x[src_blk]

    return y
Run Code Online (Sandbox Code Playgroud)