如何使用numpy函数制作此列表

时间:2019-04-23 17:10:09

标签: python python-3.x performance numpy list-comprehension

我正在尝试加快对列表的理解,我尝试使用numpy.vectorize,但无法使其正常工作。可以使用该功能吗?如果可以,如何或是否有另一种方法可以使此功能更快?列表“ a”是一个numpy ndarray(在2D模式下),因此我知道要使其更快,您应该使用numpy函数而不是列表推导,但是找不到使用这些函数的方法。

[[[255,255,255] if y else [0,0,0] for y in row ] for row in a]

3 个答案:

答案 0 :(得分:2)

您可以使用np.where

In [11]: np.where(a, np.array([255, 255, 255]), 0)
Out[11]:
array([[255, 255, 255],
       [  0,   0,   0],
       [255, 255, 255]]

另一种方法是先创建一个完整的数组或零个数组,然后更新行:

In [21] res = np.zeros((3, 3))

In [22]: np.where(a == 0, res, 255)
Out[22]:
array([[255., 255., 255.],
       [  0.,   0.,   0.],
       [255., 255., 255.]])

答案 1 :(得分:1)

您的列表看起来像是这样:

In [22]: alist = [[0,1,2],[3,0,0]]                                              

理解的结果:

In [23]: [[[255,255,255] if y else [0,0,0] for y in row] for row in alist]      
Out[23]: 
[[[0, 0, 0], [255, 255, 255], [255, 255, 255]],
 [[255, 255, 255], [0, 0, 0], [0, 0, 0]]]

要在numpy中执行类似的操作,请创建一个数组(子列表的长度必须全部相同):

In [24]: arr = np.array(alist)                                                  
In [25]: arr                                                                    
Out[25]: 
array([[0, 1, 2],
       [3, 0, 0]])

制作目标数组-3d形状:

In [27]: res = np.zeros(arr.shape+(3,),int)                                     
In [28]: res                                                                    
Out[28]: 
array([[[0, 0, 0],
        [0, 0, 0],
        [0, 0, 0]],

       [[0, 0, 0],
        [0, 0, 0],
        [0, 0, 0]]])

y不为0的掩码:

In [29]: mask = arr!=0                                                          
In [30]: mask                                                                   
Out[30]: 
array([[False,  True,  True],
       [ True, False, False]])

此遮罩将选择res的3个元素:

In [31]: res[mask]                                                              
Out[31]: 
array([[0, 0, 0],
       [0, 0, 0],
       [0, 0, 0]])

我们可以使用标量来设置值:

In [32]: res[mask] = 255                                                        
In [33]: res                                                                    
Out[33]: 
array([[[  0,   0,   0],
        [255, 255, 255],
        [255, 255, 255]],

       [[255, 255, 255],
        [  0,   0,   0],
        [  0,   0,   0]]])

或数组:

In [34]: res[mask] = [255,255,255]                                              
In [35]: res                                                                    
Out[35]: 
array([[[  0,   0,   0],
        [255, 255, 255],
        [255, 255, 255]],

       [[255, 255, 255],
        [  0,   0,   0],
        [  0,   0,   0]]])

有时在设置这样的掩码数组时,我们会遇到广播错误-目标数组和源数组之间不匹配。

另一种方法是将mask视为0/1索引数组:

In [37]: x = np.array([[0,0,0],[255,255,255]])                                  
In [39]: x[mask.astype(int)]                                                    
Out[39]: 
array([[[  0,   0,   0],
        [255, 255, 255],
        [255, 255, 255]],

       [[255, 255, 255],
        [  0,   0,   0],
        [  0,   0,   0]]])

如果您从列表开始,则列表理解方法可能是最快的。尽管数组索引编制速度很快,但将列表转换为数组的开销却很大。

====

要使用np.vectorize来做到这一点,我已经习惯了signature,它比常规的vectorize慢:

In [49]: np.vectorize(lambda y: np.array([255,255,255]) if y else np.array([0,0,
    ...: 0]), signature='()->(n)')(alist)                                       
Out[49]: 
array([[[  0,   0,   0],
        [255, 255, 255],
        [255, 255, 255]],

       [[255, 255, 255],
        [  0,   0,   0],
        [  0,   0,   0]]])

答案 2 :(得分:0)

在尝试了更多的东西之后,我发现了这个oneliner,它可以完成工作,而且速度也很快。

    np.where(a != 0, 0, 255).repeat(3, 1).reshape(len(a), len(a[0]), 3)