【问题标题】:How to efficiently resize a numpy array to a given shape, padding with zeros if necessary?如何有效地将 numpy 数组的大小调整为给定的形状,必要时用零填充?
【发布时间】:2020-05-22 18:28:26
【问题描述】:

我想基于另一个 numpy 数组创建一个给定形状的数组。尺寸的数量将匹配,但尺寸会因轴而异。如果原始尺寸太小,我想用零填充它以满足要求。需要澄清的预期行为示例:

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

resize_with_outer_zeros(embedding, (4, 3)) = np.array([
    [1, 2, 3],
    [5, 6, 7],
    [0, 0, 0],
    [0, 0, 0]
])

我想我通过下面的函数实现了预期的行为。

def resize_with_outer_zeros(embedding: np.ndarray, target_shape: Tuple[int, ...]) -> np.ndarray:
    padding = tuple((0, max(0, target_size - size)) for target_size, size in zip(target_shape, embedding.shape))
    target_slice = tuple(slice(0, target_size) for target_size in target_shape)
    return np.pad(embedding, padding)[target_slice]

但是,我对它的效率和优雅有强烈的怀疑,因为它涉及到很多纯 python 元组操作。有没有更好更简洁的方法呢?

【问题讨论】:

    标签: python numpy numpy-ndarray


    【解决方案1】:

    如果您知道您的数组不会大于某个大小 (r, c),为什么不直接:

    def pad_with_zeros(A, r, c):
       out = np.zeros((r, c))
       r_, c_ = np.shape(A)
       out[0:r_, 0:c_] = A
       return out
    

    如果你想支持任意维度(张量),它会变得有点难看,但原理是一样的:

    def pad(A, shape):
       out = np.zeros(shape)
       out[tuple(slice(0, d) for d in np.shape(A))] = A
       return out
    

    并支持更大的数组(比您要填充的更大):

    def pad(A, shape):
        shape = np.max([np.shape(A), shape], axis=0)
        out = np.zeros(shape)
        out[tuple(slice(0, d) for d in np.shape(A))] = A
        return out
    

    【讨论】:

    • 发布的速度比想象的要快,请查看编辑。
    【解决方案2】:

    我不认为你可以做得更好,但不要使用 pad 然后切片,只需以正确的大小执行 zeros 然后分配 - 这会将其削减为一个列表理解而不是两个。

    embedding = np.array([
        [1, 2, 3, 4],
        [5, 6, 7, 8]
    ])
    
    z = np.zeros((4,3))
    s = tuple([slice(None, min(za,ea)) for za,ea in zip(z.shape, embedding.shape)])
    
    z[s] = embedding[s]
    z
    # array([[1., 2., 3.],
    #        [5., 6., 7.],
    #        [0., 0., 0.],
    #        [0., 0., 0.]])
    

    【讨论】:

    • 是的,这个恕我直言看起来更好一些。我会比较两种变体的速度,稍后再报告结果。
    【解决方案3】:

    我只需使用零矩阵并运行嵌套 for 循环来设置旧数组中的值 - 其余位置将自动填充零。

    
    import numpy as np
    
    
    def resize_array(array, new_size):
        Z = np.zeros(new_size)
        for i in range(len(Z)):
            for j in range(len(Z[i])):
                try:
                    Z[i][j] = array[i][j]
                except IndexError:       # just in case array[i][j] doesn't exist in the new size and should be truncated
                    pass
        return Z
    
    
    embedding = np.array([[1, 2, 3, 4], [5, 6, 7, 8]])
    print(resize_array(embedding, (4, 3)))
    
    

    【讨论】:

    • 用纯 python 编写的循环似乎比使用 np.pad 和切片(带有一些初步的形状计算)更糟糕的解决方案?
    猜你喜欢
    • 2019-07-04
    • 2012-03-04
    • 2020-06-02
    • 2016-09-29
    • 1970-01-01
    • 2023-03-16
    • 2021-06-24
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多