据我所知,没有什么可以用纯 NumPy 加速它。但是,如果您有 numba,您可以使用 jited 函数编写您自己的“选择”版本:
import numba as nb
@nb.njit
def selection(a, b, c):
insert_idx = 0
for idx, item in enumerate(a):
if item > 0:
a[insert_idx] = a[idx]
b[insert_idx] = b[idx]
c[insert_idx] = c[idx]
insert_idx += 1
在我的测试运行中,这大约比您的 NumPy 代码快 2 倍。但是,如果您不使用 conda,numba 可能会严重依赖。
示例:
>>> import numpy as np
>>> a = np.array([0., 1., 2., 0.])
>>> b = np.array([1., 2., 3., 4.])
>>> c = np.array([1., 2., 3., 4.])
>>> selection(a, b, c)
>>> a, b, c
(array([ 1., 2., 2., 0.]),
array([ 2., 3., 3., 4.]),
array([ 2., 3., 3., 4.]))
时间:
很难准确计时,因为所有方法都在原地工作,所以我实际上使用timeit.repeat 来测量时间和number=1(这避免了由于解决方案的就地性而导致的时间中断)我使用了时间结果列表中的min,因为这在文档中被宣传为最有用的量化指标:
注意
从结果向量计算平均值和标准差并报告这些是很诱人的。但是,这不是很有用。在典型情况下,最小值给出了机器运行给定代码 sn-p 的速度的下限;结果向量中的较高值通常不是由 Python 速度的变化引起的,而是由其他进程干扰您的计时精度引起的。所以结果的 min() 可能是您应该感兴趣的唯一数字。之后,您应该查看整个向量并应用常识而不是统计数据。
Numba 解决方案
import timeit
min(timeit.repeat("""selection(a, b, c)""",
"""import numpy as np
from __main__ import selection
a = np.arange(1000000) % 3
b = a.copy()
c = a.copy()
""", repeat=100, number=1))
0.007700118746939211
原方案
import timeit
min(timeit.repeat("""survivors = np.where(a > 0)[0]
pos = len(survivors)
a[:pos] = a[survivors]
b[:pos] = b[survivors]
c[:pos] = c[survivors]""",
"""import numpy as np
a = np.arange(1000000) % 3
b = a.copy()
c = a.copy()
""", repeat=100, number=1))
0.028622144571883723
Alexander McFarlane 的解决方案(现已删除)
import timeit
min(timeit.repeat("""survivors = comb_array[:, 0].nonzero()[0]
comb_array[:len(survivors)] = comb_array[survivors]""",
"""import numpy as np
a = np.arange(1000000) % 3
b = a.copy()
c = a.copy()
comb_array = np.vstack([a,b,c]).T""", repeat=100, number=1))
0.058305527038669425
因此,Numba 解决方案实际上可以将速度提高 3-4 倍,而 Alexander McFarlane 的解决方案实际上比原始方法慢(2 倍)。但是,repeats 的少数可能会在一定程度上影响时间。