如何用networkx绘制社区
Posted
技术标签:
【中文标题】如何用networkx绘制社区【英文标题】:how to draw communities with networkx 【发布时间】:2017-09-18 09:03:45 【问题描述】:如何使用 python networkx 绘制一个包含社区的图表,如下图:
image url
【问题讨论】:
【参考方案1】:networkx.draw_networkx_nodes
和 networkx.draw_networkx_edges
的文档解释了如何设置节点和边缘颜色。可以通过查找每个社区的节点位置,然后绘制一个包含所有位置(然后是一些位置)的补丁(例如matplotlib.patches.Circle
)来制作社区的补丁。
难点是图形布局/设置节点位置。 AFAIK,networkx 中没有“开箱即用”实现所需图形布局的例程。您要做的是:
将社区相对于彼此定位:创建一个新的加权图,其中每个节点对应一个社区,权重对应于社区之间的边数。使用您最喜欢的图形布局算法(例如spring_layout
)获得体面的布局。
定位每个社区内的节点:为每个社区创建一个新图。找到子图的布局。
结合 1) 和 3) 中的节点位置。例如。将 1) 中计算的社区位置放大 10 倍;将这些值添加到该社区内所有节点的位置(如 2 中计算的)。
我一直想实现这个。我可能会在今天晚些时候或周末这样做。
编辑:
瞧。现在你只需要在节点周围(后面)画出你最喜欢的补丁。
import numpy as np
import matplotlib.pyplot as plt
import networkx as nx
def community_layout(g, partition):
"""
Compute the layout for a modular graph.
Arguments:
----------
g -- networkx.Graph or networkx.DiGraph instance
graph to plot
partition -- dict mapping int node -> int community
graph partitions
Returns:
--------
pos -- dict mapping int node -> (float x, float y)
node positions
"""
pos_communities = _position_communities(g, partition, scale=3.)
pos_nodes = _position_nodes(g, partition, scale=1.)
# combine positions
pos = dict()
for node in g.nodes():
pos[node] = pos_communities[node] + pos_nodes[node]
return pos
def _position_communities(g, partition, **kwargs):
# create a weighted graph, in which each node corresponds to a community,
# and each edge weight to the number of edges between communities
between_community_edges = _find_between_community_edges(g, partition)
communities = set(partition.values())
hypergraph = nx.DiGraph()
hypergraph.add_nodes_from(communities)
for (ci, cj), edges in between_community_edges.items():
hypergraph.add_edge(ci, cj, weight=len(edges))
# find layout for communities
pos_communities = nx.spring_layout(hypergraph, **kwargs)
# set node positions to position of community
pos = dict()
for node, community in partition.items():
pos[node] = pos_communities[community]
return pos
def _find_between_community_edges(g, partition):
edges = dict()
for (ni, nj) in g.edges():
ci = partition[ni]
cj = partition[nj]
if ci != cj:
try:
edges[(ci, cj)] += [(ni, nj)]
except KeyError:
edges[(ci, cj)] = [(ni, nj)]
return edges
def _position_nodes(g, partition, **kwargs):
"""
Positions nodes within communities.
"""
communities = dict()
for node, community in partition.items():
try:
communities[community] += [node]
except KeyError:
communities[community] = [node]
pos = dict()
for ci, nodes in communities.items():
subgraph = g.subgraph(nodes)
pos_subgraph = nx.spring_layout(subgraph, **kwargs)
pos.update(pos_subgraph)
return pos
def test():
# to install networkx 2.0 compatible version of python-louvain use:
# pip install -U git+https://github.com/taynaud/python-louvain.git@networkx2
from community import community_louvain
g = nx.karate_club_graph()
partition = community_louvain.best_partition(g)
pos = community_layout(g, partition)
nx.draw(g, pos, node_color=list(partition.values())); plt.show()
return
附录
虽然总体思路是合理的,但我上面的旧实现存在一些问题。最重要的是,对于规模不均的社区,实施效果并不好。具体来说,_position_communities
在画布上为每个社区提供相同数量的房地产。如果一些社区比其他社区大得多,这些社区最终会被压缩到与小社区相同的空间中。显然,这并不能很好地反映图的结构。
我编写了一个用于可视化网络的库,称为netgraph。它包括上述社区布局例程的改进版本,在安排社区时还考虑了社区的大小。它与networkx
和igraph
Graph 对象完全兼容,因此制作漂亮的图形应该是容易和快速的(至少是这样的想法)。
import matplotlib.pyplot as plt
import networkx as nx
# installation easiest via pip:
# pip install netgraph
from netgraph import Graph
# create a modular graph
partition_sizes = [10, 20, 30, 40]
g = nx.random_partition_graph(partition_sizes, 0.5, 0.1)
# since we created the graph, we know the best partition:
node_to_community = dict()
node = 0
for community_id, size in enumerate(partition_sizes):
for _ in range(size):
node_to_community[node] = community_id
node += 1
# # alternatively, we can infer the best partition using Louvain:
# from community import community_louvain
# node_to_community = community_louvain.best_partition(g)
community_to_color =
0 : 'tab:blue',
1 : 'tab:orange',
2 : 'tab:green',
3 : 'tab:red',
node_color = node: community_to_color[community_id] for node, community_id in node_to_community.items()
Graph(g,
node_color=node_color, node_edge_width=0, edge_alpha=0.1,
node_layout='community', node_layout_kwargs=dict(node_to_community=node_to_community),
edge_layout='bundled', edge_layout_kwargs=dict(k=2000),
)
plt.show()
【讨论】:
哇!这是个好主意。感谢实施 倒数第二行必须是nx.draw(g, pos, node_color=list(partition.values()))
@MortezaShahriariNia 感谢您的提醒。显然他们改变了partition
的类型。现在改了。
我无法让netgraph
包在我的应用程序上运行(我有一些关于维度的错误),但我使用了您的community_layout
代码,它就像一个魅力。谢谢!
@pegah 如果您在我的 github 上提出问题并包含重现问题的代码,那么我会看看。以上是关于如何用networkx绘制社区的主要内容,如果未能解决你的问题,请参考以下文章