【问题标题】:Python, Scipy: Building triplets using large adjacency matrixPython,Scipy:使用大型邻接矩阵构建三元组
【发布时间】:2011-10-19 09:45:31
【问题描述】:

我使用邻接矩阵来表示朋友网络,可以直观地解释为

Mary     0        1      1      1

Joe      1        0      1      1

Bob      1        1      0      1

Susan    1        1      1      0 

         Mary     Joe    Bob    Susan

使用这个矩阵,我想编译一个所有可能的友谊三角形的列表,条件是用户 1 是用户 2 的朋友,用户 2 是用户 3 的朋友。对于我的列表,不需要用户 1是用户 3 的朋友。

(joe, mary, bob)
(joe, mary, susan)
(bob, mary, susan)
(bob, joe, susan)

我有一些代码可以很好地处理小三角形,但我需要它来扩展非常大的稀疏矩阵。

from numpy import *
from scipy import *

def buildTriangles(G):
    # G is a sparse adjacency matrix
    start = time.time()
    ctr = 0
    G = G + G.T          # I do this to make sure it is symmetric
    triples = []
    for i in arange(G.shape[0] - 1):  # for each row but the last one
        J,J = G[i,:].nonzero()        # J: primary friends of user i
                                      # I do J,J because I do not care about the row values
        J = J[ J < i ]                # only computer the lower triangle to avoid repetition
        for j in J:
            K, buff = G[:,j].nonzero() # K: secondary friends of user i
            K = K[ K > i ]             # only compute below i to avoid repetition
            for k in K:
                ctr = ctr + 1
                triples.append( (i,j,k) )
    print("total number of triples: %d" % ctr)
    print("run time is %.2f" % (time.time() - start())
    return triples

我能够在大约 21 分钟内在 csr_matrix 上运行代码。该矩阵为 1032570 x 1032570,包含 88910 个存储元素。总共生成了 2178893 个三元组。

我需要能够对 1968654 x 1968654 稀疏矩阵和 9428596 个存储元素做类似的事情。

我对 python 非常陌生(不到一个月的经验),并且在线性代数方面不是最出色的,这就是为什么我的代码没有利用矩阵运算的原因。 任何人都可以提出任何改进建议或让我知道我的目标是否现实吗?

【问题讨论】:

  • 我认为在一条语句 (J,J=) 中两次分配相同的值在 Python 中没有任何保证的意义。从你的评论来看,我觉得这很令人困惑,你也是,所以你可能想摆脱它。
  • @larsmans 我很抱歉。 nonzero() 将矩阵的索引作为二维数组返回。或者我可以先完成row, col = G[i,:].nonzero(),然后再完成J = col。我使用了J,J= 方法,因为我担心内存使用情况并想吃掉行数组,因为它不需要。
  • 别道歉,我不是故意的。这不是 Pythonic 的习惯用法,我认为 Guido 在 lib 中可以改变 Python 版本之间该构造的含义,因此您不能依赖它来工作。如果它真的很重要,最好使用del 一个变量,尽管在这种情况下J = G[i, :].nonzero()[1] 也可以工作。
  • 感谢您的建议。它确实清理了一些代码。你对维基百科文章所做的工作正是我想要做的。我将更多地研究解决问题的线性代数方法。

标签: python numpy data-mining scipy adjacency-matrix


【解决方案1】:

我认为你只能在行或列中找到三角形。例如:

Susan    1        1      1      0 
        Mary     Joe    Bob    Susan

这意味着 Mary、Joe、Bob 都是 Susan 的朋友,因此,使用组合从 [Mary, Joe, Bob] 中选择两个人,并将其与 Susan 组合将得到一个三角形。 itertools.combinations() 可以快速完成。

代码如下:

import itertools
import numpy as np

G = np.array(   # clear half of the matrix first
    [[0,0,0,0],
     [1,0,0,0],
     [1,1,0,0],
     [1,1,1,0]])
triples = []     
for i in xrange(G.shape[0]):
    row = G[i,:]
    J = np.nonzero(row)[0].tolist() # combinations() with list is faster than NumPy array.
    for t1,t2 in itertools.combinations(J, 2):
        triples.append((i,t1,t2))
print triples

【讨论】:

  • 感谢您的回答。我什至没有考虑过这种方法,但它很有意义。您基本上将问题简化为找到两个的排列。所有的三元组都是唯一的吗?
  • @will:澄清一下,您是说 (Mary, Susan, Joe) 和 (Joe, Susan, Mary) 被视为不同还是相同?
  • @Iterator 我的意思是把它们算作相同的。我相信这种方法在这方面确实有效。在进一步查看之后,我现在意识到每个新行都保证不会出现在早期的排列中。
  • +1 到 user772649。这很棒。我想在我工作的其他语言中找到这个函数。我一直不得不自己写。
【解决方案2】:

以下是一些优化建议:

K = K[ K > i ]             # only compute below i to avoid repetition
for k in K:
    ctr = ctr + 1
    triples.append( (i,j,k) )

不要在循环中递增,这非常慢。只需ctr += K.shape[0] 即可。然后,通过将append 替换为

,完全消除嵌套最深的循环
triples += ((i, j, k) for k in K[K > i])

现在,如果您想要在此任务上真正表现,您将不得不进入一些线性代数。 “我想编译所有可能的友谊三角形的列表”意味着您想要对邻接矩阵进行平方,您可以使用简单的**2

然后意识到 1.968.654² 意味着一个非常大的矩阵,即使它非常稀疏,它的平方也会小得多,并且会占用大量内存。 (我曾经解决过一个类似的问题,我考虑了距离为 2 的 Wikipedia 文章之间的链接,这需要 20 分钟才能解决,在超级计算机集群节点上在 C++ 中。这是这不是一个小问题。不过,Wikipedia 邻接矩阵的密度要高几个数量级。)

【讨论】:

  • 当您提到“真实性能”时 - 您能否详细说明如何将两个矩阵相乘并获得两步配对的列表(而不是计数)?
  • @Iterator: 将方阵与自身相乘会得到一个具有相同等级的新矩阵,对于所有 ij 的值 >0它们以步距 2 连接。矩阵乘法是 SciPy 中经过高度优化的操作(我认为是用 C 实现的,甚至可能是 Fortran)。然后,您可以自己提取列表,而无需在矩阵中进行搜索。
  • 是的,你得到了第 2 步的计数,这就是我所说的:你可以得到 (i,*,k) 对的计数。中间 j 个节点的身份丢失。我理解(并陈述)你所说的一切,但你没有证明对完整三元组的命名有任何加速。我认为你并没有从头到尾考虑到这一点。
猜你喜欢
  • 2017-02-17
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2019-09-09
  • 2018-10-14
相关资源
最近更新 更多