如何在创建不同形状的数组的对象数组时保持numpy不被广播

时间:2017-04-02 21:06:25

标签: python python-2.7 numpy numpy-broadcasting

我尝试使用dtype=object将不同形状的数组列表存储为np.save数组(我知道我只能挑选列表,但我真的很好奇如何做这个)。 如果我这样做:

import numpy as np
np.save('test.npy', [np.zeros((2, 2)), np.zeros((3,3))])

它有效。 但是这个:

np.save('test.npy', [np.zeros((2, 2)), np.zeros((2,3))])

给我一​​个错误:

ValueError: could not broadcast input array from shape (2,2) into shape (2)

我猜np.save首先将列表转换为数组,所以我尝试了:

x=np.array([np.zeros((2, 2)), np.zeros((3,3))])
y=np.array([np.zeros((2, 2)), np.zeros((2,3))])

具有相同的效果(第一个有效,第二个无效。 结果x的行为符合预期:

>>> x.shape
(2,)
>>> x.dtype
dtype('O')
>>> x[0].shape
(2, 2)
>>> x[0].dtype
dtype('float64')

我也试图强迫对象' dtype:

np.array([np.zeros((2, 2)), np.zeros((2,3))], dtype=object)

没有成功。似乎numpy尝试将具有相同第一维的数组广播到新数组中,并且实现太晚以至于它们的形状不同。奇怪的是它似乎在某一点上起作用 - 所以我真的很好奇有什么区别,以及如何正确地做到这一点。

编辑: 我弄清楚它以前工作的情况:唯一的区别似乎是列表中的numpy数组有另一种数据类型。 它适用于dtype('<f8'),但它与dtype('float64')无关,我甚至不确定其区别。

编辑2: 我找到了一种解决我问题的非pythonic方式,我在这里添加它,也许有助于理解我想要做的事情:

array_list=np.array([np.zeros((2, 2)), np.zeros((2,3))])
save_array = np.empty((len(array_list),), dtype=object)
for idx, arr in enumerate(array_list):
    save_array[idx] = arr
np.save('test.npy', save_array)

1 个答案:

答案 0 :(得分:4)

np.save做的第一件事就是

arr = np.asanyarray(arr)

所以是的,它试图将你的列表变成一个数组。

从任意大小的数组或列表构造对象数组很棘手。 np.array(...)尝试创建尽可能高的维数组,甚至尝试在可能的情况下连接输入。最可靠的方法是做你做的 - 制作empty数组并填充它。

构建对象数组的一种稍微紧凑的方式:

In [21]: alist = [np.zeros((2, 2)), np.zeros((2,3))]
In [22]: arr = np.empty(len(alist), dtype=object)
In [23]: arr[:] = alist
In [24]: arr
Out[24]: 
array([array([[ 0.,  0.],
       [ 0.,  0.]]),
       array([[ 0.,  0.,  0.],
       [ 0.,  0.,  0.]])], dtype=object)

以下是3种情况:

形状匹配的数组,组合成一个3d数组:

In [27]: np.array([np.zeros((2, 2)), np.zeros((2,2))])
Out[27]: 
array([[[ 0.,  0.],
        [ 0.,  0.]],

       [[ 0.,  0.],
        [ 0.,  0.]]])
In [28]: _.shape
Out[28]: (2, 2, 2)

在第一个维度上不匹配的数组 - 创建对象数组

In [29]: np.array([np.zeros((2, 2)), np.zeros((3,2))])
Out[29]: 
array([array([[ 0.,  0.],
       [ 0.,  0.]]),
       array([[ 0.,  0.],
       [ 0.,  0.],
       [ 0.,  0.]])], dtype=object)
In [30]: _.shape
Out[30]: (2,)

尴尬的中间案例(甚至可能被描述为一个bug)。第一个维度匹配,但第二个维度不匹配:

In [31]: np.array([np.zeros((2, 2)), np.zeros((2,3))])
...
ValueError: could not broadcast input array from shape (2,2) into shape (2)
       [ 0.,  0.]])], dtype=object)

好像它初始化了一个(2,2,2)数组,然后发现(2,3)不适合。并且当前的逻辑不允许它像在前一个场景中那样备份和创建对象数组。

如果你想将两(2,2)个数组放在对象数组中,你必须使用创建和填充逻辑。