如何“扩展”一个numpy ndarray?

62

有没有好的方法可以“扩展”numpy的ndarray?比如我有一个像这样的ndarray:

[[1 2]
 [3 4]]

我希望每一行都包含更多的元素,通过填充零来实现:

[[1 2 0 0 0]
 [3 4 0 0 0]]

我知道有一些暴力的方法可以实现这个目标(比如构造一个更大的数组并将旧的小数组元素复制到其中),只是想知道是否有Pythonic的方式来做。尝试使用numpy.reshape,但没有成功:

import numpy as np
a = np.array([[1, 2], [3, 4]])
np.reshape(a, (2, 5))

Numpy报错: ValueError: total size of new array must be unchanged

6个回答

83
您可以使用numpy.pad,如下所示:
>>> import numpy as np
>>> a=[[1,2],[3,4]]
>>> np.pad(a, ((0,0),(0,3)), mode='constant', constant_values=0)
array([[1, 2, 0, 0, 0],
       [3, 4, 0, 0, 0]])

这里np.pad的意思是,“取数组a,在它上方添加0行,在下方添加0行,在左侧添加0列,在右侧添加3列。用由constant_values指定的constant来填充这些列。”


12
作为我的个人经历,我曾使用np.pad来动态扩展数组的大小,并注意到它的时间表现非常差。之后我改用了np.concatenate,运行时间缩短了大约10倍。 - Eliel Van Hojman

55

这里有两个索引技巧r_c_

>>> import numpy as np
>>> a = np.array([[1, 2], [3, 4]])
>>> z = np.zeros((2, 3), dtype=a.dtype)
>>> np.c_[a, z]
array([[1, 2, 0, 0, 0],
       [3, 4, 0, 0, 0]])

如果这是对性能要求很高的代码,您可能更喜欢使用等效的np.concatenate而不是索引技巧。

>>> np.concatenate((a,z), axis=1)
array([[1, 2, 0, 0, 0],
       [3, 4, 0, 0, 0]])

还有 np.resizenp.ndarray.resize,但它们由于 numpy 在内存中排列数据的方式存在一些限制(请参阅这些函数的 docstring),因此不如简单地进行连接操作。

顺便说一下,当我需要执行此操作时,我通常只使用你已经提到过的基本方法(创建一个零数组并在其中分配较小的数组),我认为这样做没有任何问题!


19

需要明确的是:没有“好”的方法来扩展NumPy数组,因为NumPy数组不可扩展。一旦数组定义了,它在内存中占用的空间(由其元素数量和每个元素的大小组成)是固定的,不能更改。唯一能做的就是创建一个新数组,并用原始数组的元素替换其中的一些元素。

为了方便使用,有很多函数可供选择(例如np.concatenate函数及其np.*stack快捷方式、np.column_stack函数、索引例程np.r_np.c_...),但它们只是方便函数。其中一些已经在C级别进行了优化(例如np.concatenate和其他一些函数),但并非所有函数都被优化过。

请注意,使用您最初建议的方法手动创建一个大型数组(可能填充为零),并自己填充初始数组也是完全可以的。这可能比更复杂的解决方案更易读。


虽然有np.ndarray.resize可以直接改变数组的大小,但它只在必要时重新分配数据空间。因此,+1。 - wim
只要数组是(i)连续的且(ii)不是对另一个数组的引用,就可以提供。而且结果总是需要三重检查,所以我想知道这是否真的值得麻烦。 - Pierre GM
对于Numpy数组的固定大小备注,建议加上+1。如果需要经常这样做,最好使用列表,最后再将其转换为数组。 - Davidmh

12

一个简单的方法:

# what you want to expand
x = np.ones((3, 3))

# expand to what shape 
target = np.zeros((6, 6))

# do expand
target[:x.shape[0], :x.shape[1]] = x

# print target
array([[ 1.,  1.,  1.,  0.,  0.,  0.],
       [ 1.,  1.,  1.,  0.,  0.,  0.],
       [ 1.,  1.,  1.,  0.,  0.,  0.],
       [ 0.,  0.,  0.,  0.,  0.,  0.],
       [ 0.,  0.,  0.,  0.,  0.,  0.],
       [ 0.,  0.,  0.,  0.,  0.,  0.]])

函数式方式:

https://dev59.com/P1sV5IYBdhLWcg3w0hpU#35751427 借鉴,稍作修改。

def pad(array, reference_shape, offsets=None):
    """
    array: Array to be padded
    reference_shape: tuple of size of narray to create
    offsets: list of offsets (number of elements must be equal to the dimension of the array)
    will throw a ValueError if offsets is too big and the reference_shape cannot handle the offsets
    """

    if not offsets:
        offsets = np.zeros(array.ndim, dtype=np.int32)

    # Create an array of zeros with the reference shape
    result = np.zeros(reference_shape, dtype=np.float32)
    # Create a list of slices from offset to offset + shape in each dimension
    insertHere = [slice(offsets[dim], offsets[dim] + array.shape[dim]) for dim in range(array.ndim)]
    # Insert the array in the result at the specified offsets
    result[insertHere] = array
    return result

9
您应该使用np.column_stackappend函数。
import numpy as np

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

p = np.column_stack( [ p , [ 0 , 0 ],[0,0] ] )

p
Out[277]: 
array([[1, 2, 0, 0],
       [3, 4, 0, 0]])

然而,似乎Append更快:

timeit np.column_stack( [ p , [ 0 , 0 ],[0,0] ] )
10000 loops, best of 3: 61.8 us per loop

timeit np.append(p, [[0,0],[0,0]],1)
10000 loops, best of 3: 48 us per loop

np.c_np.hstack的比较[附加仍然似乎是最快的]:

In [295]: z=np.zeros((2, 2), dtype=a.dtype)

In [296]: timeit np.c_[a, z]
10000 loops, best of 3: 47.2 us per loop

In [297]: timeit np.append(p, z,1)
100000 loops, best of 3: 13.1 us per loop

In [305]: timeit np.hstack((p,z))
10000 loops, best of 3: 20.8 us per loop

还有 np.concatenate [比 append 更快一些]:

In [307]: timeit np.concatenate((p, z), axis=1)
100000 loops, best of 3: 11.6 us per loop

1
奇怪。你可能会发现np.concatenate((a, z), axis=1)比append更快。 - wim
是的,这是因为在简单调用连接之前,append执行了一些额外的逻辑(实际上在这里并不需要)。 - wim

5

还有一些类似的方法,比如np.vstack、np.hstack和np.dstack。我喜欢这些方法胜过np.concatenate,因为它们明确了哪个维度被“扩展”了。

temp = np.array([[1, 2], [3, 4]])
np.hstack((temp, np.zeros((2,3))))

很容易记住,因为numpy的第一轴是垂直的,所以vstack扩展了第一轴,第二轴是水平的,所以hstack。


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