设G是图。所以G是一组节点和一组链接。我需要找到一种快速分割图形的方法。我现在正在使用的图表只有120 * 160个节点,但我可能很快会在另一个上下文(不是医学,但是网站开发)中处理同等问题,有数百万个节点。
所以,我所做的是将所有链接存储到图表矩阵中:
M=numpy.mat(numpy.zeros((len(data.keys()),len(data.keys()))))
如果节点s连接到节点t,则M在位置s,t中保持1。我确保M是对称的M [s,t] = M [t,s],并且每个节点都链接到它自己M [s,s] = 1.
如果我记得很好,如果我将M与M相乘,则结果是一个矩阵,表示连接通过两个步骤到达的顶点的图形。
因此,我继续将M与自身进行对比,直到矩阵中的零数不再减少为止。现在我有连接组件的列表。 现在我需要对这个矩阵进行聚类。
到目前为止,我对算法非常满意。我认为它简单,优雅,而且速度相当快。我在这部分遇到了麻烦。
基本上我需要将此图分割为其连接的组件。
我可以浏览所有节点,看看它们连接的是什么。
但是如何排序矩阵重新排序。但我不知道是否可以这样做。
到目前为止,代码如下:
def findzeros(M):
nZeros=0
for t in M.flat:
if not t:
nZeros+=1
return nZeros
M=numpy.mat(numpy.zeros((len(data.keys()),len(data.keys()))))
for s in data.keys():
MatrixCells[s,s]=1
for t in data.keys():
if t<s:
if (scipy.corrcoef(data[t],data[s])[0,1])>threashold:
M[s,t]=1
M[t,s]=1
nZeros=findzeros(M)
M2=M*M
nZeros2=findzeros(M2)
while (nZeros-nZeros2):
nZeros=nZeros2
M=M2
M2=M*M
nZeros2=findzeros(M2)
有人建议我使用SVD分解。以下是5x5图表上问题的简单示例。我们将使用它,因为使用19200x19200方阵并不容易看到簇。
import numpy
import scipy
M=numpy.mat(numpy.zeros((5,5)))
M[1,3]=1
M[3,1]=1
M[1,1]=1
M[2,2]=1
M[3,3]=1
M[4,4]=1
M[0,0]=1
print M
u,s,vh = numpy.linalg.linalg.svd(M)
print u
print s
print vh
基本上这里有4个集群:(0),(1,3),(2),(4) 但我仍然没有看到svn在这种情况下如何提供帮助。
答案 0 :(得分:12)
为什么不使用真实的图表库,例如Python-Graph?它有function to determine connected components(虽然没有提供示例)。我想象一个专用的库会比你编写的任何特殊的图形代码更快。
编辑:NetworkX似乎比python-graph更好的选择;它的documentation (here for the connected components function)肯定是。
答案 1 :(得分:7)
在SciPy中,您可以使用sparse matrices。还要注意,有更有效的方法可以将矩阵自身相乘。无论如何,你要做的是通过SVD分解完成的。
答案 2 :(得分:3)
还有graph_tool和networkit具有连接组件的高效例程,并且都能有效地存储网络。如果您要使用数百万个节点,那么networkx可能就不够了(这是纯粹的python afaik)。这两个工具都是用C ++编写的,因此可以处理具有合理运行时间的大图的分析。
正如菲尔指出的那样,你的方法对于大型图形(我们说的是几天,几周,几个月......)的计算时间非常长,而你对一百万个节点图表的表示将需要一百万个像千兆字节的内存!
答案 3 :(得分:2)
这是一些天真的实现,它使用depth first search找到连接的组件,我在前一段时间写过。虽然它非常简单,但它可以很好地扩展到数万个顶点和边缘......
import sys
from operator import gt, lt
class Graph(object):
def __init__(self):
self.nodes = set()
self.edges = {}
self.cluster_lookup = {}
self.no_link = {}
def add_edge(self, n1, n2, w):
self.nodes.add(n1)
self.nodes.add(n2)
self.edges.setdefault(n1, {}).update({n2: w})
self.edges.setdefault(n2, {}).update({n1: w})
def connected_components(self, threshold=0.9, op=lt):
nodes = set(self.nodes)
components, visited = [], set()
while len(nodes) > 0:
connected, visited = self.dfs(nodes.pop(), visited, threshold, op)
connected = set(connected)
for node in connected:
if node in nodes:
nodes.remove(node)
subgraph = Graph()
subgraph.nodes = connected
subgraph.no_link = self.no_link
for s in subgraph.nodes:
for k, v in self.edges.get(s, {}).iteritems():
if k in subgraph.nodes:
subgraph.edges.setdefault(s, {}).update({k: v})
if s in self.cluster_lookup:
subgraph.cluster_lookup[s] = self.cluster_lookup[s]
components.append(subgraph)
return components
def dfs(self, v, visited, threshold, op=lt, first=None):
aux = [v]
visited.add(v)
if first is None:
first = v
for i in (n for n, w in self.edges.get(v, {}).iteritems()
if op(w, threshold) and n not in visited):
x, y = self.dfs(i, visited, threshold, op, first)
aux.extend(x)
visited = visited.union(y)
return aux, visited
def main(args):
graph = Graph()
# first component
graph.add_edge(0, 1, 1.0)
graph.add_edge(1, 2, 1.0)
graph.add_edge(2, 0, 1.0)
# second component
graph.add_edge(3, 4, 1.0)
graph.add_edge(4, 5, 1.0)
graph.add_edge(5, 3, 1.0)
first, second = graph.connected_components(op=gt)
print first.nodes
print second.nodes
if __name__ == '__main__':
main(sys.argv)
答案 4 :(得分:2)
正如其他人所指出的,不需要重新发明轮子。已经将很多想法付诸于最佳聚类技术。 Here是一个众所周知的集群计划。
答案 5 :(得分:2)
找到最佳图分区是NP难问题,因此无论算法如何,它都将是近似或启发式算法。毫不奇怪,不同的聚类算法产生(疯狂地)不同的结果。
Newman模块化算法的Python实现: modularity
答案 6 :(得分:0)
看起来有一个库PyMetis,它会根据链接列表为您划分图表。通过传递链接节点的原始列表(而不是矩阵乘法导出的链接节点),从图表中提取链接列表应该相当容易。
重复执行M'= MM对于M的大阶数不会有效。对于N阶矩阵的完全矩阵乘法将花费N次乘法和每个元素的N-1次加法,其中有N 2 ,即O(N 3 )操作。如果你将其缩放到“数百万个节点”,那么每个矩阵 - 矩阵乘法就是O(10 18 )运算,你想要做几个。
简而言之,你不想这样做。来自Vartec的SVD suggestion将是唯一合适的选择。您最好的选择就是使用PyMetis,而不是尝试重新发明图分区。
答案 7 :(得分:0)
SVD算法不适用于此,但Phil H是正确的。