如何在Python中对图形进行集群?
设G是一个图。所以G是一组节点和一组链接。我需要找到一种快速的方法来划分图形。我现在正在处理的图只有120*160个节点,但我可能很快就会处理一个同等的问题,在另一个环境中(不是医学,而是网站开发),有数百万个节点 因此,我所做的是将所有链接存储到一个图形矩阵中:如何在Python中对图形进行集群?,python,sorting,matrix,cluster-analysis,graph-theory,Python,Sorting,Matrix,Cluster Analysis,Graph Theory,设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=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,结果是一个矩阵,表示通过两个步骤连接顶点的图形
所以我继续对它本身进行多重叠加,直到矩阵中的零的数量不再减少。现在我有了连接组件的列表。
现在我需要对这个矩阵进行聚类
到目前为止,我对算法相当满意。我认为它简单、优雅,而且相当快。这部分我有问题
本质上,我需要将这个图拆分为它的连接组件
我可以浏览所有节点,看看它们连接到什么
但是对矩阵进行排序,对行进行重新排序呢。但我不知道是否有可能做到这一点
以下是迄今为止的代码:
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)
这里基本上有4个集群:(0)、(1,3)、(2)、(4)
但我仍然不知道svn在这方面有什么帮助。在SciPy中,您可以使用。还要注意的是,有更有效的方法将矩阵本身相乘。无论如何,你要做的事情可以通过SVD分解来完成
为什么不使用真正的图形库,比如?它有一个示例(尽管没有提供示例)。我可以想象,一个专用的库将比您编写的任何特殊图形代码都要快
编辑:看起来它可能是比python图形更好的选择;当然是这样。看起来有一个库,它将根据链接列表为您划分图形。通过将链接节点的原始列表(而不是矩阵乘以导出的列表)传递给图,从图中提取链接列表应该相当容易 重复执行M'=MM对于M的大阶而言是无效的。对于N阶矩阵的全矩阵乘法将花费N次乘法和N-1次加法,其中有N2次,即O(N3)次运算。如果要将其扩展到“数百万个节点”,则每个矩阵乘法将执行O(1018)个操作,其中您需要执行几个操作
简而言之,你不想这样做。来自瓦特克的援助将是那里唯一合适的选择。最好的选择是只使用PyMetis,而不是尝试重新创建图形分区 这里有一些简单的实现,它使用我不久前写过的代码查找连接的组件。虽然它非常简单,但它可以很好地扩展到上万个顶点和边
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)
SVD算法在这里不适用,但在其他方面Phil H是正确的 正如其他人所指出的,没有必要重新发明轮子。人们对最优聚类技术进行了大量的研究。是一个著名的聚类程序。找到一个最优的图划分是一个NP难问题,因此无论采用何种算法,它都将是一个近似或启发式问题。毫不奇怪,不同的聚类算法会产生(大范围)不同的结果 纽曼模块化算法的Python实现: 另外:,还有和,它们对连接的组件具有有效的例程,并且都有效地存储网络。如果要处理数百万个节点,networkx可能不够(它是纯python afaik)。这两种工具都是用C++编写的,因此可以处理合理运行时间的大型图形分析。
正如Phil指出的,对于大型图,您的方法的计算时间将非常长(我们谈论的是天、周、月……),而对于一百万个节点的图,您的表示将需要大约一百万GB的内存 多谢各位。我查阅了资源,但我真的不知道它有什么帮助。我用一个简单的例子更新了这个问题,以及SVN des似乎无法解决这个问题。也许我用错了?那怎么办呢?无论如何谢谢:)这就是SVD(单值分解)。基本上,对于数百万个节点这样大的对象,需要近似算法,而不是精确算法(图聚类是NP完全的)。文章得到了解释这些算法的文章的链接。你是想重新创造PageRank或点击率吗?不是真的。现在只需对属于哪个生物细胞的数据进行排序。在未来,我有一个类似的问题,最终将生成一个搜索引擎。但不是在书页上。不使用链接。(在这个阶段不能再多说了:))。无论如何,祝贺你!好的,哈哈。那么潜在的语义分析呢?;-)好吧,我不会扯你的舌头。请记住,小规模可能发生的事情在大规模时变得非常复杂。大多数图算法都有很高的多项式复杂度,所以在1mln节点上使用它是非常困难的。你能澄清一下你的问题吗。我发现了一个“可能吗”(答案总是肯定的,所以这不是你真正的问题)和一个“我不知道SVD能帮上什么忙”,这不是一个真正的问题。你的问题是什么?你好,谢谢你花时间回答我的问题。问题是,明确的是:“我应该如何确定连接的成分?”我以为你理解它,以及在哪里有一些天真的乐趣。“Pietro Speroni:考虑重写你的问题,使它更简单,更集中和更清楚。”长时间的讨论很难跟上。简短的代码示例和一个非常明显的问题更好。您提供了一些代码,所以问“我应该如何确定…”似乎是不对的。谢谢您,但既然我确实收到了答案,我是l
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)