如何用networkx绘制社区 [英] how to draw communities with networkx

查看:166
本文介绍了如何用networkx绘制社区的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我如何使用 python networkx 绘制它的社区图,如下图:

将 numpy 导入为 np导入 matplotlib.pyplot 作为 plt将 networkx 导入为 nxdef community_layout(g,分区):"计算模块化图的布局.参数:----------g -- networkx.Graph 或 networkx.DiGraph 实例要绘制的图形分区--字典映射int节点->内部社区图分区返回:--------pos -- dict 映射 int 节点 ->(浮动x,浮动y)节点位置"pos_communities = _position_communities(g, partition, scale=3.)pos_nodes = _position_nodes(g, partition, scale=1.)# 合并位置pos = dict()对于 g.nodes() 中的节点:pos[节点] = pos_communities[节点] + pos_nodes[节点]返回位置def _position_communities(g, partition, **kwargs):# 创建一个加权图,其中每个节点对应一个社区,# 和每条边权重到社区之间的边数between_community_edges = _find_between_community_edges(g,分区)社区 = 设置(partition.values())超图 = nx.DiGraph()hypergraph.add_nodes_from(社区)对于 (ci, cj), between_community_edges.items() 中的边:hypergraph.add_edge(ci, cj, weight=len(edges))# 找到社区的布局pos_communities = nx.spring_layout(超图,**kwargs)# 将节点位置设置为社区位置pos = dict()对于节点,partition.items() 中的社区:pos[节点] = pos_communities[社区]返回位置def _find_between_community_edges(g, 分区):边缘 = dict()对于 g.edges() 中的 (ni, nj):ci = 分区[ni]cj = 分区[nj]如果 ci != cj:尝试:边[(ci, cj)] += [(ni, nj)]除了 KeyError:边[(ci, cj)] = [(ni, nj)]返回边def _position_nodes(g, partition, **kwargs):"在社区内定位节点."社区 = dict()对于节点,partition.items() 中的社区:尝试:社区[社区] += [节点]除了 KeyError:社区[社区] = [节点]pos = dict()对于 ci,communities.items() 中的节点:subgraph = g.subgraph(节点)pos_subgraph = nx.spring_layout(子图,**kwargs)pos.update(pos_subgraph)返回位置定义测试():# 安装 networkx 2.0 兼容版本的 python-louvain 使用:# pip install -U git+https://github.com/taynaud/python-louvain.git@networkx2从社区导入 community_louvaing = nx.karate_club_graph()分区 = community_louvain.best_partition(g)pos = community_layout(g,分区)nx.draw(g, pos, node_color=list(partition.values()));plt.show()返回

附录

虽然总体思路是合理的,但我上面的旧实现有一些问题.最重要的是,对于规模不均匀的社区,实施效果不佳.具体来说,_position_communities 在画布上为每个社区提供相同数量的不动产.如果某些社区比其他社区大得多,这些社区最终会被压缩成与小社区相同的空间.显然,这并不能很好地反映图的结构.

我编写了一个用于可视化网络的库,名为

将 matplotlib.pyplot 导入为 plt将 networkx 导入为 nx# 通过 pip 安装最简单:# pip 安装网络图从网络图导入图# 创建一个模块化图分区大小 = [10, 20, 30, 40]g = nx.random_partition_graph(partition_sizes, 0.5, 0.1)# 因为我们创建了图,所以我们知道最好的分区:node_to_community = dict()节点 = 0对于 community_id,枚举中的大小(partition_sizes):对于 _ 范围(大小):node_to_community[节点] = community_id节点 += 1# # 或者,我们可以使用 Louvain 推断最佳分区:# 从社区导入 community_louvain# node_to_community = community_louvain.best_partition(g)community_to_color = {0 : '标签:蓝色',1 : '标签:橙色',2 : '标签:绿色',3 : '标签:红色',}node_color = {node: community_to_color[community_id] 用于节点,community_id in node_to_community.items()}图(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()

How can I draw a graph with it's communities using python networkx like this image :

image url

解决方案

The documentation for networkx.draw_networkx_nodes and networkx.draw_networkx_edges explains how to set the node and edge colors. The patches bounding the communities can be made by finding the positions of the nodes for each community and then drawing a patch (e.g. matplotlib.patches.Circle) that contains all positions (and then some).

The hard bit is the graph layout / setting the node positions. AFAIK, there is no routine in networkx to achieve the desired graph layout "out of the box". What you want to do is the following:

  1. Position the communities with respect to each other: create a new, weighted graph, where each node corresponds to a community, and the weights correspond to the number of edges between communities. Get a decent layout with your favourite graph layout algorithm (e.g.spring_layout).

  2. Position the nodes within each community: for each community, create a new graph. Find a layout for the subgraph.

  3. Combine node positions in 1) and 3). E.g. scale community positions calculated in 1) by a factor of 10; add those values to the positions of all nodes (as computed in 2)) within that community.

I have been wanting to implement this for a while. I might do it later today or over the weekend.

EDIT:

Voila. Now you just need to draw your favourite patch around (behind) the nodes.

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

Addendum

Although the general idea is sound, my old implementation above has a few issues. Most importantly, the implementation doesn't work very well for unevenly sized communities. Specifically, _position_communities gives each community the same amount of real estate on the canvas. If some of the communities are much larger than others, these communities end up being compressed into the same amount of space as the small communities. Obviously, this does not reflect the structure of the graph very well.

I have written a library for visualizing networks, which is called netgraph. It includes an improved version of the community layout routine outlined above, which also considers the sizes of the communities when arranging them. It is fully compatible with networkx and igraph Graph objects, so it should be easy and fast to make great looking graphs (at least that is the idea).

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()

这篇关于如何用networkx绘制社区的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