Kas*_*mvd 7 python performance numpy list-comprehension matrix-multiplication
最近我回答了这个想要两个列表相乘的问题,一些用户提出了以下使用numpy的方法,我认为这是正确的方法:
(a.T*b).T
Run Code Online (Sandbox Code Playgroud)
我也发现它aray.resize()具有相同的性能.任何方式另一个答案建议使用列表理解的解决方案:
[[m*n for n in second] for m, second in zip(b,a)]
Run Code Online (Sandbox Code Playgroud)
但在基准测试之后我发现列表理解比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")
Run Code Online (Sandbox Code Playgroud)
结果:
first: 1.49778485298
second : 7.43547797203
Run Code Online (Sandbox Code Playgroud)
正如你所看到的,numpy大约快5倍.但最令人惊讶的是,它在不使用转置的情况下更快,并且代码如下:
a=np.array([[2,3,5],[3,6,2],[1,3,2]])
b=np.array([[4],[2],[1]])
a*b
Run Code Online (Sandbox Code Playgroud)
列表理解仍然快5倍.除此之外,列表推导在C中执行,这里我们使用了2个嵌套循环和zip函数那么原因是什么呢?是因为*在numpy 中操作?
另外请注意,有没有问题,timeit在这里我的推杆的import部分setup.
我也尝试过更大的arras,差异越小但仍然没有意义:
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")
Run Code Online (Sandbox Code Playgroud)
结果:
first: 10.7480301857
second : 13.1278889179
Run Code Online (Sandbox Code Playgroud)
unu*_*tbu 13
创建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
Run Code Online (Sandbox Code Playgroud)
在通过快速的基础C/Fortran函数执行计算之前,还可以通过NumPy函数调用产生固定成本.这可以包括确保输入是NumPy数组,
在假设NumPy解决方案本质上比纯Python解决方案更快之前,需要牢记这些设置/固定成本.当您设置一次大型数组然后在阵列上执行许多快速NumPy操作时,NumPy会发光.如果数组很小,它可能无法胜过纯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'))
Run Code Online (Sandbox Code Playgroud)
产量
s1: 4.6990
s2: 0.1224
s3: 0.1234
Run Code Online (Sandbox Code Playgroud)