Y, X = np.mgrid[-3:-3:10j, -3:3:10j]
我注意到在上面的网格网格上应用某些操作时会出现错误,因为操作可能与numpy不兼容。有时可能有一个numpy函数替代sin,cos但不适用于所有函数,如scipy.integrate中的quad函数。
如何解决这个问题?我需要在整个meshgrids上应用操作。
答案 0 :(得分:5)
您的问题(以及后续评论)至少可以采取两种不同的方式:
你有一个多个参数的函数,并且你希望能够以与numpy原生支持的广播调用语法相似的方式调用该函数。性能不是问题,只是函数的调用语法。
你有一个多个参数的函数,要在一系列numpy数组上进行求值,但是这个函数的实现方式并不能充分利用numpy数组的连续内存布局。表现是问题;你很乐意遍历numpy数组,并以一种无聊的,简单的旧for循环风格调用你的函数,除非这样做太慢了。
对于第1项,有一个名为vectorize
的numpy提供的便利功能,它采用常规可调用并返回一个可调用的调用,可以使用numpy数组作为参数调用,并遵守numpy的广播规则。
考虑这个人为的例子:
def my_func(x, y):
return x + 2*y
现在假设我需要在二维网格中到处评估此功能。这是一种普通的无聊方式:
Y, X = np.mgrid[0:10:1, 0:10:1]
Z = np.zeros_like(Y)
for i in range(Y.shape[0]):
for j in range(Y.shape[1]):
Z[i,j] = my_func(X[i,j], Y[i,j])
如果我们有一些不同的函数,比如my_func
,那么将这个过程概括为一个在二维数组上“映射”给定函数的函数可能会很好。
import itertools
def array_map(some_func, *arg_arrays):
output = np.zeros_like(arg_arrays[0])
coordinates = itertools.imap(range, output.shape)
for coord in itertools.product(coordinates):
args = [arg_array[coord] for arg_array in arg_arrays]
output[coord] = some_func(*args)
return output
现在我们可以看到array_map(my_func, X, Y)
就像嵌套的for循环一样:
In [451]: array_map(my_func, X, Y)
Out[451]:
array([[ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9],
[ 2, 3, 4, 5, 6, 7, 8, 9, 10, 11],
[ 4, 5, 6, 7, 8, 9, 10, 11, 12, 13],
[ 6, 7, 8, 9, 10, 11, 12, 13, 14, 15],
[ 8, 9, 10, 11, 12, 13, 14, 15, 16, 17],
[10, 11, 12, 13, 14, 15, 16, 17, 18, 19],
[12, 13, 14, 15, 16, 17, 18, 19, 20, 21],
[14, 15, 16, 17, 18, 19, 20, 21, 22, 23],
[16, 17, 18, 19, 20, 21, 22, 23, 24, 25],
[18, 19, 20, 21, 22, 23, 24, 25, 26, 27]])
现在,如果我们可以调用array_map(my_func)
并省去额外的数组参数,那不是很好吗?而只是回到一个新的功能,只是等待执行所需的for循环。
我们可以使用functools.partial
执行此操作 - 因此我们可以编写一个方便的小矢量图,如下所示:
import functools
def vectorizer(regular_function):
awesome_function = functools.partial(array_map, regular_function)
return awesome_function
并测试出来:
In [453]: my_awesome_func = vectorizer(my_func)
In [454]: my_awesome_func(X, Y)
Out[454]:
array([[ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9],
[ 2, 3, 4, 5, 6, 7, 8, 9, 10, 11],
[ 4, 5, 6, 7, 8, 9, 10, 11, 12, 13],
[ 6, 7, 8, 9, 10, 11, 12, 13, 14, 15],
[ 8, 9, 10, 11, 12, 13, 14, 15, 16, 17],
[10, 11, 12, 13, 14, 15, 16, 17, 18, 19],
[12, 13, 14, 15, 16, 17, 18, 19, 20, 21],
[14, 15, 16, 17, 18, 19, 20, 21, 22, 23],
[16, 17, 18, 19, 20, 21, 22, 23, 24, 25],
[18, 19, 20, 21, 22, 23, 24, 25, 26, 27]])
现在my_awesome_func
的行为就像你可以直接在ndarrays上调用它一样!
我已经忽略了许多额外的小的性能细节,边界检查等,同时制作了这个名为vectorizer
的玩具版本...但幸运的是在numpy中有vectorize
已经做到了这一点!
In [455]: my_vectorize_func = np.vectorize(my_func)
In [456]: my_vectorize_func(X, Y)
Out[456]:
array([[ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9],
[ 2, 3, 4, 5, 6, 7, 8, 9, 10, 11],
[ 4, 5, 6, 7, 8, 9, 10, 11, 12, 13],
[ 6, 7, 8, 9, 10, 11, 12, 13, 14, 15],
[ 8, 9, 10, 11, 12, 13, 14, 15, 16, 17],
[10, 11, 12, 13, 14, 15, 16, 17, 18, 19],
[12, 13, 14, 15, 16, 17, 18, 19, 20, 21],
[14, 15, 16, 17, 18, 19, 20, 21, 22, 23],
[16, 17, 18, 19, 20, 21, 22, 23, 24, 25],
[18, 19, 20, 21, 22, 23, 24, 25, 26, 27]])
正如我之前对OP的评论以及vectorize
的文档中所强调的那样 - 不是速度优化。实际上,在某些情况下,额外的函数调用开销会比直接编写for循环慢。但是,对于速度不是问题的情况,此方法允许您使自定义函数遵循与numpy相同的调用约定 - 这可以提高库的接口的一致性并使代码更一致和更易读。
关于第2项已经写了很多其他的东西。如果你的问题是你需要优化你的函数来利用连续的内存块并绕过重复的动态类型检查(numpy数组添加的主要功能)到Python列表)然后这里有一些你可能会发现有用的链接: