您好, 欢迎来到 !    登录 | 注册 | | 设为首页 | 收藏本站

如何将通用函数应用于numpy行?

如何将通用函数应用于numpy行?

此类问题已被SO淘汰,但我将尝试说明您的框架存在的问题:

In [1]: a = np.arange(15).reshape([3,5])
   ...: b = np.arange(30, step=2).reshape([3,5])
   ...: 
In [2]: def f(x,y):
   ...:     return np.dot(x,y)

列表解析方法适用f于3行ab。也就是说,像遍历列表一样在两个数组上进行迭代。每次调用时,您的函数将获得2个1d数组。dot可以接受其他形状,但目前我们假装它仅适用于一对1ds

In [3]: np.array([f(x,y) for x,y in zip(a,b)])
Out[3]: array([  60,  510, 1460])
In [4]: np.dot(a[0],b[0])
Out[4]: 60

np.vectorize迭代输入(使用广播- 可能很方便),并给出函数标量值。我将用frompyfuncreturn来说明一个对象dtype数组(并由所使用vectorize):

In [5]: vf = np.frompyfunc(f, 2,1)
In [6]: vf(a,b)
Out[6]: 
array([[0, 2, 8, 18, 32],
       [50, 72, 98, 128, 162],
       [200, 242, 288, 338, 392]], dtype=object)

因此结果是(3,5)数组;偶然地跨列求和得到期望的结果

In [9]: vf(a,b).sum(axis=1)
Out[9]: array([60, 510, 1460], dtype=object)

np.vectorize 没有做出任何速度承诺。

我不知道你怎么用apply_along_axis。它只需要一个数组。经过大量设置后,它最终完成了(对于像这样的2d数组a):

for i in range(3):
    idx = (i, slice(None))
    outarr[idx] = asanyarray(func1d(arr[idx], *args, **kwargs))

对于3d和更大的尺寸,它使在“其他”轴上的迭代更加简单;对于2d来说,这是过分的杀伤力。无论如何,它不会加快计算速度。它仍然是迭代。

apply_along_axis采用arr*args。迭代arr,但使用*args整体。)。

np.dot(a[np.arange(3)], b[np.arange(3)])

是相同的

np.dot(a, b)

dot是矩阵乘积,(3,5)与(5,3)一起产生(3,3)。它将1d作为特殊情况处理(请参阅文档),(3)与(3,)产生(3,)。

对于真正的泛型来说f(x,y),压缩列表理解的唯一替代方法是如下所示的索引循环:

In [18]: c = np.zeros((a.shape[0]))
In [19]: for i in range(a.shape[0]):
    ...:    c[i] = f(a[i,:], b[i,:])
In [20]: c
Out[20]: array([   60.,   510.,  1460.])

速度会差不多。(可以通过将操作移至已编译的代码cython,但我认为您不准备深入了解该代码。)

评论中所述,如果数组为(N,M),并且N与相比较小M,则此迭代的成本并不高。也就是说,完成一个大任务的几个循环是可以的。如果它们简化了大型阵列内存管理,它们甚至可能更快。

理想的解决方案是使用numpy编译函数重写通用函数,使其可用于2d数组。

在矩阵乘法的情况下,einsum已在编译代码中实现了“乘积和”的广义形式:

In [22]: np.einsum('ij,ij->i',a,b)
Out[22]: array([  60,  510, 1460])

matmul 也可以推广该产品,但最适合3D阵列:

In [25]: a[:,None,:]@b[:,:,None]    # needs reshape
Out[25]: 
array([[[  60]],

       [[ 510]],

       [[1460]]])
其他 2022/1/1 18:47:50 有467人围观

撰写回答


你尚未登录,登录后可以

和开发者交流问题的细节

关注并接收问题和回答的更新提醒

参与内容的编辑和改进,让解决方法与时俱进

请先登录

推荐问题


联系我
置顶