Skip to content
import marimo as mo

Annotating Circos Plot Node Labels

This example set was contributed by Alireza Hosseini.

from random import randint
import matplotlib.pyplot as plt
import networkx as nx
import nxviz as nv
from nxviz import annotate
G = nx.erdos_renyi_graph(n=30, p=0.1)
for _n, _d in G.nodes(data=True):
    G.nodes[_n]['group'] = randint(0, 5)
G = nx.relabel_nodes(G, {i: 'long name #' + str(i) for i in range(len(G))})
nv.circos(G, group_by='group', node_color_by='group')
annotate.circos_labels(G, group_by='group', layout='rotate')
plt.tight_layout(rect=(0.05, 0.05, 0.95, 0.95))
# The rotated labels take up more space, so we will have to increase the
# padding a bit. 5% on all sides works well here.
plt.show()
G_1 = nx.erdos_renyi_graph(n=30, p=0.1)
for _n, _d in G_1.nodes(data=True):
    G_1.nodes[_n]['group'] = randint(0, 5)
G_1 = nx.relabel_nodes(G_1, {i: 'long name #' + str(i) for i in range(len(G_1))})
nv.circos(G_1, group_by='group', node_color_by='group')
annotate.circos_labels(G_1, group_by='group', layout='numbers')
plt.tight_layout(rect=(0.15, 0.15, 0.85, 0.85))
plt.show()

Custom color mapping circos nodes and edges

This example shows how to customize the color mapping of nodes and edges in a Circos plot.

The example is contributed by Kelvin Tuong.

from itertools import cycle
categories = ['sun', 'moon', 'stars', 'cloud', 'wheel', 'box', 'plant', 'chair', 'slippers', 'tablet', 'laptop', 'dishwasher', 'bicycle', 'piano', 'laptop']
palette = ['#1f77b4', '#ff7f0e', '#279e68', '#d62728', '#aa40fc', '#8c564b', '#e377c2', '#b5bd61', '#17becf', '#aec7e8', '#ffbb78', '#98df8a', '#ff9896', '#c5b0d5', '#c49c94', '#f7b6d2', '#dbdb8d', '#9edae5', '#ad494a', '#8c6d31']
categorical = cycle(categories[0:4])
categories[0:4]
many_categorical = cycle(categories)
_n = 71
p = 0.01
G_2 = nx.erdos_renyi_graph(n=_n, p=p)
legend_kwargs = {'ncol': 1, 'bbox_to_anchor': (1, 0.5), 'frameon': False, 'loc': 'center left'}
for _n in G_2.nodes():
    G_2.nodes[_n]['group1'] = next(categorical)
    G_2.nodes[_n]['group2'] = next(many_categorical)
for u, v in G_2.edges():
    G_2.edges[u, v]['edge_group1'] = next(categorical)
    G_2.edges[u, v]['edge_group2'] = next(many_categorical)
    G_2.edges[u, v]['thickness'] = 3

Current default behavior

nv.circos(G_2, group_by='group1', node_color_by='group1')
annotate.node_colormapping(G_2, color_by='group1', legend_kwargs=legend_kwargs)

Now we can manusally specify the node colors:

nv.circos(G_2, group_by='group1', node_color_by='group1', node_palette=palette[:4])  # specify 4 colors for 4 groups

now with more than 12 categories (14), and a long color palette (20 colors)

nv.circos(G_2, group_by='group2', node_color_by='group2', node_palette=palette)

same as above but limit to 7 colors - colors start to cycle if palette is provided as a list.

nv.circos(G_2, group_by='group2', node_color_by='group2', node_palette=palette[:7])

palette provides as a dictionary

pal = {'moon': 'red', 'stars': 'yellow', 'sun': 'black', 'cloud': 'blue'}
nv.circos(G_2, group_by='group1', node_color_by='group1', node_palette=pal)
annotate.node_colormapping(G_2, color_by='group1', palette=pal)

order of keys don't matter

pal_1 = {'moon': 'red', 'cloud': 'pink', 'stars': 'yellow', 'sun': 'black'}
nv.circos(G_2, group_by='group1', node_color_by='group1', node_palette=pal_1)
annotate.node_colormapping(G_2, color_by='group1', palette=pal_1)

can mix colors/hex codes

pal_2 = ['pink', '#1f77B4', 'green', '#ff7f0e']
nv.circos(G_2, group_by='group1', node_color_by='group1', node_palette=pal_2)
annotate.node_colormapping(G_2, color_by='group1', palette=pal_2)

swapping of order of colors in a list matters. But the plot should reflect this correctly - if you look up at the dictionary examples, the same order is preserved.

pal_3 = ['pink', '#1f77B4', '#ff7f0e', 'green']  # swapped the order of the last two colours
nv.circos(G_2, group_by='group1', node_color_by='group1', node_palette=pal_3)
annotate.node_colormapping(G_2, color_by='group1', palette=pal_3)

Can be used on edges as well:

ax = nv.circos(G_2, group_by='group1', node_color_by='group1', edge_color_by='edge_group1', node_palette=pal_3, edge_palette=palette, edge_lw_by='thickness')
annotate.edge_colormapping(G_2, color_by='edge_group1', palette=palette)  # not quite sure how to make both node and edge legend appear