Numpy:重新排列矩阵以使每行与其左/右上下文堆叠的最有效方法是什么?

时间:2012-08-23 19:04:08

标签: arrays numpy reshape

让我用一个小例子解释一下:

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

我想要一个具有

形式的新数组
array([[0, 0, 1, 2, 3, 4],
       [1, 2, 3, 4, 5, 6],
       [3, 4, 5, 6, 7, 8],
       [5, 6, 7, 8, 0, 0]])

这里,上下文的大小为+/- 1,但我想保持变量。

到目前为止,我正在做的是将零添加到原始数组中:

>>> y = np.concatenate((np.zeros((1, 2)), x, np.zeros((1, 2))), axis=0)
>>> y
array([[ 0.,  0.],
       [ 1.,  2.],
       [ 3.,  4.],
       [ 5.,  6.],
       [ 7.,  8.],
       [ 0.,  0.]])

通过读取新大小的行将值放入新数组中:

>>> z = np.empty((x.shape[0], x.shape[1]*3))
>>> for i in range(x.shape[0]): z[i] = y[i:i+3].flatten()

这种作品,但我发现它缓慢,丑陋和unpythonic。 你能想到一个更好的方法来进行这种重新排列吗?对于现场解决方案的额外赞成:)

2 个答案:

答案 0 :(得分:2)

可以选择使用stride_tricks,但我不会说这是最好的答案,因为虽然它是“最有效的方式”,但在考虑可读性时这种方式并不总是最好的它正在玩火。

# We make it flat (and copy if necessary) to be on the safe side, and because
# it is more obvious this way with stride tricks (or my function below):
y = y.ravel()

# the new shape is (y.shape[0]//2-2, 6). When looking at the raveled y, the first
# dimension takes steps of 2 elements (so y.strides[0]*2) and the second is
# just the old one:
z = np.lib.stride_tricks.as_strided(y, shape=(y.shape[0]//2-2, 6),
                                       strides=(y.strides[0]*2, y.strides[0]))

请注意z这里只是一个视图,因此在编辑之前使用z.copy()可以避免任何意外的事情,否则在您的示例中,如果您编辑其中一个,则所有1都会更改。从好的方面来说,如果你的意思是“就地”,你现在可以更改y中的元素,而z也会改变。

如果你想要做更多这样的魔术,可以从https://gist.github.com/3430219查看我的rolling_window函数,该函数将最后一行替换为:

# 6 values long window, but only every 2nd step on the original array:
z = rolling_window(y, 6, asteps=2)

重要提示: np.lib.stride_tricks.as_strided本身通常安全,必须小心使用,因为它可能会造成细分错误。

答案 1 :(得分:1)

索引应该有效:

y = np.concatenate(([0, 0], x.flat, [0, 0]))  # or use np.pad with NumPy 1.7
i = np.tile(np.arange(6), (4, 1)) + np.arange(4)[:, None] * 2
z = y[i]

显然,如果你愿意,这就到位了!

要了解其工作原理,请查看i索引数组:

array([[ 0,  1,  2,  3,  4,  5],
       [ 2,  3,  4,  5,  6,  7],
       [ 4,  5,  6,  7,  8,  9],
       [ 6,  7,  8,  9, 10, 11]])

灵活变通:

context = 1
h, w = x.shape
zeros = np.zeros((context, w), dtype=x.dtype)
y = np.concatenate((zeros, x, zeros), axis=0).flat
i = np.tile(np.arange(w + 2 * context * w), (h, 1)) + np.arange(h)[:, None] * w
z = y[i]