【问题标题】:How do I apply function to third-dimension array effectively with numpy?如何使用 numpy 有效地将函数应用于三维数组?
【发布时间】:2017-01-11 12:57:46
【问题描述】:

我想将任意函数应用于 3d-ndarray 作为元素,它使用(3 维)数组作为其参数并返回标量。因此,我们应该得到 2d-Matrix。

例如)伪代码

A = [[[1,2,3],[4,5,6]],
     [[7,8,9],[10,11,12]]]
A.apply_3d_array(sum) ## or apply_3d_array(A,sum) is Okey.
>> [[6,15],[24,33]]

我知道循环使用 ndarray.shape 函数是可能的,但是正如官方文档所说,直接索引访问效率低下。 有没有比使用循环更有效的方法?

def chromaticity(pixel):
    geo_mean = math.pow(sum(pixel),1/3)
    return map(lambda x: math.log(x/geo_mean),pixel ) 

【问题讨论】:

  • 可以分享arbitrary function的实现吗?对于明显的加速,这可能是这里的关键。
  • 感谢@Divakar 的评论。任意函数意味着所有函数都传递数组参数并返回一些值。
  • 我知道这里的任意函数是什么意思。我的意思是,如果您希望加速某些特定功能,您能否分享它的实现,因为我们可能可以使用 NumPy ufunc 来矢量化该功能所涉及的操作。
  • 哦对不起误会了。我的母语不是英语,所以我可能会使用一些奇怪的表达和误解。事实上,我对每个元素数组应用了波纹管def chromaticity(pixel): geo_mean = math.pow(sum(pixel),1/3) return map(lambda x: math.log(x/geo_mean),pixel ) 跨度>
  • 是的,就是那个!那么,您能否编辑您的问题并添加到该功能实现中?

标签: python arrays numpy multidimensional-array vectorization


【解决方案1】:

鉴于函数实现,我们可以使用 NumPy ufuncs 对其进行矢量化,这将一次性对整个输入数组 A 进行操作,从而避免使用不支持数组矢量化的 math 库函数。在这个过程中,我们还会引入非常高效的矢量化工具:NumPy broadcasting。所以,我们会有这样的实现 -

np.log(A/np.power(np.sum(A,2,keepdims=True),1/3))

样品运行和验证

没有lamdba 构造并引入NumPy 函数而不是math 库函数的函数实现看起来像这样-

def chromaticity(pixel): 
    geo_mean = np.power(np.sum(pixel),1/3) 
    return np.log(pixel/geo_mean)

使用迭代实现的示例运行 -

In [67]: chromaticity(A[0,0,:])
Out[67]: array([-0.59725316,  0.09589402,  0.50135913])

In [68]: chromaticity(A[0,1,:])
Out[68]: array([ 0.48361096,  0.70675451,  0.88907607])

In [69]: chromaticity(A[1,0,:])
Out[69]: array([ 0.88655887,  1.02009026,  1.1378733 ])

In [70]: chromaticity(A[1,1,:])
Out[70]: array([ 1.13708257,  1.23239275,  1.31940413])    

使用建议的矢量化实现运行示例 -

In [72]: np.log(A/np.power(np.sum(A,2,keepdims=True),1/3))
Out[72]: 
array([[[-0.59725316,  0.09589402,  0.50135913],
        [ 0.48361096,  0.70675451,  0.88907607]],

       [[ 0.88655887,  1.02009026,  1.1378733 ],
        [ 1.13708257,  1.23239275,  1.31940413]]])

运行时测试

In [131]: A = np.random.randint(0,255,(512,512,3)) # 512x512 colored image

In [132]: def org_app(A):
     ...:     out = np.zeros(A.shape)     
     ...:     for i in range(A.shape[0]):
     ...:         for j in range(A.shape[1]):
     ...:             out[i,j] = chromaticity(A[i,j])
     ...:     return out
     ...: 

In [133]: %timeit org_app(A)
1 loop, best of 3: 5.99 s per loop

In [134]: %timeit np.apply_along_axis(chromaticity, 2, A) #@hpaulj's soln
1 loop, best of 3: 9.68 s per loop

In [135]: %timeit np.log(A/np.power(np.sum(A,2,keepdims=True),1/3))
10 loops, best of 3: 90.8 ms per loop

这就是为什么在使用数组对事物进行矢量化并一次性处理尽可能多的元素时总是尝试推入NumPy funcs

【讨论】:

  • 太棒了!这似乎是我想要的理想解决方案。谢谢。
  • @tkowt 在里面添加了一些时序测试结果。
【解决方案2】:

apply_along_axis 旨在简化此任务:

In [683]: A=np.arange(1,13).reshape(2,2,3)
In [684]: A
Out[684]: 
array([[[ 1,  2,  3],
        [ 4,  5,  6]],

       [[ 7,  8,  9],
        [10, 11, 12]]])
In [685]: np.apply_along_axis(np.sum, 2, A)
Out[685]: 
array([[ 6, 15],
       [24, 33]])

实际上是这样

for all i,j:
    out[i,j] = func( A[i,j,:])

注意细节。它并不比自己进行迭代快,但它更容易。

另一个技巧是将输入重塑为 2d,执行更简单的 1d 迭代,然后重塑结果

 A1 = A.reshape(-1, A.shape[-1])
 for i in range(A1.shape[0]):
     out[i] = func(A1[i,:])
 out.reshape(A.shape[:2])

为了更快地做事,你需要深入挖掘函数的本质,并弄清楚如何在多个维度上使用 compile numpy 操作。在sum 的简单情况下,该功能已经可以在选定的轴上工作。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2020-12-17
    • 2019-05-06
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2018-01-24
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多