矩阵分配到位?
Matrix assignment in place?
假设我初始化一个矩阵如下:
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
但假设生成器表达式对我来说更方便,以我安排代码的方式。
我想知道的是:在上面的生成器表达式中,我是否在进行额外的矩阵分配?
您的第一个解决方案(列表理解)的问题在于它会生成一个列表列表并将其分配给 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 传递给数组构造函数,你就完成了。
第二个赋值(生成器)确实创建了一个新矩阵。
如果您使用 python 的 id() function,您可以看到 m 在该赋值后指向不同的位置。
例如:
>> import scipy
>> m = scipy.zeros((10, 10))
>> id(m)
4455211696
>> m = scipy.array([[i * j for j in range(10)] for i in range(10)])
>> id(m)
4478936688
让我们做一些实际的时间测试:
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
2 次迭代保持并驾齐驱;矢量化的好多了。
我可以使用 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
函数可以包装您的计算,但它旨在简化诸如广播之类的事情,并且没有声称可以加快代码速度。还是要迭代。
如果计算量小且速度快,值得关注迭代方法,但如果计算量大,在迭代机制上花费的时间比例较小,需要关注迭代的速度黑匣子。
假设我初始化一个矩阵如下:
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
但假设生成器表达式对我来说更方便,以我安排代码的方式。
我想知道的是:在上面的生成器表达式中,我是否在进行额外的矩阵分配?
您的第一个解决方案(列表理解)的问题在于它会生成一个列表列表并将其分配给 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 传递给数组构造函数,你就完成了。
第二个赋值(生成器)确实创建了一个新矩阵。 如果您使用 python 的 id() function,您可以看到 m 在该赋值后指向不同的位置。
例如:
>> import scipy
>> m = scipy.zeros((10, 10))
>> id(m)
4455211696
>> m = scipy.array([[i * j for j in range(10)] for i in range(10)])
>> id(m)
4478936688
让我们做一些实际的时间测试:
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
2 次迭代保持并驾齐驱;矢量化的好多了。
我可以使用 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
函数可以包装您的计算,但它旨在简化诸如广播之类的事情,并且没有声称可以加快代码速度。还是要迭代。
如果计算量小且速度快,值得关注迭代方法,但如果计算量大,在迭代机制上花费的时间比例较小,需要关注迭代的速度黑匣子。