python 如何不循环地移动numpy数组的块?

cx6n0qe3  于 2023-03-16  发布在  Python
关注(0)|答案(6)|浏览(185)

我有一个numpy数组,它是这样组织的,其中2行组成一个块,所以总共有4个块:

array([[0., 0., 0., 0.],
[0., 0., 0., 0.],
[1., 1., 1., 1.],
[1., 1., 1., 1.],
[2., 2., 2., 2.],
[2., 2., 2., 2.],
[3., 3., 3., 3.],
[3., 3., 3., 3.]])

我想转换数组,这样每隔一个块就追加到第一个块的列中:

array([[0., 0., 0., 0., 1., 1., 1., 1.],
[0., 0., 0., 0., 1., 1., 1., 1.],
[2., 2., 2., 2., 3., 3., 3., 3.],
[2., 2., 2., 2., 3., 3., 3., 3.]])

尝试了块上的for循环的蛮力方法。这工作,但寻找一个更快的方法。任何见解赞赏!

p4rjhz4m

p4rjhz4m1#

用些变形和换位的魔法怎么样?

import numpy as np

arr = np.array([
    [0., 0., 0., 0.],
    [0., 0., 0., 0.],
    [1., 1., 1., 1.],
    [1., 1., 1., 1.],
    [2., 2., 2., 2.],
    [2., 2., 2., 2.],
    [3., 3., 3., 3.],
    [3., 3., 3., 3.]
])

X, Y, H, W = 2, 2, 2, 4

out = arr.reshape(X, Y, H, W).swapaxes(1, 2).reshape(X * H, Y * W)

输出:

array([[0., 0., 0., 0., 1., 1., 1., 1.],
       [0., 0., 0., 0., 1., 1., 1., 1.],
       [2., 2., 2., 2., 3., 3., 3., 3.],
       [2., 2., 2., 2., 3., 3., 3., 3.]])

说明:
在逻辑上有4个维度在起作用:

  • 每个块是W宽。
  • 每个块为H高。
  • 你想把这些块放到一个网格中,这个网格的宽度是Y,在这个例子中是Y = 2;
  • 这个网格的高度是未指定的,但是由所选的Y明确地确定。在这种情况下,对于N = 4总块,我们可以说网格的高度为X = N / Y = 2

当前,存储器顺序是(X Y H) W,这意味着预想的“轴”X, Y, H在原始数组中的轴0上被分组在一起,因此长度为X * Y * H = 8
您所需的输出数组的排序为(X H) (Y W)。因此:

out = (
    arr                     # (X Y H) W
    .reshape(X, Y, H, W)    #  X Y H W
    .swapaxes(1, 2)         #  X H Y W     
    .reshape(X * H, Y * W)  # (X H) (Y W)
)

# Note that reshaping can only ever "regroup" axes,
# and that transposing can only ever reorder "groups" of axes.
u0sqgete

u0sqgete2#

另一种可能的解决方案:

chunks = arr.reshape(4, 2, 4)
np.hstack([np.vstack(chunks[::2, :, :]), np.vstack(chunks[1::2, :, :])])

输出:

array([[0., 0., 0., 0., 1., 1., 1., 1.],
       [0., 0., 0., 0., 1., 1., 1., 1.],
       [2., 2., 2., 2., 3., 3., 3., 3.],
       [2., 2., 2., 2., 3., 3., 3., 3.]])
ukdjmx9f

ukdjmx9f3#

您也可以通过切片来实现:

x = np.array([[0., 0., 0., 0.],
[0., 0., 0., 0.],
[1., 1., 1., 1.],
[1., 1., 1., 1.],
[2., 2., 2., 2.],
[2., 2., 2., 2.],
[3., 3., 3., 3.],
[3., 3., 3., 3.]])

np.concatenate((x[::2], x[1::2]), axis=1)
bbmckpt7

bbmckpt74#

你可以使用NumPy的数组操作函数以更有效的方式实现这一点。一种方法是使用np.concatenate()函数水平连接数组,然后重新调整连接数组的形状,将每两行组成一个块。下面是你的操作方法:

import numpy as np

