如何在numpy.append中短路展平

时间:2019-03-05 06:06:03

标签: python arrays numpy

我有一个数据结构,该数据结构继承自第三方类,该类重载__getitem__,并返回一个元组。

现在,我在其他地方有涉及将这些对象的集合附加到NumPy数组的代码:

class ThirdPartyThing:

    def __init__(self, size):
        self.size = size

    def __len__(self):
        return self.size

    def __getitem__(self, key):
        return (self, key)

    def __iter__(self):
        return zip([self] * self.size, range(self.size))

class MyThing(ThirdPartyThing):
    pass

x = numpy.array([], dtype = MyThing, ndmin = 1)
temp = [MyThing(1) for _ in range(5)]
x = numpy.append(x, temp)

执行此操作时,我期望的是一个Numpy数组,其中包含五个MyThing类型的对象,但是得到的却是这样的1-d数组:

[MyThing(), 0, MyThing(), 0, MyThing(), 0, MyThing(), 0, MyThing(), 0]

长度为10,其中每个其他元素都是整数。

根据the docs,如果未定义append但在我的情况下定义轴没有任何作用,axis尝试使数组变平。反正有什么办法避免这种陷阱?

更新 经过仔细检查,我意识到基类重载了__len__。我认为这就是导致问题的原因。

1 个答案:

答案 0 :(得分:2)

帖子的确切副本:

In [1]: class MyThing: 
   ...:     pass 
   ...:  
   ...: x = numpy.array([], dtype = MyThing, ndmin = 1) 
   ...: temp = [MyThing() for _ in range(5)] 
   ...: x = numpy.append(x, temp)                                               
In [2]: x                                                                       
Out[2]: 
array([<__main__.MyThing object at 0x7f21b45cd2e8>,
       <__main__.MyThing object at 0x7f21b45cd278>,
       <__main__.MyThing object at 0x7f21b45cd240>,
       <__main__.MyThing object at 0x7f21b45cd320>,
       <__main__.MyThing object at 0x7f21b45cd390>], dtype=object)

对于np.append,其代码为:

def append(arr, values, axis=None):
    arr = asanyarray(arr)
    if axis is None:
        if arr.ndim != 1:
            arr = arr.ravel()
        values = ravel(values)
        axis = arr.ndim-1
    return concatenate((arr, values), axis=axis)

因此,对于一个轴,它只是concatenate。没有它,请确保两个参数均为1d。

您的x为(0,)形状,您的temp为5元素列表,其asarray变为(5,)形状,结果为(5,)

In [14]: x=numpy.array([], dtype = MyThing, ndmin = 1)                          
In [15]: x.shape                                                                
Out[15]: (0,)
In [16]: np.array(temp).shape                                                   
Out[16]: (5,)
In [17]: np.concatenate((x,temp)).shape                                         
Out[17]: (5,)

我看不到问题。 np.append中的'flattening'不会影响代码。但是正如我评论的那样,我不喜欢np.append。它使太多的新用户感到困惑,因此不需要。直接使用concatenate

您还包含了ThirdPartyThing类的代码,但没有任何使用。


再次MyThing

In [21]: MyThing.__repr__= lambda self: "MYTHING" 

并定义一个不同的temp

In [28]: temp1 = np.array([(MyThing(),0) for _ in range(3)])

现在,我们看到了append奇迹的影响:

In [30]: np.append(x,temp1)                                                     
Out[30]: array([MYTHING, 0, MYTHING, 0, MYTHING, 0], dtype=object)

(3,2)temp1在与(0,)(6m,)加入之前变成x

添加axis=0无效,因为尺寸数不同。


使用已编辑的代码:

In [64]: temp = np.array([MyThing(1) for _ in range(3)])                        
In [65]: temp                                                                   
Out[65]: 
array([[[<__main__.MyThing object at 0x7f21adbc5048>, 0]],

       [[<__main__.MyThing object at 0x7f21adbc5a58>, 0]],

       [[<__main__.MyThing object at 0x7f21adbc5470>, 0]]], dtype=object)

In [66]: temp.shape                                                             
Out[66]: (3, 1, 2)

或与我的代表

In [67]: MyThing.__repr__= lambda self: "MYTHING"                               
In [68]: temp                                                                   
Out[68]: 
array([[[MYTHING, 0]],

       [[MYTHING, 0]],

       [[MYTHING, 0]]], dtype=object)

In [70]: np.append(x,temp)                                                      
Out[70]: array([MYTHING, 0, MYTHING, 0, MYTHING, 0], dtype=object)

并添加axis=0仍会提供

ValueError: all the input arrays must have same number of dimensions

无论如何构造它,尝试将一个(0,)形状数组与一个(3,1,2)形状连接起来都需要一些调整。

但是为什么要合并这两个数组呢? (0,)形状数组从哪里来?


构造列表的方式是问题的根源:

In [87]: [MyThing(1) for _ in range(3)]                                         
Out[87]: [MYTHING, MYTHING, MYTHING]
In [88]: np.array(_)                                                            
Out[88]: 
array([[[MYTHING, 0]],

       [[MYTHING, 0]],

       [[MYTHING, 0]]], dtype=object)
In [89]: [MyThing(i) for i in range(3)]      # different MyThing parameter each time                                   
Out[89]: [MYTHING, MYTHING, MYTHING]
In [90]: np.array(_)                                                            
Out[90]: array([MYTHING, MYTHING, MYTHING], dtype=object)

但是np.array([MyThing(2),MyThing(3)])会导致某种无限循环。


但是回到append的问题。通常,当迭代构建数组时,我们建议收集列表中的值(list append相当快),并在末尾进行一次数组构建(使用np.arraynp.stack和/或{{1 }}。

不建议反复进行串联。它比较慢,并且在创建有效的起始“空”数组时存在问题。您的np.concatenate看起来像一个空的启动器。 x误认为这种迭代数组构造与列表追加方法一样好。不是。这就是为什么我不喜欢np.append的部分原因。使用np.append,您至少必须直接解决数组维度上的差异。 concatenate列出一个列表,而不仅仅是两个参数。因此它可以在循环之外运行。


使用concatenateleniter(并继承ThirdPartyThing)是可迭代的。 MyThing从这些事物的列表构造数组时,也尝试对其进行迭代(与使用列表的列表相同)。

我可以制作一个空的对象数组,然后单独填充它,而不是从np.array的列表中创建数组。现在,我得到了这些对象的“干净”数组:

MyThing

甚至

In [93]: temp = np.empty(5, object)                                             
In [94]: temp                                                                   
Out[94]: array([None, None, None, None, None], dtype=object)
In [95]: for i in range(3): 
    ...:     temp[i] = MyThing(1) 
    ...:                                                                        
In [96]: temp                                                                   
Out[96]: array([MYTHING, MYTHING, MYTHING, None, None], dtype=object)

请不要将列表提供给In [100]: temp[:] = [MyThing(1) for _ in range(5)] In [101]: temp Out[101]: array([MYTHING, MYTHING, MYTHING, MYTHING, MYTHING], dtype=object)

np.array可以通过多种方式串联:

temp