在numpy中设置数组的滑动值windows

setting the values of sliding windows of an array in numpy

假设我有一个形状为 (3, 3) 的二维数组,称之为 a,以及一个形状为 (7, 7, 5, 5) 的零数组,称之为 b。我想按以下方式修改 b

for p in range(5):
    for q in range(5):
        b[p:p + 3, q:q + 3, p, q] = a

鉴于:

a = np.array([[4, 2, 2],
              [9, 0, 5],
              [9, 9, 4]])
b = np.zeros((7, 7, 5, 5), dtype=int)

b 会变成这样:

>>> b[:, :, 0, 0]
array([[4, 2, 2, 0, 0, 0, 0],
       [9, 0, 5, 0, 0, 0, 0],
       [9, 9, 4, 0, 0, 0, 0],
       [0, 0, 0, 0, 0, 0, 0],
       [0, 0, 0, 0, 0, 0, 0],
       [0, 0, 0, 0, 0, 0, 0],
       [0, 0, 0, 0, 0, 0, 0]])
>>> b[:, :, 0, 1]
array([[0, 4, 2, 2, 0, 0, 0],
       [0, 9, 0, 5, 0, 0, 0],
       [0, 9, 9, 4, 0, 0, 0],
       [0, 0, 0, 0, 0, 0, 0],
       [0, 0, 0, 0, 0, 0, 0],
       [0, 0, 0, 0, 0, 0, 0],
       [0, 0, 0, 0, 0, 0, 0]])

考虑此问题的一种方法是制作 b (6D) 的滑动 window 视图,切出您想要的部分(3D 或 4D),然后分配 a给他们。

但是,有一种更简单的方法可以完全做到这一点。滑动 window 视图的工作方式是创建一个小于您正在查看的维度的完整尺寸的维度。例如:

>>> x = np.array([1, 2, 3, 4])
array([1, 2, 3, 4])
>>> window = np.lib.stride_tricks.as_strided(
                 x, shape=(x.shape[0] - 2, 3),
                    strides=x.strides * 2)
[[1 2 3]
 [2 3 4]]

我在这里特意使用 np.lib.stride_tricks.as_strided rather than np.lib.stride_tricks.sliding_window_view,因为它具有您需要的一定灵活性。

只要小心,您的步幅可以比您正在查看的轴。在这种情况下,连续数组更为宽容,但绝不是必需的。 np.diag 就是一个例子。你可以像这样实现它:

>>> x = np.arange(12).reshape(3, 4)
array([[ 0,  1,  2,  3],
       [ 4,  5,  6,  7],
       [ 8,  9, 10, 11]])
>>> diag = np.lib.stride_tricks.as_strided(
               x, shape=(min(x.shape),),
                  strides=(sum(x.strides),))
array([ 0,  5, 10])

诀窍是只查看 b 中您关心的部分,从而使分配变得容易。由于广播规则,您将希望视图的最后两个维度为 a.shape,步幅为 b.strides[:2],因为这是您要放置 a.[=33 的位置=]

视图的前两个维度将负责制作 a 的副本。您想要 25 个副本,因此形状将为 (5, 5)。步幅是比较棘手的部分。我们来看一个二维的案例,因为这样更容易形象化,然后尝试概括一下:

>>> a0 = np.array([1, 2])
>>> b0 = np.zeros((4, 3), dtype=int)
>>> b0[0:2, 0] = b0[1:3, 1] = b0[2:4, 2] = a0

目标是制作一个视图,该视图在第一个轴中沿着 b0 的对角线跨步。所以:

>>> np.lib.stride_tricks.as_strided(
        b0, shape=(b0.shape[0] - a0.shape[0] + 1, a0.shape[0]),
            strides=(sum(b0.strides), b0.strides[0]))[:] = a0
>>> b0
array([[1, 0, 0],
       [2, 1, 0],
       [0, 2, 1],
       [0, 0, 2]])

这就是您为 b 所做的,但是将每个第二个维度相加:

a = np.array([[4, 2, 2],
              [9, 0, 5],
              [9, 9, 4]])
b = np.zeros((7, 7, 5, 5), dtype=int)
vshape = (*np.subtract(b.shape[:a.ndim], a.shape) + 1,
          *a.shape)
vstrides = (*np.add(b.strides[:a.ndim], b.strides[a.ndim:]),
            *b.strides[:a.ndim])
np.lib.stride_tricks.as_strided(b, shape=vshape, strides=vstrides)[:] = a

TL;DR

def emplace_window(a, b):
    vshape = (*np.subtract(b.shape[:a.ndim], a.shape) + 1, *a.shape)
    vstrides = (*np.add(b.strides[:a.ndim], b.strides[a.ndim:]), *b.strides[:a.ndim])
    np.lib.stride_tricks.as_strided(b, shape=vshape, strides=vstrides)[:] = a

我是这样表述的,因为现在您可以将它应用于任意数量的维度。唯一的期望是 2 * a.ndim == b.ndimb.shape[a.ndim:] == b.shape[:a.ndim] - a.shape + 1.