假设我按如下方式初始化矩阵:
import scipy
m = scipy.zeros((10, 10))
现在我做一些计算,我想将结果分配到m
。在作业中,m
的大小不会发生变化,因此我认为如果分配完成后会更快。
m = scipy.array([[i * j for j in range(10)] for i in range(10)])
我担心在上面的代码中,会创建一个包含结果的临时矩阵,然后将m
分配给此值。这是低效的,因为它涉及分配新矩阵。更有效的解决方案是将值直接存储在m
中,可以表示如下:
for i in range(10):
for j in range(10):
m[i,j] = i * j
但是假设生成器表达式对我来说更方便,就像我安排我的代码一样。
我想知道的是:在上面的生成器表达式中,我是否正在进行额外的矩阵分配?
答案 0 :(得分:3)
让我们做一些实际的时间测试:
In [793]: timeit m=np.array([[i*j for j in range(N)] for i in range(M)])
10000 loops, best of 3: 47.8 µs per loop
In [794]: %%timeit
.....: m=np.zeros((N,M),int)
.....: for i in range(M):
for j in range(N):
m[i,j] = i*j
.....:
10000 loops, best of 3: 40.2 µs per loop
所以预分配和分配稍微快一些 - 但不是那么大。
与矢量乘法形成对比:
In [796]: timeit np.arange(M)[:,None]*np.arange(N)[None,:]
10000 loops, best of 3: 17.1 µs per loop
对较大的数组执行相同的操作:
In [797]: N,M=1000,1000
In [798]: timeit m=np.array([[i*j for j in range(N)] for i in range(M)])
1 loops, best of 3: 325 ms per loop
In [799]: %%timeit
m=np.zeros((N,M),int)
for i in range(M):
for j in range(N):
m[i,j] = i*j
.....:
1 loops, best of 3: 338 ms per loop
In [800]: timeit np.arange(M)[:,None]*np.arange(N)[None,:]
100 loops, best of 3: 12.5 ms per loop
两次迭代仍然是一对一的;矢量化要好得多。
我可以使用fromiter
削减一些迭代时间,但没有像矢量化那样。
In [805]: timeit np.fromiter([i*j for j in range(N) for i in range(M)],int).reshape(N,M)
1 loops, best of 3: 235 ms per loop
这是一个经常出现的问题,我只是懒得去寻找最好的复制品。 :)通常人们声称他们的计算是一些复杂的黑盒子只需要标量,所以没有办法对它进行矢量化。
有一个np.vectorize
函数可以包装你的计算,但它的目的是简化广播之类的事情,并且不会对加速代码提出任何要求。它仍然需要迭代。
如果计算小而快,则值得关注迭代方法,但如果它很复杂,那么花在迭代机制上的时间比例很小,你应该关注黑盒的速度。
答案 1 :(得分:1)
第一个解决方案的问题是列表理解,它产生一个列表列表并将其分配给m
。但是,从您的第一个语句开始,您似乎希望m
成为一个numpy数组(这是在执行scipy.zeros()
时在幕后创建的数组)。所以,你基本上创建了一个数组,然后用列表覆盖它。如果您希望将数据结构保持为np.array
,那么嵌套的for
循环是最好的方法。
另外,您说" matrix ",但创建了一个数组。如果您想要一个实际矩阵(例如,进行矩阵数学运算),请将嵌套列表推导传递给np.matrix()
:
# assuming you've already run `import numpy as np`
In [5]: m = np.matrix([[i * j for j in range(10)] for i in range(10)])
In [6]: m
Out[6]:
matrix([[ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9],
[ 0, 2, 4, 6, 8, 10, 12, 14, 16, 18],
[ 0, 3, 6, 9, 12, 15, 18, 21, 24, 27],
[ 0, 4, 8, 12, 16, 20, 24, 28, 32, 36],
[ 0, 5, 10, 15, 20, 25, 30, 35, 40, 45],
[ 0, 6, 12, 18, 24, 30, 36, 42, 48, 54],
[ 0, 7, 14, 21, 28, 35, 42, 49, 56, 63],
[ 0, 8, 16, 24, 32, 40, 48, 56, 64, 72],
[ 0, 9, 18, 27, 36, 45, 54, 63, 72, 81]])
哎呀,即使你想要一个数组,也可以将嵌套的listcomp传递给上面的数组构造函数,然后你就可以了。
答案 2 :(得分:1)
第二个赋值(生成器)确实创建了一个新矩阵。 如果您使用python' id() function,您可以看到m指向该分配后的其他位置。
例如:
SELECT *
FROM Orders
WHERE mod(OrderID,2) = 0;