Search code examples
pythondata-miningnetworkxgraph-visualization

How to draw a small graph with community structure in networkx


The graph has around 100 nodes, and the number of communities ranges from 5 to 20. Is there any way to draw the graph such that the nodes of the same community are close to each other?

I've tried to assign different communities different colors, but that does not work well in my application.

I've read this and this but did not find a good solution.

I'm using python 2.7.12 and newtorkx-1.11


Solution

  • For small graph, I find the spring_layout pretty OK for drawing communities. If you need to highlight nodes (and their communities) I recommend you:

    • choose different colors for different communities (the more the colors differ visually, the better),

    • increase the size of nodes and

    • make the edges a lighter gray (this way the graph looks less cluttered and the nodes are more highlighted visually).

    If you choose spring_layout you can additionally play around with the k argument (the documentation states: Increase this value to move nodes farther apart). Note that spring_layout can give different images for each time you run the code (this way you can run the code multiple times and save the image only when you're satisfied with the result).

    In the following example I use a default graph (nx.karate_club_graph) in which I detect the communities automatically with the python-louvain package (imported as community). Node sized are defined with the node_size argument in nx.draw_networkx_nodes. The node colors depend on the community to which they belong -- I use the plt.cm.RdYlBu color map (see more color maps here). Note that you can also affect node sizes (and edges lengths) by defining a bigger or smaller image with figsize in plt.figure.

    import networkx as nx
    import community
    import matplotlib.pyplot as plt
    
    G = nx.karate_club_graph()  # load a default graph
    
    partition = community.best_partition(G)  # compute communities
    
    pos = nx.spring_layout(G)  # compute graph layout
    plt.figure(figsize=(8, 8))  # image is 8 x 8 inches
    plt.axis('off')
    nx.draw_networkx_nodes(G, pos, node_size=600, cmap=plt.cm.RdYlBu, node_color=list(partition.values()))
    nx.draw_networkx_edges(G, pos, alpha=0.3)
    plt.show(G)
    

    Output (I ran the code multiple times and choosed the "prettiest" image):

    enter image description here

    But what if you have a bigger graph with less obvious communities? Here is a more complicated graph with 100 nodes and 100 random edges (and thus random communities) but with the same drawing approach:

    import networkx as nx
    import community
    import matplotlib.pyplot as plt
    import random
    
    H = nx.Graph()
    
    nodes = list(range(100))  # 100 nodes
    
    # add 100 random edges
    for i in range(100):
        src = random.choice(nodes)
        dest = random.choice(nodes)
        # we don't want src to be the same as dest
        while src == dest:
            dest = random.choice(nodes)
    
        H.add_edge(src, dest)
    
    partition = community.best_partition(H)  # compute communities
    
    pos = nx.spring_layout(H)  # compute graph layout
    plt.figure(figsize=(10, 10))
    plt.axis('off')
    nx.draw_networkx_nodes(H, pos, node_size=600, cmap=plt.cm.RdYlBu, node_color=list(partition.values()))
    nx.draw_networkx_edges(H, pos, alpha=0.3)
    plt.show(H)
    

    Output:

    enter image description here

    We see no clear communities in the image above. Here you have at least three choices:

    • define the graph layout (nodes coordinates/positions) manually (pos in my code),

    • experiment with different layouts (found here) and

    • have an image for each community (or at least the most important communities).

    If you choose the third option, you can have the nodes of one highlighted community bigger than other nodes (and of different color of course). You could also change the color and thickness of edges in that community (not shown in the example below).

    node_size = []
    
    # first community against the others
    for node, community in partition.items():
        if community == 1:
            node_size.append(900)
        else:
            partition[node] = 0  # I put all the other communities in one communitiy
            node_size.append(300)
    
    plt.figure(figsize=(10, 10))
    plt.axis('off')
    nodes = nx.draw_networkx_nodes(H, pos, node_size=node_size, cmap=plt.cm.winter, node_color=list(partition.values()))
    nx.draw_networkx_edges(H, pos, alpha=0.3)
    plt.show(H)
    

    Output (only the first community highlighted):

    enter image description here

    If you have multiple images for the same graph, I recommend that the nodes have the same positions in all of them (you'll need to have the same pos between drawings). That way the images are more comparable.