有比np更好的方法吗?矢量化使用函数对不同形状的阵列的阵列?



下面的函数对两个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)

如果您要使用对象数组,frompyfuncvectorize更快:

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文档。