mimi
mimi

Reputation: 71

How to visualize communities from a list in igraph python

I have a community list as the following list_community. How do I edit the code below to make the community visible?

from igraph import *

list_community = [['A', 'B', 'C', 'D'],['E','F','G'],['G', 'H','I','J']]
list_nodes = ['A', 'B', 'C', 'D','E','F','G','H','I','J']
tuple_edges = [('A','B'),('A','C'),('A','D'),('B','C'),('B','D'), ('C','D'),('C','E'),
              ('E','F'),('E','G'),('F','G'),('G','H'),
              ('G','I'), ('G','J'),('H','I'),('H','J'),('I','J'),]

# Make a graph
g_test = Graph()
g_test.add_vertices(list_nodes)
g_test.add_edges(tuple_edges)

# Plot
layout = g_test.layout("kk")
g.vs["name"] = list_nodes
visual_style = {}
visual_style["vertex_label"] = g.vs["name"]
visual_style["layout"] = layout
ig.plot(g_test, **visual_style)

enter image description here

I would like a plot that visualizes the community as shown below.

enter image description here

I can also do this by using a module other than igraph. Thank you.

Upvotes: 2

Views: 546

Answers (2)

Vincent Traag
Vincent Traag

Reputation: 728

In igraph you can use the VertexCover to draw polygons around clusters (as also suggested by Szabolcs in his comment). You have to supply the option mark_groups when plotting the cover, possibly with some additional palette if you want. See some more detail in the documentation here.

In order to construct the VertexCover, you first have to make sure you get integer indices for each node in the graph you created. You can do that using g_test.vs.find.

clusters = [[g_test.vs.find(name=v).index for v in cl] for cl in list_community]
cover = ig.VertexCover(g_test, clusters)

After that, you can simply draw the cover like

ig.plot(cover,
        mark_groups=True,
        palette=ig.RainbowPalette(3))

resulting in the following picture

enter image description here

Upvotes: 5

Frodnar
Frodnar

Reputation: 2242

Here is a script that somewhat achieves what you're looking for. I had to handle the cases of single-, and two-nodes communities separately, but for greater than two nodes this draws a polygon within the nodes.

I had some trouble with matplotlib not accounting for overlapping edges and faces of polygons which meant the choice was between (1) not having the polygon surround the nodes or (2) having an extra outline just inside the edge of the polygon due to matplotlib overlapping the widened edge with the fill of the polygon. I left a comment on how to change the code from option (2) to option (1).

I also blatantly borrowed a convenience function from this post to handle correctly sorting the nodes in the polygon for appropriate filling by matplotlib's plt.fill().

Option 1:

option 1

Option 2:

option 2

Full code:

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

def sort_xy(x, y):

    x0 = np.mean(x)
    y0 = np.mean(y)

    r = np.sqrt((x-x0)**2 + (y-y0)**2)

    angles = np.where((y-y0) > 0, np.arccos((x-x0)/r), 2*np.pi-np.arccos((x-x0)/r))

    mask = np.argsort(angles)

    x_sorted = x[mask]
    y_sorted = y[mask]

    return x_sorted, y_sorted

G = nx.karate_club_graph()

pos = nx.spring_layout(G, seed=42)
fig, ax = plt.subplots(figsize=(8, 10))
nx.draw(G, pos=pos, with_labels=True)

communities = nx.community.louvain_communities(G)

alpha = 0.5
edge_padding = 10
colors = cm.get_cmap('viridis', len(communities))

for i, comm in enumerate(communities):

    if len(comm) == 1:
        cir = plt.Circle((pos[comm.pop()]), edge_padding / 100, alpha=alpha, color=colors(i))
        ax.add_patch(cir)

    elif len(comm) == 2:
        comm_pos = {k: pos[k] for k in comm}
        coords = [a for a in zip(*comm_pos.values())]
        x, y = coords[0], coords[1]
        plt.plot(x, y, linewidth=edge_padding, linestyle="-",  alpha=alpha, color=colors(i))

    else:
        comm_pos = {k: pos[k] for k in comm}
        coords = [a for a in zip(*comm_pos.values())]
        x, y = sort_xy(np.array(coords[0]), np.array(coords[1]))
        plt.fill(x, y, alpha=alpha, facecolor=colors(i), 
                 edgecolor=colors(i), # set to None to remove edge padding
                 linewidth=edge_padding)

Upvotes: 1

Related Questions