Python numpy性能-超大阵列上的选择
我正在尝试优化一些代码,其中一个耗时的操作如下:Python numpy性能-超大阵列上的选择,python,arrays,performance,numpy,indexing,Python,Arrays,Performance,Numpy,Indexing,我正在尝试优化一些代码,其中一个耗时的操作如下: import numpy as np survivors = np.where(a > 0)[0] pos = len(survivors) a[:pos] = a[survivors] b[:pos] = b[survivors] c[:pos] = c[survivors] 在我的代码中,是一个非常大(超过100000个)的浮点数组。他们中的许多人将是0 有什么方法可以加快速度吗?在我看来,没有任何东西可以用纯NumPy来加快速度。但
import numpy as np
survivors = np.where(a > 0)[0]
pos = len(survivors)
a[:pos] = a[survivors]
b[:pos] = b[survivors]
c[:pos] = c[survivors]
在我的代码中,是一个非常大(超过100000个)的浮点数组。他们中的许多人将是0
有什么方法可以加快速度吗?在我看来,没有任何东西可以用纯NumPy来加快速度。但是,如果您有numba,您可以使用jitted函数编写此“选择”的自己版本:
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可能是一个严重的依赖项
例子:
时间:
很难准确地计算时间,因为所有方法都在适当的位置工作,所以我实际上使用number=1
来测量计时(这避免了由于解决方案的适当性而导致计时中断)我使用了所产生的计时列表的min
,因为这在文档中被宣传为最有用的定量度量:
注
从结果向量中计算平均值和标准偏差并报告它们是很有诱惑力的。然而,这不是很有用。在典型情况下,最低值给出了机器运行给定代码段的速度下限;结果向量中较高的值通常不是由Python速度的变化引起的,而是由干扰计时精度的其他进程引起的。因此,结果的min()可能是您应该感兴趣的唯一数字。之后,您应该查看整个向量并应用常识而不是统计数据
努巴溶液
0.007700118746939211
原液
0.028622144571883723
Alexander McFarlane的解决方案(现已删除)
0.058305527038669425
因此,Numba解决方案实际上可以将速度提高3-4倍,而Alexander McFarlane的解决方案实际上比原始方法慢(2倍)。然而,少量的
repeat
s可能会在一定程度上影响计时。什么是b
和c
您使用的是a=0是否为它们编制索引?是。因此,a、b和c代表同一对象的不同特征。然后我想选择a>0的对象,并得到一个新的a、b和c,其中只有这个对象a[:pos]
意味着a[pos:
之后的所有内容都是未使用的,同样的b
和c
这是真的吗?这是一种遗传算法吗?你想把幸存者留到下一代吗?没错。从哪里开始,我只关心a[:pos]
,而不是整个a。顺便问一下,我如何在注释中生成内联代码?因此a
必须保持其原始形状?@AlexanderMcFarlane我不太确定您的方法是否正确。我根本无法解释矢量化numpy运算的1000倍加速。我想如果避免使用临时数组或使用更高效的操作,您可以期望的速度限制是2-5倍。我现在要测试它,但是如果“a”、“b”和“c”是其他数组上的视图,您的解决方案是否有效?它们会根据我的需要进行修改吗?顺便说一句,我一直在测试代码的其他瓶颈,特别是numpy数组的操作(相同的形状、求和、除法、获取数组和),发现它比原始numpy代码慢。。。有什么一般建议吗?是的,它将在视图上工作(您可以通过运行选择(a[0:2],b,c)
而不是选择(a,b,c)来验证这一点)
在我的示例中。@MSeifert感谢您研究我的方法!在您的帮助下,我了解了一点%%timeit
magic命令的弱点-我将删除我的解决方案,因为它将彻底迷惑正在查看它的人-请在您的答案中加入它,作为解决问题的方法avoid@DiogoSantos很难给他基因ral关于numba性能的指南。重击的一个规则是:自己编写所有循环,并尽量避免在numba函数中调用“复杂”的numpy函数(例如,高级索引或创建临时数组的操作)。然后还有不同的方法来迭代numpy数组,有时对数组中的元素使用,或对范围内的idx(len(array))使用,
甚至对np.nditer(array)中的元素使用会更快
。要获得最快的numba函数,必须进行一些试验,有时最快的方法取决于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.]))
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))
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))
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))