plot different graphs with plot_network_clusters
Hi,
To plot the communities it is very easy with plot_network_clusters, however it is tedious to plot each network in a cell (for instance using Jupiter or google Colab). In networkx you can do since there is a hyperparameter ax on the function draw network. I checked the code here and I see that the function is based on Networkx plotting implementation, could be useful to just add the ax parameters allowing the possible to plot multiple plots in one figure.
best,
Salvatore
Thanks for submitting your first issue!
it may be useful, I did a simple test, just copying the function and adding ax in the nx draw functions
import warnings
import numpy as np
import matplotlib
import matplotlib.pyplot as plt
import networkx as nx
from cdlib import NodeClustering
from cdlib.utils import convert_graph_formats
from community import community_louvain
def __filter(partition: list, top_k: int, min_size: int) -> list:
if isinstance(min_size, int) and min_size > 0:
partition = list(filter(lambda nodes: len(nodes) >= min_size, partition))
if isinstance(top_k, int) and top_k > 0:
partition = partition[:top_k]
return partition
# [r, b, g, c, m, y, k, 0.8, 0.2, 0.6, 0.4, 0.7, 0.3, 0.9, 0.1, 0.5]
COLOR = (
(1, 0, 0),
(0, 0, 1),
(0, 0.5, 0),
(0, 0.75, 0.75),
(0.75, 0, 0.75),
(0.75, 0.75, 0),
(0, 0, 0),
(0.8, 0.8, 0.8),
(0.2, 0.2, 0.2),
(0.6, 0.6, 0.6),
(0.4, 0.4, 0.4),
(0.7, 0.7, 0.7),
(0.3, 0.3, 0.3),
(0.9, 0.9, 0.9),
(0.1, 0.1, 0.1),
(0.5, 0.5, 0.5),
)
def plot_network_clusters(
graph: object,
partition: NodeClustering,
position: dict = None,
figsize: tuple = (8, 8),
node_size: int = 200,
plot_overlaps: bool = False,
plot_labels: bool = False,
cmap: object = None,
top_k: int = None,
min_size: int = None,
ax = None
) -> object:
"""
Plot a graph with node color coding for communities.
:param graph: NetworkX/igraph graph
:param partition: NodeClustering object
:param position: A dictionary with nodes as keys and positions as values. Example: networkx.fruchterman_reingold_layout(G). By default, uses nx.spring_layout(g)
:param figsize: the figure size; it is a pair of float, default (8, 8)
:param node_size: int, default 200
:param plot_overlaps: bool, default False. Flag to control if multiple algorithms memberships are plotted.
:param plot_labels: bool, default False. Flag to control if node labels are plotted.
:param cmap: str or Matplotlib colormap, Colormap(Matplotlib colormap) for mapping intensities of nodes. If set to None, original colormap is used.
:param top_k: int, Show the top K influential communities. If set to zero or negative value indicates all.
:param min_size: int, Exclude communities below the specified minimum size.
Example:
>>> from cdlib import algorithms, viz
>>> import networkx as nx
>>> g = nx.karate_club_graph()
>>> coms = algorithms.louvain(g)
>>> pos = nx.spring_layout(g)
>>> viz.plot_network_clusters(g, coms, pos)
"""
if not isinstance(cmap, (type(None), str, matplotlib.colors.Colormap)):
raise TypeError(
"The 'cmap' argument must be NoneType, str or matplotlib.colors.Colormap, "
"not %s." % (type(cmap).__name__)
)
partition = __filter(partition.communities, top_k, min_size)
graph = convert_graph_formats(graph, nx.Graph)
if position is None:
position = nx.spring_layout(graph)
n_communities = len(partition)
if n_communities == 0:
warnings.warn("There are no communities that match the filter criteria.")
return None
if cmap is None:
n_communities = min(n_communities, len(COLOR))
cmap = matplotlib.colors.ListedColormap(COLOR[:n_communities])
else:
cmap = plt.cm.get_cmap(cmap, n_communities)
_norm = matplotlib.colors.Normalize(vmin=0, vmax=n_communities - 1)
fontcolors = list(
map(
lambda rgb: ".15" if np.dot(rgb, [0.2126, 0.7152, 0.0722]) > 0.408 else "w",
[cmap(_norm(i))[:3] for i in range(n_communities)],
)
)
plt.figure(figsize=figsize)
plt.axis("off")
filtered_nodelist = list(np.concatenate(partition))
filtered_edgelist = list(
filter(
lambda edge: len(np.intersect1d(edge, filtered_nodelist)) == 2,
graph.edges(),
)
)
fig = nx.draw_networkx_nodes(
graph, position, node_size=node_size, node_color="w", nodelist=filtered_nodelist, ax = ax
)
fig.set_edgecolor("k")
nx.draw_networkx_edges(graph, position, alpha=0.5, edgelist=filtered_edgelist, ax = ax)
if plot_labels:
nx.draw_networkx_labels(
graph,
position,
font_color=".8",
ax = ax,
labels={node: str(node) for node in filtered_nodelist},
)
for i in range(n_communities):
if len(partition[i]) > 0:
if plot_overlaps:
size = (n_communities - i) * node_size
else:
size = node_size
fig = nx.draw_networkx_nodes(
graph,
position,
node_size=size,
ax = ax,
nodelist=partition[i],
node_color=[cmap(_norm(i))],
)
fig.set_edgecolor("k")
if plot_labels:
for i in range(n_communities):
if len(partition[i]) > 0:
nx.draw_networkx_labels(
graph,
position,
font_color=fontcolors[i],
ax = ax,
labels={node: str(node) for node in partition[i]},
)
return fig
then just:
plt.rcParams['axes.grid'] = False
plt.rcParams['axes.spines.left'] = False
plt.rcParams['axes.spines.right'] = False
plt.rcParams['axes.spines.top'] = False
plt.rcParams['axes.spines.bottom'] = False
fig, ((ax, ax1), (ax2, ax3)) = plt.subplots(2, 2, figsize=(15,15))
G= nx.from_pandas_edgelist(cora, "id1", "id2")
coms = algorithms.louvain(G, weight='weight', resolution=1., randomize=False)
pos = nx.spring_layout(G)
plot_network_clusters(G, coms, pos, ax = ax1)
this work, even if it is not optimal
Thank you, we are thinking of improvements for the plotting function and indeed what you propose is relevant. I'll try to take care of it when I have some free time, unless you're confident in the change in which case you can directly propose a pull request of course.