numpy,在列表优化上应用函数

numpy , applying function over list optimization

我有这两个代码,它们做同样的事情,但数据结构不同

res = np.array([np.array([2.0, 4.0, 6.0]), np.array([8.0, 10.0, 12.0])], dtype=np.int)
%timeit np.sum(res, axis=1)
4.08 µs ± 728 ns per loop (mean ± std. dev. of 7 runs, 100000 loops each)
list_obj_array = np.ndarray((2,), dtype=np.object)
list_obj_array[0] = [2.0, 4.0, 6.0]
list_obj_array[1] = [8.0, 10.0, 12.0]

v_func = np.vectorize(np.sum, otypes=[np.int])
%timeit v_func(list_obj_array)
20.6 µs ± 486 ns per loop (mean ± std. dev. of 7 runs, 100000 loops each)

第二个慢了5倍,有没有更好的优化方法?

@nb.jit()
def nb_np_sum(arry_list):
   return [np.sum(row) for row in arry_list]

%timeit nb_np_sum(list_obj_array)
30.8 µs ± 5.88 µs per loop (mean ± std. dev. of 7 runs, 10000 loops each)
@nb.jit()
def nb_sum(arry_list):
    return [sum(row) for row in arry_list]

%timeit nb_sum(list_obj_array)
13.6 µs ± 669 ns per loop (mean ± std. dev. of 7 runs, 100000 loops each)

迄今为止最好的(感谢@hpaulj)

%timeit [sum(l) for l in list_obj_array]
850 ns ± 115 ns per loop (mean ± std. dev. of 7 runs, 1000000 loops each)
@nb.njit()
def nb_sum(arry_list):
    return [sum(row) for row in arry_list]


TypingError: Failed in nopython mode pipeline (step: nopython frontend)
Untyped global name 'sum': cannot determine Numba type of <class 'builtin_function_or_method'>

File "<ipython-input-54-3bb48c5273bb>", line 3:
def nb_sum(arry_list):
        return [sum(row) for row in arry_list]

对于更长的数组

list_obj_array = np.ndarray((n,), dtype=np.object)
for i in range(n):
    list_obj_array[i] = list(range(7))

vectorized版本更接近最佳选择(列表理解)

%timeit [sum(l) for l in list_obj_array]
23.4 µs ± 4.19 µs per loop (mean ± std. dev. of 7 runs, 10000 loops each)
 %timeit v_func(list_obj_array)
29.6 µs ± 4.91 µs per loop (mean ± std. dev. of 7 runs, 10000 loops each)

numba 还是比较慢

%timeit nb_sum(list_obj_array)
74.4 µs ± 6.11 µs per loop (mean ± std. dev. of 7 runs, 10000 loops each)

自从您使用 otypes 后,您阅读了足够多的 vectorize 文档,知道它不是性能工具。

In [430]: timeit v_func(list_obj_array)
38.3 µs ± 894 ns per loop (mean ± std. dev. of 7 runs, 10000 loops each)

列表理解速度更快:

In [431]: timeit [sum(l) for l in list_obj_array]
2.08 µs ± 62.2 ns per loop (mean ± std. dev. of 7 runs, 100000 loops each)

如果您从列表列表而不是对象 dtype 数组开始,那就更好了:

In [432]: alist = list_obj_array.tolist()
In [433]: timeit [sum(l) for l in alist]
542 ns ± 11.1 ns per loop (mean ± std. dev. of 7 runs, 1000000 loops each)

编辑

np.frompyfuncnp.vectorize 快,尤其是在处理对象 dtype 数组时:

In [459]: np.frompyfunc(sum,1,1)(list_obj_array)
Out[459]: array([12.0, 30.0], dtype=object)
In [460]: timeit np.frompyfunc(sum,1,1)(list_obj_array)
2.22 µs ± 16.7 ns per loop (mean ± std. dev. of 7 runs, 100000 loops each)

正如我在其他地方看到的那样 frompyfunc 与列表理解具有竞争力。

有趣的是,使用 np.sum 而不是 sum 会减慢速度。我认为这是因为应用于列表的 np.sum 具有将列表转换为数组的开销。 sum 应用于数字列表非常好,使用 python 自己的编译代码。

In [461]: timeit np.frompyfunc(np.sum,1,1)(list_obj_array)
30.3 µs ± 165 ns per loop (mean ± std. dev. of 7 runs, 10000 loops each)

所以让我们在矢量化中尝试 sum

In [462]: v_func = np.vectorize(sum, otypes=[int])
In [463]: timeit v_func(list_obj_array)
8.7 µs ± 331 ns per loop (mean ± std. dev. of 7 runs, 100000 loops each)

好多了。