2016-01-31 86 views
3

假设我初始化一个矩阵如下:矩阵分配到位?

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

如果您习惯于像C这样的语言,则数组分配可能看起来很昂贵,但与CPython的无JIT字节码解释和动态一切的内在开销相比,它的成本低廉。如果您希望NumPy/SciPy代码高效,最关键的是使用NumPy矢量化操作将工作推送到C级循环中,以避免所有这些开销。试图最小化分配并不会有多大帮助,甚至可能会使代码变慢。 – user2357112

回答

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 

的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函数包装你的计算,但它的目的是精简如广播的东西,并没有声称加快代码。它仍然需要迭代。

如果计算结果很小且很快,那么注意迭代方法是值得的,但是如果它很复杂,在迭代机制上花费的时间比例很小,并且您应该关注黑盒子的速度。

1

您的第一个解决方案(列表理解)的问题是它生成一个列表并将其分配给m。但是,从第一条语句开始,您似乎希望m是一个numpy数组(这是执行scipy.zeros()时在幕后创建的数组)。所以,你基本上创建了一个数组,然后用一个列表覆盖它。如果你想保持数据结构为np.array,嵌套的for循环是最好的选择。

另外,你说“矩阵”,但创建了一个数组。如果你想要一个实际的矩阵(例如,做矩阵数学),通过你的嵌套列表理解到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到数组构造像上面一样,你都准备好了。

+0

对不起,我错过了作业中的'scipy.array(...)'。编辑了这个问题。所以你说这个任务比for循环更好?哪个更快? – becko

+0

我不鼓励使用'np.matrix' - 除非OP嵌入到旧的MATLAB操作中。 – hpaulj

+0

@hpaulj为什么是这样? – MattDMo

1

第二个赋值(生成器)确实创建了一个新的矩阵。 如果您使用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