如何通过对n个元素求和来减少numpy数组的维数?

4

我有以下数组(数字仅作为示例):

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

                [5,  5,  5,    6,  6,  6,    7,  7,  7,    8,  8,  8 ],
                [5,  5,  5,    6,  6,  6,    7,  7,  7,    8,  8,  8 ],
                [5,  5,  5,    6,  6,  6,    7,  7,  7,    8,  8,  8 ],

                [9,  9,  9,    10, 10, 10,   11, 11, 11,   12, 12, 12],
                [9,  9,  9,    10, 10, 10,   11, 11, 11,   12, 12, 12],
                [9,  9,  9,    10, 10, 10,   11, 11, 11,   12, 12, 12],

                [13, 13, 13,   14, 14, 14,   15, 15, 15,   16, 16, 16],
                [13, 13, 13,   14, 14, 14,   15, 15, 15,   16, 16, 16],
                [13, 13, 13,   14, 14, 14,   15, 15, 15,   16, 16, 16]])

我希望通过一种方式减少维度,使得每9个元素(3x3区域)的相同数字被加总。因此12*12的数组应该变成4x4的数组。

我在这里寻找其他答案,并找到了一个适用于1D数组的解决方案,我进行了调整。然而,它并没有按照预期工作:

result = np.sum(arr.reshape(-1,3), axis=1)
result = np.sum(result .reshape(3,-1), axis=0)

如何正确地实现所需结果?


每个“方格”都有唯一的值,就像示例中一样吗? - yatu
1
@MykolaZotko提供的链接包含了部分答案,但对于这个情况,我认为我们需要使用步长,就像我在下面的回答中所包含的那样。 - Felix
这个问题问的是另外一件事情。从解决问题的角度来看,它们是相关的,但是它们询问的是不同的事情@MykolaZotko - yatu
4个回答

3

我建议按照Nils的答案进行操作,因为对于这种情况,它更简单和高效,如果您想要除了求和之外的其他结果,则下面提到的内容更为通用。


您正在寻找卷积。一个小内核在数组上运行,执行逐元素相乘并将结果求和,为新数组的每个步骤生成值。在这种情况下,我们希望得到一个简单的总和,因此我们将使用一个大小合适的内核(3x3)来进行计算。由于我们不希望有重叠,所以在两个方向上我们的步长(stride)也是3。

NumPy中没有可用的2D卷积函数,因此我们需要从SciPy导入。但是该函数没有跳过(skipping)功能,因此我们将手动实现自己的跳过功能。

from scipy.signal import convolve2d

kernel = np.ones((3, 3))
convolved = convolve2d(arr, kernel, mode='valid')
strided = convolved[::3, ::3]

strided 在这里包含了结果,我们可以通过将其除以九来检查最终结果,以获取每个单元格的原始值。

>>> strided / 9
array([[ 1.,  2.,  3.,  4.],
       [ 5.,  6.,  7.,  8.],
       [ 9., 10., 11., 12.],
       [13., 14., 15., 16.]])

2

如果我们查看扁平化的数组

arr.ravel()
# array([ 1,  1,  1,  2,  2,  2,  3,  3,  3,  4,  4,  4,  1,  1,  1,  2,  2,
#         2,  3,  3,  3,  4,  4,  4,  1,  1,  1,  2,  2,  2,  3,  3,  3,  4,
#         4,  4,  5,  5,  5,  6,  6,  6,  7,  7,  7,  8,  8,  8,  5,  5,  5,
#         6,  6,  6,  7,  7,  7,  8,  8,  8,  5,  5,  5,  6,  6,  6,  7,  7,
#         7,  8,  8,  8,  9,  9,  9, 10, 10, 10, 11, 11, 11, 12, 12, 12,  9,
#         9,  9, 10, 10, 10, 11, 11, 11, 12, 12, 12,  9,  9,  9, 10, 10, 10,
#        11, 11, 11, 12, 12, 12, 13, 13, 13, 14, 14, 14, 15, 15, 15, 16, 16,
#        16, 13, 13, 13, 14, 14, 14, 15, 15, 15, 16, 16, 16, 13, 13, 13, 14,
#        14, 14, 15, 15, 15, 16, 16, 16])

我们可以看到一个模式:
  1. 每组包含3个数字
  2. 每组又分成4个数字
  3. 每个超级组包含3个小组
使用这种方式来重新构造你的数组(从后往前),并求和。
arr.reshape(-1, 3, 4, 3).sum((-1, -3))
# array([[  9,  18,  27,  36],
#        [ 45,  54,  63,  72],
#        [ 81,  90,  99, 108],
#        [117, 126, 135, 144]])

也许可以通过知道块的大小来获得4吗?arr.shape[0]//3 - yatu

1
在使用reshape进行一些调整后,我想出了这个。

arr = np.array([[ 1,  1,  1,  2,  2,  2,  3,  3,  3,  4,  4,  4],
        [ 1,  1,  1,  2,  2,  2,  3,  3,  3,  4,  4,  4],
        [ 1,  1,  1,  2,  2,  2,  3,  3,  3,  4,  4,  4],
        [ 5,  5,  5,  6,  6,  6,  7,  7,  7,  8,  8,  8],
        [ 5,  5,  5,  6,  6,  6,  7,  7,  7,  8,  8,  8],
        [ 5,  5,  5,  6,  6,  6,  7,  7,  7,  8,  8,  8],
        [ 9,  9,  9, 10, 10, 10, 11, 11, 11, 12, 12, 12],
        [ 9,  9,  9, 10, 10, 10, 11, 11, 11, 12, 12, 12],
        [ 9,  9,  9, 10, 10, 10, 11, 11, 11, 12, 12, 12]])

a = np.size(arr,0)//3
b = np.size(arr,1)//3

np.sum(arr.reshape(a, 3, b, 3), axis=(1,3))

# result

array([[  9,  18,  27,  36],
       [ 45,  54,  63,  72],
       [ 81,  90,  99, 108]])


0
我们可以使用skimageview_as_blocks来对数组进行分块处理,然后对每个块求和:
from skimage.util.shape import view_as_blocks
n = 3
view_as_blocks(arr, (n,n)).sum((-1,2))
array([[  9,  18,  27,  36],
       [ 45,  54,  63,  72],
       [ 81,  90,  99, 108],
       [117, 126, 135, 144]])

为什么要除以 n ** 2?如果你想要平均值,只需使用 .mean()。但是 OP 想要求和,所以不需要归一化。此外,您可以使用 .sum(-1, -2) - Nils Werner
是的,我以为是“平均值”,不好意思,已经更新了。谢谢@nils。 - yatu
我认为您需要一个元组来连接要减少的轴 @nils - yatu

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接