2d numpy数组,使每个值都是以其为中心的3x3平方之和

Tra*_*ack 5 python numpy

我有一个方形的2D numpy数组A和一个B具有相同形状的零数组。

对于每一个指标(i, j)A,除第一和最后的行和列等,我想分配给B[i, j]的值np.sum(A[i - 1:i + 2, j - 1:j + 2]

例:

A =
array([[0, 0, 0, 0, 0],
       [0, 1, 0, 1, 0],
       [0, 1, 1, 0, 0],
       [0, 1, 0, 1, 0],
       [0, 0, 0, 0, 0])

B =
array([[0, 0, 0, 0, 0],
       [0, 3, 4, 2, 0],
       [0, 4, 6, 3, 0],
       [0, 3, 4, 2, 0],
       [0, 0, 0, 0, 0])
Run Code Online (Sandbox Code Playgroud)

有一种有效的方法可以做到这一点吗?还是我应该简单地使用for循环?

Mad*_*ist 6

有一种聪明的方法(请阅读“ borderline smartass”)np.lib.stride_tricks.as_stridedas_strided允许您通过在视图中添加另一个维度来在模拟窗口的缓冲区中创建视图。例如,如果您有一个像

>>> x = np.arange(10)
>>> np.lib.stride_tricks.as_strided(x, shape=(3, x.shape[0] - 2), strides=x.strides * 2)
array([[0, 1, 2, 3, 4, 5, 6, 7],
       [1, 2, 3, 4, 5, 6, 7, 8],
       [2, 3, 4, 5, 6, 7, 8, 9]])
Run Code Online (Sandbox Code Playgroud)

希望很明显,您可以求和axis=0以得到每个3号窗口的总和。没有理由不能将其扩展到两个或多个维度。我以建议解决方案的方式编写了上一个示例的形状和索引:

A = np.array([[0, 0, 0, 0, 0],
              [0, 1, 0, 1, 0],
              [0, 1, 1, 0, 0],
              [0, 1, 0, 1, 0],
              [0, 0, 0, 0, 0]])
view = np.lib.stride_tricks.as_strided(A,
    shape=(3, 3, A.shape[0] - 2, A.shape[1] - 2),
    strides=A.strides * 2
)
B[1:-1, 1:-1] = view.sum(axis=(0, 1))
Run Code Online (Sandbox Code Playgroud)

v1.7.0开始,np.sum 支持同时沿多个轴求和。对于较旧的numpy版本,只需将重复(两次)即可axis=0

填充的边缘B就留给读者做练习(因为它不是真正的问题的一部分)。

顺便说一句,如果您愿意的话,这里的解决方案是单线的。就个人而言,我认为与的任何东西as_strided已经足够难以辨认,并且不需要任何进一步的混淆。我不确定for循环是否会严重影响性能,以证明该方法是正确的。

供以后参考,这里是一个通用的窗口制作功能,可用于解决此类问题:

def window_view(a, window=3):
    """
    Create a (read-only) view into `a` that defines window dimensions.

    The first ``a.ndim`` dimensions of the returned view will be sized according to `window`.
    The remaining ``a.ndim`` dimensions will be the original dimensions of `a`, truncated by `window - 1`.

    The result can be post-precessed by reducing the leading dimensions. For example, a multi-dimensional moving average could look something like ::


         window_view(a, window).sum(axis=tuple(range(a.ndim))) / window**a.ndim

    If the window size were different for each dimension (`window` were a sequence rather than a scalar), the normalization would be ``np.prod(window)`` instead of ``window**a.ndim``.

    Parameters
    -----------
    a : array-like
        The array to window into. Due to numpy dimension constraints, can not have > 16 dims.
    window :
        Either a scalar indicating the window size for all dimensions, or a sequence of length `a.ndim` providing one size for each dimension.

    Return
    ------
    view : numpy.ndarray
         A read-only view into `a` whose leading dimensions represent the requested windows into `a`.
         ``view.ndim == 2 * a.ndim``.
    """
    a = np.array(a, copy=False, subok=True)
    window = np.array(window, copy=False, subok=False, dtype=np.int)
    if window.size == 1:
        window = np.full(a.ndim, window)
    elif window.size == a.ndim:
        window = window.ravel()
    else:
        raise ValueError('Number of window sizes must match number of array dimensions')

    shape = np.concatenate((window, a.shape))
    shape[a.ndim:] -= window - 1
    strides = a.strides * 2

    return np.lib.stride_tricks.as_strided(a, shake=shape, strides=strides)
Run Code Online (Sandbox Code Playgroud)