矩阵分配到位?

时间:2016-01-31 03:08:19

标签: python matrix scipy variable-assignment

假设我按如下方式初始化矩阵:

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

但是假设生成器表达式对我来说更方便,就像我安排我的代码一样。

我想知道的是:在上面的生成器表达式中,我是否正在进行额外的矩阵分配?

3 个答案:

答案 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;