如何对numpy行应用泛型函数?

2024-04-25 05:35:31 发布

您现在位置:Python中文网/ 问答频道 /正文

在您将其标记为重复之前,让我向您解释一下,我阅读了this page和许多其他内容,但我仍然没有找到解决问题的方法。在

这就是我遇到的问题:给定两个二维数组,我想对这两个数组应用函数F。F取两个1D数组作为输入。在

import numpy as np
a = np.arange(15).reshape([3,5])
b = np.arange(30, step=2).reshape([3,5])

# what is the 'numpy' equivalent of the following?
np.array([np.dot(x,y) for x,y in zip(a,b)])

请注意,np.dot只是为了演示。这里真正的问题是在两组1D数组上工作的任何泛型函数F。在

  • 矢量化要么直接失败并出现错误,要么逐个元素应用函数,而不是逐个数组(或逐行)
  • np.apply_along_axis迭代地应用函数;例如,使用上面定义的变量,它执行F(a[0], b[0]),并将其与F(a[0], b[1])和{}相结合。这不是我要找的。理想情况下,我希望它停止在F(a[0], b[0])
  • 索引切片/高级切片也不能实现我想要的效果。首先,如果我做类似np.dot(a[np.arange(3)], b[np.arange(3)])的操作,就会抛出一个值错误,说形状(3,5)和(3,5)没有对齐。我不知道怎么解决这个问题。在

我试图用我能用的任何方法来解决这个问题,但是我想出的唯一可行的解决方案就是使用列表理解。但我担心使用列表理解会对性能造成损失。如果可能的话,我也希望通过numpy手术达到同样的效果。我该怎么做?在


Tags: the方法函数标记numpy列表错误np
2条回答

这种类型的问题已经被打得死去活来了,但我将尝试用您的框架来说明这些问题:

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应用于a和{}的3行。也就是说,它在2个数组上迭代,就像它们是列表一样。每次调用时,函数都会得到2个一维数组。dot可以接受其他形状,但目前我们假设它只适用于一对1d

^{pr2}$

矢量化/从PyFunc

np.vectorize迭代输入(使用广播-这很方便),并给出函数标量值。我将用frompyfunc返回一个对象数据类型数组(由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。只需要一个数组。在进行了大量的设置之后,它最终完成了以下操作(对于像a这样的2d数组):

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

对于3d和更大的,它使得在“其他”轴上的迭代更简单;对于2d,这是一种过度杀戮。无论如何,它都不能加快计算速度。它仍然是迭代。在

apply_along_axisarr和{}。它迭代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),并且与M相比,N很小,那么这个迭代并不昂贵。也就是说,在一个大任务上执行几个循环是可以的。如果简化了大阵列内存管理,它们甚至可能更快。在

最佳

理想的解决方案是使用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]]])

如果您想用NumPy快速解决问题,请远离一般函数。尽管NumPy有一些隐藏python循环的功能,但是这些循环仍然存在(在函数内部),而且这些解决方案并不是很快(至少与普通的NumPy函数相比)。在

你应该做的是:在NumPy,SciPy中找到一个函数。。。这能满足你的需要。这些函数很快,但有时需要一些搜索和/或试验,直到找到匹配项。在

例如,矢量点乘就是按元素乘法的行的和:

np.sum(a * b, axis=1)        # array([  60,  510, 1460])

np.einsum('ij,ij->i', a, b)  # array([  60,  510, 1460])

相关问题 更多 >