下面的函数对两个numpy数组应用numpy函数:
import numpy as np
def my_func(a: np.ndarray, b: np.ndarray) -> float:
return np.nanmin(a, axis=0) + np.nanmin(b, axis=0)
>>> my_func(np.array([1., 2., np.nan]), np.array([1., np.nan]))
2.0
然而,将相同的函数应用于np的最佳方法是什么?np的数组。不同形状的数组?
a = np.array([np.array([1., 2]), np.array([1, 2., 3, np.nan])], dtype=object) # First array shape (2,), second (3,)
b = np.array([np.array([1]), np.array([1.5, 2.5, np.nan])], dtype=object)
np。矢量化确实有效
>>> np.vectorize(my_func)(a, b)
array([2. , 2.5])
,但由矢量化文档指定:
提供矢量化函数主要是为了方便,而不是为了表演这个实现本质上是一个for循环。
有更聪明的解决方案吗?我可以用np。pad具有相同的形状,但它似乎不是最优的,因为它需要填充到内部数组的最大长度(这里a
为4,b
为3)。
我看了numba和这个堆栈交换关于性能,但我不确定这种情况下的最佳实践。
谢谢!
你的函数和数组:
In [222]: def my_func(a: np.ndarray, b: np.ndarray) -> float:
...: return np.nanmin(a, axis=0) + np.nanmin(b, axis=0)
...:
In [223]: a = np.array([np.array([1., 2]), np.array([1, 2., 3, np.nan])], dtype=object
...: ) # First array shape (2,), second (3,)
...: b = np.array([np.array([1]), np.array([1.5, 2.5, np.nan])], dtype=object)
In [224]: a
Out[224]: array([array([1., 2.]), array([ 1., 2., 3., nan])], dtype=object)
In [225]: b
Out[225]: array([array([1]), array([1.5, 2.5, nan])], dtype=object)
比较vectorize
与一个简单的列表推导式:
In [226]: np.vectorize(my_func)(a, b)
Out[226]: array([2. , 2.5])
In [227]: [my_func(i,j) for i,j in zip(a,b)]
Out[227]: [2.0, 2.5]
和它们的时间:
In [228]: timeit np.vectorize(my_func)(a, b)
157 µs ± 117 ns per loop (mean ± std. dev. of 7 runs, 10000 loops each)
In [229]: timeit [my_func(i,j) for i,j in zip(a,b)]
85.9 µs ± 148 ns per loop (mean ± std. dev. of 7 runs, 10000 loops each)
In [230]: timeit np.array([my_func(i,j) for i,j in zip(a,b)])
89.7 µs ± 1.03 µs per loop (mean ± std. dev. of 7 runs, 10000 loops each)
如果您要使用对象数组,frompyfunc
比vectorize
更快:
In [231]: np.frompyfunc(my_func,2,1)(a, b)
Out[231]: array([2.0, 2.5], dtype=object)
In [232]: timeit np.frompyfunc(my_func,2,1)(a, b)
83.2 µs ± 50.1 ns per loop (mean ± std. dev. of 7 runs, 10000 loops each)
我有点惊讶,它甚至比列表理解还好。
frompyfunc
(和vectorize
)在输入需要相互"广播"时更有用:
In [233]: np.frompyfunc(my_func,2,1)(a[:,None], b)
Out[233]:
array([[2.0, 2.5],
[2.0, 2.5]], dtype=object)
我不是numba
专家,但我怀疑它不能处理对象dtype数组,或者它不能提高速度。请记住,object dtype意味着元素是对象引用,就像在列表中一样。
通过使用otypes
并将函数创建从计时循环中取出,我获得了更好的时间:
In [235]: %%timeit f=np.vectorize(my_func, otypes=[float])
...: f(a, b)
...:
...:
95.5 µs ± 316 ns per loop (mean ± std. dev. of 7 runs, 10000 loops each)
In [236]: %%timeit f=np.frompyfunc(my_func,2,1)
...: f(a, b)
...:
...:
81.1 µs ± 103 ns per loop (mean ± std. dev. of 7 runs, 10000 loops each)
如果你不了解otypes
,说明你还没有很好地阅读np.vectorize
文档。