【问题标题】:How to keep numpy from broadcasting when creating an object array of different shaped arrays创建不同形状数组的对象数组时如何防止numpy广播
【发布时间】:2017-08-27 15:57:42
【问题描述】:

我尝试使用np.save 将不同形状的数组列表存储为dtype=object 数组(我知道我可以只腌制列表,但我真的很好奇如何做到这一点)。 如果我这样做:

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')

我也尝试强制使用 'object' 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)

【问题讨论】:

    标签: python python-2.7 numpy array-broadcasting


    【解决方案1】:

    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,)
    

    还有尴尬的中间情况(甚至可以被描述为一个错误)。第一个维度匹配,但第二个不匹配):

    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) 数组放入对象数组中,则必须使用创建和填充逻辑。

    【讨论】:

    • 谢谢,是的,我认为python中的整个“不要测试和失败”策略似乎这是预期的行为。我没有意识到我可以在不破坏对象数组的情况下执行 arr[:] = alist,从而使该解决方案更容易接受 :)
    猜你喜欢
    • 2019-04-22
    • 1970-01-01
    • 2019-12-03
    • 2020-02-11
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2016-12-11
    • 1970-01-01
    相关资源
    最近更新 更多