为什么numpy ndarray比简单循环的列表慢得多?

时间:2019-01-15 15:51:39

标签: python numpy

我是Python的新手,所以我决定解决一些常见的挑战,以提高我的语言知识。我了解了numpy及其有效的ndarrays,因此尝试了以下实验:

考虑2和问题(例如here),让我们以幼稚的方式解决它(此问题的目的无关紧要)。 这是使用python列表的解决方案:

from  itertools import combinations

def twosum1(n_lst):
    pairs=list(combinations(n_lst,2))
    solutions=[]
    for pair in pairs:
        if sum(pair)==7: solutions.append(pair)
    return(solutions)

然后我使用np.arrays创建了一个版本,希望它可以大大加快计算速度:

from  itertools import combinations
import numpy as np

def twosum2(n_lst):
    pairs=np.array(list(combinations(n_lst,2)),dtype=int)
    return pairs[pairs[:,1]+pairs[:,0]==7]

但是,在对两个函数进行计时之后,twosum2的速度比twosum1慢2倍。所以我认为问题可能出在元素的动态选择上,所以我用ndarrays替换了列表,从而编写了twosum1的精确副本...

def twosum3(n_lst):
    pairs=np.array(list(combinations(n_lst,2)))
    solutions=np.empty((0,2))
    for pair in pairs:
        if np.sum(pair)==7: 
            solutions=np.append(solutions,[pair],axis=0)
    return(solutions)

...并且生成的功能比原始功能慢10倍!

这怎么可能?我在这里做错了什么?显然,删除循环并使用ndarrays替换列表不足以提高速度(与我从this中学到的相反)。

编辑:

  • 我在jupyter中使用%timeit来计时功能。
  • 我对我选择的所有功能采用相同的基准。
  • 我在3个函数中以相同的方式计算组合的事实告诉我,速度下降是由于numpy引起的,但看不到如何实现。

1 个答案:

答案 0 :(得分:3)

代价昂贵的操作是np.array(list(combinations(n_lst,2)),dtype=int),因为python必须扫描列表中的每个成员,检查成员是否“兼容”,将其转换为整数并将其存储在数组中。

要达到numpy性能,必须在numpy中构思所有算法。例如:

In [63]: n_lst=list(range(100))

In [64]: %timeit twosum1(n_lst)
11.2 ms ± 1.64 ms per loop (mean ± std. dev. of 7 runs, 100 loops each)


In [65]: np.vstack(np.where(np.add.outer(n_lst,n_lst)==7)).T
Out[65]: 
array([[0, 7],
       [1, 6],
       [2, 5],
       [3, 4],
       [4, 3],
       [5, 2],
       [6, 1],
       [7, 0]], dtype=int64)

In [66]: %timeit np.vstack(np.where(np.add.outer(n_lst,n_lst)==7)).T
306 µs ± 19 µs per loop (mean ± std. dev. of 7 runs, 1000 loops each)

这样,根据问题,您将赢得30到100的系数。