函数和测试数组:
In [22]: def getSum(n):
...: n=n**2
...: sum = 0
...: while (n != 0):
...:
...: sum = sum + int(n % 10)
...: n = int(n/10)
...: if sum <20:
...: return True
...: return False
...:
In [23]: mylist=np.array([120,3,10,33,5,54,2,23,599,801])
您的filter 解决方案:
In [51]: list(filter(getSum, mylist))
Out[51]: [120, 3, 10, 33, 5, 54, 2, 23, 801]
还有一个采样时间:
In [52]: timeit list(filter(getSum, mylist))
32.8 µs ± 185 ns per loop (mean ± std. dev. of 7 runs, 10000 loops each)
由于这会返回一个列表并进行迭代,如果mylist 是一个列表而不是一个数组,它应该会更快:
In [53]: %%timeit alist=mylist.tolist()
...: list(filter(getSum, alist))
18.4 µs ± 378 ns per loop (mean ± std. dev. of 7 runs, 10000 loops each)
替代方案
您提议使用 np.vectorize:
In [56]: f = np.vectorize(getSum); mylist[f(mylist)]
Out[56]: array([120, 3, 10, 33, 5, 54, 2, 23, 801])
In [57]: timeit f = np.vectorize(getSum); mylist[f(mylist)]
63.4 µs ± 151 ns per loop (mean ± std. dev. of 7 runs, 10000 loops each)
In [58]: timeit mylist[f(mylist)]
57.6 µs ± 920 ns per loop (mean ± std. dev. of 7 runs, 10000 loops each)
哎呀!即使我们从计时循环中删除 f 创建,这也会慢一些。 vectorize 很漂亮,但不保证速度。
我发现 frompyfunc 比 np.vectorize 快(尽管它们是相关的):
In [59]: g = np.frompyfunc(getSum, 1,1)
In [60]: g(mylist)
Out[60]:
array([True, True, True, True, True, True, True, True, False, True],
dtype=object)
结果是object dtype,在这种情况下必须转换为bool:
In [63]: timeit mylist[g(mylist).astype(bool)]
25.5 µs ± 233 ns per loop (mean ± std. dev. of 7 runs, 10000 loops each)
这比您的 filter 更好 - 但仅适用于数组,而不是列表。
@Saandeep 提出了一个列表理解:
In [65]: timeit mylist[[getSum(i) for i in mylist]]
40.7 µs ± 1.21 µs per loop (mean ± std. dev. of 7 runs, 10000 loops each)
这比你的 filter 慢一点。
使用列表推导的更快方法是:
[i for i in mylist if getSum(i)]
这与您的 filter 相同 - 对于数组和列表版本(我丢失了我正在计时的会话)。
纯numpy
>
@lante 提出了一个纯粹的numpy 解决方案,聪明但有点晦涩难懂。我还没有弄清楚逻辑:
def lante(mylist):
max_digits = np.ceil(np.max(np.log10(mylist))) # max number of digits in mylist
digits = mylist//(10**np.arange(max_digits)[:, None])%10 # matrix of digits
digitsum = np.sum(digits, axis=0) # array of sums
mask = digitsum > 20
return mask
不幸的是不是速度恶魔:
In [69]: timeit mylist[~lante(mylist)]
58.9 µs ± 757 ns per loop (mean ± std. dev. of 7 runs, 10000 loops each)
我没有安装numba,所以无法为@jezrael's 解决方案计时。
所以你原来的filter 是一个很好的解决方案,特别是如果你从一个列表而不是一个数组开始。尤其是在考虑转换时间时,一个好的 Python 列表解决方案通常比 numpy 更好。
对于一个大的例子来说,时间可能会有所不同,但我预计不会有任何不安。