Python,Scipy:使用大邻接矩阵构建三元组

Python,Scipy:使用大邻接矩阵构建三元组,python,numpy,data-mining,scipy,adjacency-matrix,Python,Numpy,Data Mining,Scipy,Adjacency Matrix,我使用邻接矩阵来表示一个朋友网络,可以直观地解释为 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

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

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
从numpy导入*
从scipy导入*
def构建三角形(G):
#G是稀疏邻接矩阵
开始=时间。时间()
ctr=0
我这样做是为了确保它是对称的
三元组=[]
对于arange中的i(G.shape[0]-1):#对于每一行,但最后一行除外
J、 J=G[i,:].nonzero()#J:用户i的主要朋友
#我做J,J,因为我不关心行值
J=J[Ji]#仅在i以下计算以避免重复
对于k中的k:
ctr=ctr+1
三元组追加((i,j,k))
打印(“三元组的总数:%d”%ctr)
打印(“运行时间为%.2f”%(time.time()-start())
返回三元组
我能够在大约21分钟内在csr_矩阵上运行代码。该矩阵为1032570 x 1032570,包含88910个存储元素。总共生成了2178893个三元组

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

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

以下是一些优化建议:

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²意味着一个非常大的矩阵,即使它非常稀疏,它的平方也会非常小,并且会占用大量内存。(我曾经解决过一个类似的问题,我考虑了维基百科的文章在两个距离之间的链接,在C++的超级计算机集群节点上花了20分钟来解决。这不是一个小问题。维基百科相邻矩阵是几个数量级的。)

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

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

我不认为在一个语句中两次赋值(
J,J=
)在Python中有任何保证的意义。我发现它非常令人困惑,从你的评论来看,你也是,所以你可能想摆脱它。@larsmans,我的道歉。非零()以二维数组的形式返回矩阵的索引。或者,我可以执行
行,col=G[I,:]。nonzero()
然后是
J=col
。我使用了
J,J=
方法,因为我担心内存使用,想吃掉行数组,因为它不是必需的。不要道歉,我不是故意苛刻的。这不是Pythonic习惯用法,我认为Guido在lib中可以在Python版本之间更改该结构的含义o你不能依赖它工作。如果它真的很重要,最好
del
一个变量,尽管在这种情况下
J=G[i,:].nonzero()[1]
也可以。谢谢你的建议。它确实让代码变得干净了一点。你在维基百科文章中所做的工作正是我要做的。我将更深入地研究解决这个问题的线性代数方法。当你提到“真实性能”时,你能详细说明如何将两个矩阵相乘并得到一个列表吗(而不是计数)两步配对?@迭代器:将一个方矩阵与自身相乘,可以得到一个相同秩的新矩阵,该矩阵在步距2处连接的所有i,j的值都大于0。矩阵乘法在SciPy中是一个高度优化的运算(我认为是用C实现的,甚至可能是用Fortran实现的)。然后,您可以自己提取列表,而不必在矩阵中进行太多搜索。是的,您可以得到步骤2计数,这就是我所说的:您可以得到(I,*,k)对的计数。中间j节点的标识丢失。我理解(并声明)你所说的一切,但你还没有证明整个三元组命名的任何加速。我想你没有把这个问题想清楚。谢谢你的回答。我甚至没有考虑过这种方法,但它很有意义。你基本上把问题简化为寻找两个的排列。所有三元组都是唯一的吗?@wiLH:你是说(玛丽,苏珊,乔)和(乔,苏珊,玛丽)是否计算为不同的或相同的?@Iterator我的意思是将它们计算为相同的。我相信这种方法在这方面确实有效。进一步研究后,我现在意识到,每一个新行都保证不在早期的排列中。+1到user772649。这很好。我想在我使用的其他语言中找到这个函数n、 我一直都得自己写。