NetworkX基于节点颜色以圆形形式群集节点

2024-04-18 05:03:23 发布

您现在位置:Python中文网/ 问答频道 /正文

我有同样的问题。但是,解决方案是可行的,我似乎无法用数据集隔开节点并使它们以循环格式显示。我总共有大约30个节点是彩色编码的

相同颜色的节点重叠,而不是以圆形格式聚集/更同心

我使用了上面问题中的代码,并尝试了所有可能的半径值,但似乎不能使相同颜色的节点聚集在一个圆中

代码:

import networkx
import numpy as np
import matplotlib.pyplot as plt

nodesWithGroup = {'A':'#7a8eff', 'B': '#7a8eff', 'C': '#eb2c30', 'D':'#eb2c30', 'E': '#eb2c30', 'F':'#730a15', 'G': '#730a15'}
# Set up graph, adding nodes and edges
G = nx.Graph()
G.add_nodes_from(nodesWithGroup.keys())

# Create a dictionary mapping color to a list of nodes
nodes_by_color = {}
for k, v in nodesWithGroup.items():
    if v not in nodes_by_color:
        nodes_by_color[v] = [k]
    else:
        nodes_by_color[v].append(k)

# Create initial circular layout
pos = nx.circular_layout(RRR)

# Get list of colors
colors2 = list(nodes_by_color.keys())
# clustering
angs = np.linspace(0, 2*np.pi, 1+len(colors))
repos = []
rad = 13
for ea in angs:
    if ea > 0:
        repos.append(np.array([rad*np.cos(ea), rad*np.sin(ea)]))

for color, nodes in nodes_by_color.items():
    posx = colors.index(color)
    for node in nodes:
        pos[node] += repos[posx]


# Plot graph
fig,ax = plt.subplots(figsize=(5, 5))


# node colors
teamX = ['A', 'B']
teamY = ['C', 'D', 'E']
teamZ = ['F', 'G']

for n in G.nodes():
    if n in teamX:
        G.nodes[n]['color'] = '#7a8eff'
    elif n in teamY:
        G.nodes[n]['color'] = '#eb2c30'
    else:
        G.nodes[n]['color'] = '#730a15'


colors = [node[1]['color'] for node in G.nodes(data=True)]

# edges
zorder_edges = 3
zorder_nodes = 4
zorder_node_labels = 5


for edge in G.edges():
    source, target = edge
    rad = 0.15
    node_color_dict = dict(G.nodes(data='color'))
    if node_color_dict[source] == node_color_dict[target]:
        arrowprops=dict(lw=G.edges[(source,target)]['weight'],
                        arrowstyle="-",
                        color='blue',
                        connectionstyle=f"arc3,rad={rad}",
                        linestyle= '-',
                        alpha=0.65, zorder=zorder_edges)
        ax.annotate("",
                    xy=pos[source],
                    xytext=pos[target],
                    arrowprops=arrowprops
                   )
    else:
        arrowprops=dict(lw=G.edges[(source,target)]['weight'],
                        arrowstyle="-",
                        color='purple',
                        connectionstyle=f"arc3,rad={rad}",
                        linestyle= '-',
                        alpha=0.65,  zorder=zorder_edges)
        ax.annotate("",
                    xy=pos[source],
                    xytext=pos[target],
                    arrowprops=arrowprops
                   )

# drawing 
node_labels_dict = nx.draw_networkx_labels(G, pos, font_size=5, font_family="monospace", font_color='white', font_weight='bold')


for color, nodes in nodes_by_color.items():
    nodes_draw = nx.draw_networkx_nodes(G, pos=pos, nodelist=nodes, node_color=color, edgecolors=[(0,0,0,1)])
    nodes_draw.set_zorder(zorder_nodes)
    for node_labels_draw in node_labels_dict.values():
        node_labels_draw.set_zorder(zorder_node_labels)




plt.show()

我得到以下输出: enter image description here

期望输出(如在解决方案中): enter image description here


