为什么列表理解比 numpy 的数组乘法快得多?
Why list comprehension is much faster than numpy for multiplying arrays?
最近我回答了 THIS 的问题,该问题需要 2 个列表的乘法,一些用户建议使用 numpy 的以下方法,我认为这是正确的方法:
(a.T*b).T
我还发现aray.resize()
也有类似的表现。无论如何,另一个答案建议使用列表理解的解决方案:
[[m*n for n in second] for m, second in zip(b,a)]
但在基准测试之后,我发现列表理解比 numpy 执行得更快:
from timeit import timeit
s1="""
a=[[2,3,5],[3,6,2],[1,3,2]]
b=[4,2,1]
[[m*n for n in second] for m, second in zip(b,a)]
"""
s2="""
a=np.array([[2,3,5],[3,6,2],[1,3,2]])
b=np.array([4,2,1])
(a.T*b).T
"""
print ' first: ' ,timeit(stmt=s1, number=1000000)
print 'second : ',timeit(stmt=s2, number=1000000,setup="import numpy as np")
结果:
first: 1.49778485298
second : 7.43547797203
如您所见,numpy 大约快 5 倍。但最令人惊讶的是它在不使用转置的情况下速度更快,并且对于以下代码:
a=np.array([[2,3,5],[3,6,2],[1,3,2]])
b=np.array([[4],[2],[1]])
a*b
列表推导仍然是5次faster.So除了列表推导在C中执行的这一点之外我们使用了2个嵌套循环和一个zip
函数那么可能是什么原因呢?是因为numpy中的*
操作吗?
还要注意这里的timeit
没有问题 我把import
部分放在了setup
.
我也试过用更大的阿拉斯,差异变小了但仍然没有意义:
s1="""
a=[[2,3,5],[3,6,2],[1,3,2]]*10000
b=[4,2,1]*10000
[[m*n for n in second] for m, second in zip(b,a)]
"""
s2="""
a=np.array([[2,3,5],[3,6,2],[1,3,2]]*10000)
b=np.array([4,2,1]*10000)
(a.T*b).T
"""
print ' first: ' ,timeit(stmt=s1, number=1000)
print 'second : ',timeit(stmt=s2, number=1000,setup="import numpy as np")
结果:
first: 10.7480301857
second : 13.1278889179
创建 numpy 数组比创建列表慢得多:
In [153]: %timeit a = [[2,3,5],[3,6,2],[1,3,2]]
1000000 loops, best of 3: 308 ns per loop
In [154]: %timeit a = np.array([[2,3,5],[3,6,2],[1,3,2]])
100000 loops, best of 3: 2.27 µs per loop
在肉之前,NumPy 函数调用也会产生固定成本
计算的一部分可以通过快速底层 C/Fortran 函数执行。这可以包括确保输入是 NumPy 数组,
这些 setup/fixed 成本是在使用 NumPy 之前要记住的事情
解决方案本质上比纯 Python 解决方案更快。 NumPy 大放异彩
您设置 large 数组 once 然后执行 many fast NumPy operations
在阵列上。如果数组很小,它可能无法胜过纯 Python
因为设置成本可能超过卸载计算的好处
编译 C/Fortran 函数。对于小型阵列,可能根本不够
计算使其物有所值。
如果稍微增加数组的大小,并移动数组的创建
进入设置,那么 NumPy 可以比纯 Python:
快得多
import numpy as np
from timeit import timeit
N, M = 300, 300
a = np.random.randint(100, size=(N,M))
b = np.random.randint(100, size=(N,))
a2 = a.tolist()
b2 = b.tolist()
s1="""
[[m*n for n in second] for m, second in zip(b2,a2)]
"""
s2 = """
(a.T*b).T
"""
s3 = """
a*b[:,None]
"""
assert np.allclose([[m*n for n in second] for m, second in zip(b2,a2)], (a.T*b).T)
assert np.allclose([[m*n for n in second] for m, second in zip(b2,a2)], a*b[:,None])
print 's1: {:.4f}'.format(
timeit(stmt=s1, number=10**3, setup='from __main__ import a2,b2'))
print 's2: {:.4f}'.format(
timeit(stmt=s2, number=10**3, setup='from __main__ import a,b'))
print 's3: {:.4f}'.format(
timeit(stmt=s3, number=10**3, setup='from __main__ import a,b'))
产量
s1: 4.6990
s2: 0.1224
s3: 0.1234
最近我回答了 THIS 的问题,该问题需要 2 个列表的乘法,一些用户建议使用 numpy 的以下方法,我认为这是正确的方法:
(a.T*b).T
我还发现aray.resize()
也有类似的表现。无论如何,另一个答案建议使用列表理解的解决方案:
[[m*n for n in second] for m, second in zip(b,a)]
但在基准测试之后,我发现列表理解比 numpy 执行得更快:
from timeit import timeit
s1="""
a=[[2,3,5],[3,6,2],[1,3,2]]
b=[4,2,1]
[[m*n for n in second] for m, second in zip(b,a)]
"""
s2="""
a=np.array([[2,3,5],[3,6,2],[1,3,2]])
b=np.array([4,2,1])
(a.T*b).T
"""
print ' first: ' ,timeit(stmt=s1, number=1000000)
print 'second : ',timeit(stmt=s2, number=1000000,setup="import numpy as np")
结果:
first: 1.49778485298
second : 7.43547797203
如您所见,numpy 大约快 5 倍。但最令人惊讶的是它在不使用转置的情况下速度更快,并且对于以下代码:
a=np.array([[2,3,5],[3,6,2],[1,3,2]])
b=np.array([[4],[2],[1]])
a*b
列表推导仍然是5次faster.So除了列表推导在C中执行的这一点之外我们使用了2个嵌套循环和一个zip
函数那么可能是什么原因呢?是因为numpy中的*
操作吗?
还要注意这里的timeit
没有问题 我把import
部分放在了setup
.
我也试过用更大的阿拉斯,差异变小了但仍然没有意义:
s1="""
a=[[2,3,5],[3,6,2],[1,3,2]]*10000
b=[4,2,1]*10000
[[m*n for n in second] for m, second in zip(b,a)]
"""
s2="""
a=np.array([[2,3,5],[3,6,2],[1,3,2]]*10000)
b=np.array([4,2,1]*10000)
(a.T*b).T
"""
print ' first: ' ,timeit(stmt=s1, number=1000)
print 'second : ',timeit(stmt=s2, number=1000,setup="import numpy as np")
结果:
first: 10.7480301857
second : 13.1278889179
创建 numpy 数组比创建列表慢得多:
In [153]: %timeit a = [[2,3,5],[3,6,2],[1,3,2]]
1000000 loops, best of 3: 308 ns per loop
In [154]: %timeit a = np.array([[2,3,5],[3,6,2],[1,3,2]])
100000 loops, best of 3: 2.27 µs per loop
在肉之前,NumPy 函数调用也会产生固定成本 计算的一部分可以通过快速底层 C/Fortran 函数执行。这可以包括确保输入是 NumPy 数组,
这些 setup/fixed 成本是在使用 NumPy 之前要记住的事情 解决方案本质上比纯 Python 解决方案更快。 NumPy 大放异彩 您设置 large 数组 once 然后执行 many fast NumPy operations 在阵列上。如果数组很小,它可能无法胜过纯 Python 因为设置成本可能超过卸载计算的好处 编译 C/Fortran 函数。对于小型阵列,可能根本不够 计算使其物有所值。
如果稍微增加数组的大小,并移动数组的创建 进入设置,那么 NumPy 可以比纯 Python:
快得多import numpy as np
from timeit import timeit
N, M = 300, 300
a = np.random.randint(100, size=(N,M))
b = np.random.randint(100, size=(N,))
a2 = a.tolist()
b2 = b.tolist()
s1="""
[[m*n for n in second] for m, second in zip(b2,a2)]
"""
s2 = """
(a.T*b).T
"""
s3 = """
a*b[:,None]
"""
assert np.allclose([[m*n for n in second] for m, second in zip(b2,a2)], (a.T*b).T)
assert np.allclose([[m*n for n in second] for m, second in zip(b2,a2)], a*b[:,None])
print 's1: {:.4f}'.format(
timeit(stmt=s1, number=10**3, setup='from __main__ import a2,b2'))
print 's2: {:.4f}'.format(
timeit(stmt=s2, number=10**3, setup='from __main__ import a,b'))
print 's3: {:.4f}'.format(
timeit(stmt=s3, number=10**3, setup='from __main__ import a,b'))
产量
s1: 4.6990
s2: 0.1224
s3: 0.1234