# Create the input array
arr = np.array([[0., 0., 0., 0.],
                [0., 0., 0., 0.],
                [1., 1., 1., 1.],
                [1., 1., 1., 1.],
                [2., 2., 2., 2.],
                [2., 2., 2., 2.],
                [3., 3., 3., 3.],
                [3., 3., 3., 3.]])

# Reshape the array into chunks of two rows
chunks = arr.reshape(-1, 2, arr.shape[1])

# Concatenate every other chunk horizontally
result = np.concatenate([chunks[0::2], chunks[1::2]], axis=2)

# Reshape the concatenated array to group every two rows as a chunk
result = result.reshape(-1, arr.shape[1]*2)

print(result)

输出:

array([[0., 0., 0., 0., 1., 1., 1., 1.],
       [0., 0., 0., 0., 1., 1., 1., 1.],
       [2., 2., 2., 2., 3., 3., 3., 3.],
       [2., 2., 2., 2., 3., 3., 3., 3.]])

在这段代码中,首先使用arr.reshape(-1, 2, arr.shape[1])将输入数组arr重新整形为两行的块。然后,使用切片语法chunks[0::2]chunks[1::2]水平连接每隔一个块以选择备用块,然后使用np.concatenate()沿着列轴(axis=2)连接它们。最后,我们使用result.reshape(-1, arr.shape[1]*2)将串接数组重新整形为两行的块。

mzsu5hc0

mzsu5hc05#

晚安@dmn,我得到了你的代码,并把它放到机器上运行。如果一个问题没有循环处理,根据你的例子,如果通过追加第一列,最快的方法将是concatenate
细胞:

import numpy as np
group = np.array([[0., 0., 0., 0.],
                  [0., 0., 0., 0.],
                  [1., 1., 1., 1.],
                  [1., 1., 1., 1.],
                  [2., 2., 2., 2.],
                  [2., 2., 2., 2.],
                  [3., 3., 3., 3.],
                  [3., 3., 3., 3.]])

group

输出:

array([[0., 0., 0., 0.],
       [0., 0., 0., 0.],
       [1., 1., 1., 1.],
       [1., 1., 1., 1.],
       [2., 2., 2., 2.],
       [2., 2., 2., 2.],
       [3., 3., 3., 3.],
       [3., 3., 3., 3.]])

细胞:

len(group)

输出:

8

细胞:

np.concatenate((group))

输出:

array([0., 0., 0., 0., 0., 0., 0., 0., 1., 1., 1., 1., 1., 1., 1., 1., 2.,
       2., 2., 2., 2., 2., 2., 2., 3., 3., 3., 3., 3., 3., 3., 3.])

细胞:

len(np.concatenate((group)))

输出:

32
i5desfxk

i5desfxk6#

您可以通过在为数组指定最终形状之前交换每组四行的第二行和第三行来“就地”执行此操作:

import numpy as np
group = np.array([[0., 0., 0., 0.],
                  [0., 0., 0., 0.],
                  [1., 1., 1., 1.],
                  [1., 1., 1., 1.],
                  [2., 2., 2., 2.],
                  [2., 2., 2., 2.],
                  [3., 3., 3., 3.],
                  [3., 3., 3., 3.]])

group[1::4],group[2::4] = group[2::4],group[1::4].copy()
group.shape = (group.shape[0]//2,-1)

print(group)    
[[0. 0. 0. 0. 1. 1. 1. 1.]
 [0. 0. 0. 0. 1. 1. 1. 1.]
 [2. 2. 2. 2. 3. 3. 3. 3.]
 [2. 2. 2. 2. 3. 3. 3. 3.]]

为了证明这会组合适当的行,如果您从以下内容开始:

[[ 1.  2.  3.  4.]
 [11. 12. 13. 14.]
 [ 5.  6.  7.  8.]
 [15. 16. 17. 18.]
 [21. 22. 23. 24.]
 [31. 32. 33. 34.]
 [25. 26. 27. 28.]
 [35. 36. 37. 38.]]

您将获得:

[[ 1.  2.  3.  4.  5.  6.  7.  8.]
 [11. 12. 13. 14. 15. 16. 17. 18.]
 [21. 22. 23. 24. 25. 26. 27. 28.]
 [31. 32. 33. 34. 35. 36. 37. 38.]]

相关问题