快速python矩阵创建和迭代

时间:2014-05-29 17:18:38

标签: python performance python-2.7 cpu-speed

我需要从权重矩阵的值开始创建矩阵。在创建和迭代时,哪个是在速度方面保持矩阵的最佳结构?我正在考虑一个列表列表或一个numpy 2D数组,但它们对我来说似乎都很慢。 我需要的是:

numpy array
A = np.zeros((dim, dim))
for r in range(A.shape[0]):
    for c in range(A.shape[0]):
        if(r==c):
            A.itemset(node_degree[r])
        else:
            A.itemset(arc_weight[r,c])

list of lists
l = []
for r in range(dim):
    l.append([])
    for c in range(dim):
        if(i==j):
            l[i].append(node_degree[r])
        else:
            l[i].append(arc_weight[r,c])

其中dim也可以是20000,node_degree是向量而arc_weight是另一个矩阵。我用c ++编写它,它花费的时间不到0.5秒,而其他两个在python中的时间超过20秒。我知道python不是c ++,但我需要尽可能快。 谢谢大家。

3 个答案:

答案 0 :(得分:2)

如果你已经知道它的大小,那么你不应该追加到列表中。

首先使用列表推导来预先分配内存,并使用r, c而不是xrange()生成range()值,因为您使用的是Python< 3.x(见here):

l = [[0 for c in xrange(dim)] for r in xrange(dim)]

更好的是,您可以使用以下方式一次性构建所需内容:

l = [[node_degree[r] if r == c else arc_weight[r,c] 
            for c in xrange(dim)] for r in xrange(dim)]

与原始实现相比,这应该使用更少的内存(因为xrange()生成器)和更少的时间,因为您通过预先指定维度来消除重新分配内存的需要。

答案 1 :(得分:0)

Numpy矩阵通常更快,因为他们知道它们的尺寸和入口类型。

在您的特定情况下,由于您已经创建了arc_weight和node_degree矩阵,因此您可以直接从arc_weight创建矩阵,然后替换对角线:

A = np.matrix(arc_matrix)
np.fill_diagonal(A, node_degree)

另一个选择是用一个函数替换double循环,该函数将右元素放在每个位置并从函数创建一个矩阵:

def fill_matrix(r, c):
    return arc_weight[r,c] if r != c else node_degree[r]

A = np.fromfunction(fill_matrix, (dim, dim))

根据经验,numpy必须不惜一切代价避免循环。第一种方法应该更快,但你应该分析两者,看看哪些方法适合你。您还应该考虑到您似乎在内存中复制了数据集,因此如果它真的很大,您可能会遇到麻烦。最好的想法是直接创建矩阵,完全避免使用arc_weight和node_degree。

编辑:列表理解和numpy矩阵创建之间的一些简单时间比较。由于我不知道你的arc_weight和node_degree是如何定义的,所以我只编写了两个随机函数。如果函数有条件,numpy.fromfunction似乎有点抱怨,所以我分两步构造矩阵。

import numpy as np

def arc_weight(a,b):
    return a+b

def node_degree(a):
    return a*a

def create_as_list(N):
    return [[arc_weight(c,r) if c!=r else node_degree(c) for c in xrange(N)] for r in xrange(N)]

def create_as_numpy(N):
    A = np.fromfunction(arc_weight, (N,N))
    np.fill_diagonal(A, node_degree(np.arange(N)))
    return A

此处N=2000的时间安排:

time A = create_as_list(2000)
CPU times: user 839 ms, sys: 16.5 ms, total: 856 ms
Wall time: 845 ms

time A = create_as_numpy(2000)
CPU times: user 83.1 ms, sys: 12.9 ms, total: 96 ms
Wall time: 95.3 ms

答案 2 :(得分:0)

复制arc_weight并使用node_degree的值填充对角线。对于20000×20000输出,我的机器需要大约1.6秒:

>>> import numpy
>>> dim = 20000
>>> arc_weight = numpy.arange(dim**2).reshape([dim, dim])
>>> node_degree = numpy.arange(dim)
>>> import timeit
>>> timeit.timeit('''
... A = arc_weight.copy()
... A.flat[::dim+1] = node_degree
... ''', '''
... from __main__ import dim, arc_weight, node_degree''',
... number=1)
1.6081738501125764

一旦你有了你的阵列,尽量不要迭代它。与广播运算符和NumPy内置函数相比,Python级循环是一种性能灾难。