Tags: inposnodesourcetargetforbylabels
1条回答
网友
1楼 · 发布于 2024-04-18 05:03:23

正如@willcrack所建议的,稍微调整this answer效果很好

可以通过更改partition_layout中的ratio参数来调整节点重叠

enter image description here

#!/usr/bin/env python

import numpy as np
import matplotlib.pyplot as plt
import networkx as nx


NODE_LAYOUT = nx.circular_layout
COMMUNITY_LAYOUT = nx.circular_layout


def partition_layout(g, partition, ratio=0.3):
    """
    Compute the layout for a modular graph.

    Arguments:
         
    g   networkx.Graph or networkx.DiGraph instance
        network to plot

    partition   dict mapping node -> community or None
        Network partition, i.e. a mapping from node ID to a group ID.

    ratio: 0 < float < 1.
        Controls how tightly the nodes are clustered around their partition centroid.
        If 0, all nodes of a partition are at the centroid position.
        if 1, nodes are positioned independently of their partition centroid.

    Returns:
        
    pos   dict mapping int node -> (float x, float y)
        node positions

    """

    pos_communities = _position_communities(g, partition)

    pos_nodes = _position_nodes(g, partition)
    pos_nodes = {k : ratio * v for k, v in pos_nodes.items()}

    # 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 = COMMUNITY_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():
        if community in communities:
            communities[community] += [node]
        else:
            communities[community] = [node]

    pos = dict()
    for community, nodes in communities.items():
        subgraph = g.subgraph(nodes)
        pos_subgraph = NODE_LAYOUT(subgraph, **kwargs)
        pos.update(pos_subgraph)

    return pos


def _layout(networkx_graph):
    edge_list = [edge for edge in networkx_graph.edges]
    node_list = [node for node in networkx_graph.nodes]

    pos = circular_layout(edge_list)

    # NB: some nodes might not be connected and hence will not be in the edge list.
    # Assuming a [0, 0, 1, 1] canvas, we assign random positions on the periphery
    # of the existing node positions.
    # We define the periphery as the region outside the circle that covers all
    # existing node positions.
    xy = list(pos.values())
    centroid = np.mean(xy, axis=0)
    delta = xy - centroid[np.newaxis, :]
    distance = np.sqrt(np.sum(delta**2, axis=1))
    radius = np.max(distance)

    connected_nodes = set(_flatten(edge_list))
    for node in node_list:
        if not (node in connected_nodes):
            pos[node] = _get_random_point_on_a_circle(centroid, radius)

    return pos


def _flatten(nested_list):
    return [item for sublist in nested_list for item in sublist]


def _get_random_point_on_a_circle(origin, radius):
    x0, y0 = origin
    random_angle = 2 * np.pi * np.random.random()
    x = x0 + radius * np.cos(random_angle)
    y = y0 + radius * np.sin(random_angle)
    return np.array([x, y])


def test():

    # create test data
    cliques = 8
    clique_size = 7
    g = nx.connected_caveman_graph(cliques, clique_size)
    partition = {ii : np.int(ii/clique_size) for ii in range(cliques * clique_size)}

    pos = partition_layout(g, partition, ratio=0.2)
    nx.draw(g, pos, node_color=list(partition.values()))
    plt.show()

def test2():

    # create test data
    cliques = 8
    clique_size = 7
    g = nx.connected_caveman_graph(cliques, clique_size)
    partition = {ii : np.int(ii/clique_size) for ii in range(cliques * clique_size)}

    # add additional between-clique edges
    total_nodes = cliques*clique_size
    for ii in range(cliques):
        start = ii*clique_size + int(clique_size/2)
        stop = (ii+cliques/2)*clique_size % total_nodes + int(clique_size/2)
        g.add_edge(start, stop)

    pos = partition_layout(g, partition, ratio=0.2)
    nx.draw(g, pos, node_color=list(partition.values()))
    plt.show()


if __name__ == '__main__':
    test()
    test2()

附录

注释中要求的具有额外簇间边缘的示例:

enter image description here

相关问题 更多 >