PyCirclize: Circular Data Visualization, from R to Python

Shikha.sen . 01 Apr, 2024 • 8 min read

Introduction

Artificial Intelligence revolutionizes various fields such as networking, multi-dimensional data exploration, genomics, and sensor data analysis, increasing the demand for advanced data visualization tools. R, known for its robust visualization capabilities, particularly in circular plots, has played a significant role in this regard. However, Python’s visualization libraries like matplotlib and seaborn, though powerful, lack specific features for circular data visualization. Recognizing this gap, developers created pycirclize, drawing inspiration from R’s circlize and RCircos, offering Python users a specialized tool for intricate circular visualizations.

This library enhances Python’s data analysis toolkit, allowing users to understand complex relationships and patterns better, and present their findings with clarity and impact. The motivation behind pycirclize likely stems from the growing trend of transitioning from R to Python seen in other libraries.

Applications of Circular Data Visualization

Python users now have access to circular visualization, an area historically dominated by R, thanks to pycirclize. This new library enhances the Python visualization landscape, enabling the creation of intricate plots that were previously challenging. pycirclize offers a fresh and insightful approach to visualizing and analyzing data, whether for understanding multi-dimensional datasets, dissecting network traffic nuances, or unraveling genomic sequences.

Circular visualization proves beneficial for Large Language Model (LLM) vector databases, simplifying the representation of high-dimensional vector spaces into a more understandable two-dimensional format. It aids in displaying relationships and groupings within the vector data, facilitating visual exploration of relationships between various vectors, such as words, sentences, or documents.

Leveraging Gen AI for analyzing and interpreting complex datasets, including those visualized circularly, holds promise. AI can identify patterns or clusters within circular visualizations of vector databases or genomic data, enriching understanding and insights drawn from such visuals.

This advancement not only expands Python enthusiasts’ analytical capabilities but also promotes a more inclusive and adaptable visualization environment, bridging the gap between Python and R’s established strengths in data visualization.

Applications of Circular Data Visualization

Where can we use the complex circular data visualisation ?

pycirclize, like circlize in R, can display various data types in circular plots. Here are common data formats:

  • Genomic Data: Often used to compare gene expressions, mutations etc. across samples.
  • Network Data: Effective for visualizing relationships or interactions, especially with chord diagrams. The Stanford Large Network Dataset Collection (SNAP) offers data for creating circular visualizations.
  • Comparative Data: Useful for comparing entities across parameters, like sales or performance metrics.
  • Time Series Data: Can creatively represent patterns over time, especially for seasonal data.
  • Multidimensional Data: Can represent data with multiple measurements across categories. Circular heatmaps can be used to show interactions between variables.

Data Preparation for Visualization

Before utilizing data sources, ensuring proper data cleaning and structuring is crucial. This may involve normalization, handling missing values, or aggregating data points to make visualizations informative and meaningful.

#for pip installation 
pip install pycirclize


#Conda Installation
conda install -c conda-forge pycirclize
#import csv

Similarly like R package, pyCirclize uses a circular layout design with Sectors and Tracks. Each sector can have a different set of data, and each sector can have an unlimited number of tracks for data visualization.

Understanding Sectors and Tracks in pycirclize

Each sector in the `Pycirclize` package represents a division on the circle’s circumference, while “sectors”  in the data refer to various categories or groupings. Within each sector where the data is shown, there are concentric rings called “tracks” that enable the presentation of numerous layers of information. 

The Understanding of Sector and track and later we will use data frame and see how sectors and tracks can be used.

from pycirclize import Circos
# Initialize circos sectors
sectors = {"cat A": 15, "Cat B": 15, "Cat C": 12, "Cat D": 20, "Cat E": 10}
circos = Circos(sectors, space=6) #space =6, the space between each sector
for sector in circos.sectors:
   # Plot sector axis & name text
   sector.axis(fc="pink", ls="dashdot", lw=2, ec="green", alpha=0.5, )
   sector.text(f"Sector: {sector.name}={sector.size}", size=12) #size of the text on figure


fig = circos.plotfig()
#import csv
Understanding Sectors and Tracks in pycirclize

Tracks in the Ciclize Library

Tracks are concentric rings within the circle, each displaying a different aspect or measurement of the data. Sectors provide a categorical breakdown, while tracks offer layers of data detail, allowing a multifaceted view of the dataset in a single cohesive circular plot.

 Set Tracks

You can freely place tracks within the sector radius range (0 – 100).

from pycirclize import Circos


# Initialize circos sectors
sectors = {"cat A": 15, "Cat B": 15, "Cat C": 12, "Cat D": 20, "Cat E": 10}
circos = Circos(sectors, space=5)


for sector in circos.sectors:
   # Plot sector axis & name text
   sector.axis(fc="none", ls="dashed", lw=2, ec="Blue", alpha=0.3)
   sector.text(f"{sector.name}={sector.size}", size=10)
   # Set Track01 (Radius: 80 - 100)
   track1 = sector.add_track((80, 100))
   track1.axis(fc="red", alpha=0.2)
   track1.text(track1.name)
 # as you can see the track radius is kept from 80 to 100 and then 
# next track is kept at 75 so that there is a gap of 5 between the tracks
# we can reduce or increase it, as done in the next tracks
   # Set Track02 (Radius: 50 - 75)
   track2 = sector.add_track((50, 75))
   track2.axis(fc="cyan", alpha=0.2)
   track2.text(track2.name)
   # Set Track03 (Radius: 20 - 45)
   track3 = sector.add_track((20, 48))
   track3.axis(fc="lime", alpha=0.2)
   track3.text(track3.name)
   #Set Track04 (Radius 5 -15)
   track4 = sector.add_track((5, 15))
   track4.axis(fc="yellow", alpha=0.2)
   track4.text(track4.name)
fig = circos.plotfig()
#import csv
Tracks in the Ciclize Library

As You can see, the basic parameters are similar to that of matplotlib and seaborn, for example : linestyle, fc, Alpha and, etc. However you must intialise the object Circos for setting of tracks and sectors. 

Combining Sector and Track Together and other Data Visualization Plots

from pycirclize import Circos
import numpy as np
np.random.seed(0)

sectors = {"A": 10, "B": 15, "C": 12, "D": 20, "E": 15} 
circos = Circos(sectors, space=5) #initilaise 
for sector in circos.sectors:
   # Plot sector name
   sector.text(f"Sector {sector.name}", r=106, size=12) 
   # r = 106 place #the text, if we change the value the texts move 
   # Create x positions & randomized y values for data plotting
   x = np.arange(sector.start, sector.end) + 0.5 
   #sector.start 
   #is 0 and ends at the value of sector defined ,for a it is 10, B is 15 
   y = np.random.randint(0, 100, len(x))
   # Plot line
   line_track = sector.add_track((75, 100), r_pad_ratio=0.1)
   line_track.axis(fc= 'yellow')
   line_track.xticks_by_interval(1)
   line_track.line(x, y)
   # Plot points
   points_track = sector.add_track((45, 70), r_pad_ratio=0.1)
   points_track.axis(fc)
   points_track.scatter(x, y)
   # Plot bar
   bar_track = sector.add_track((15, 40), r_pad_ratio=0.1)
   bar_track.axis()
   bar_track.bar(x, y)

fig = circos.plotfig()
#import csv
Combining Sector and Track Together and other Data Visualization Plots

Circos Class Plots 

Axis, Text, Rect, Line 

from pycirclize import Circos
import math


sectors = {"A": 10, "B": 20, "C": 15}
circos = Circos(sectors, end=270, space=10) #end angle 
circos.axis(fc="lightgrey", ec="red") #ec is edge color
circos.line(r=80, deg_lim=(0, 270), color="red") #line circular 
circos.line(r=60, deg_lim=(90, 360), color="blue", lw=3, ls="dotted")
circos.text("center") #represent text on the chart 
circos.text("right-middle", r=50, deg=90) # r is radius with degree 
circos.text("bottom", r=100, deg=180)
circos.text("left", r=100, deg=270)
circos.text("left-top", r=100 * math.sqrt(2), deg=315)
circos.rect(r_lim=(30, 50), deg_lim=(90, 360), fc="lime", 
ec="grey", lw=2, hatch="//") # rectangular Fill
circos.rect(r_lim=(30, 100), deg_lim=(0, 90), fc="orange", alpha=0.2)
fig = circos.plotfig()
#import csv
Axis, Text, Rect, Line 

Link charts are essential for showing linkages or connections between various data points or entities in circular visualizations, particularly those created with the R packages Circos and Circlize. These are especially helpful in fields such as genomics, networking, and any other area where links between different segments need to be emphasised. In genomics, they can be used to illustrate interactions between genes or genomic areas. Link charts provide an excellent way to express the nature, strength, and frequency of relationships by visually connecting points on a circular plot. This makes the complicated relational data they represent easy to understand.

Let’s say we have a networking data which looks like this.

import pandas as pd


net_df = pd.DataFrame({
   "Connection_from": ["A", "A", "A", "B", "B", "C", "C"],
   "val_1": [0, 1, 9, 5, 18, 1, 11.5],
   "val_2": [0, 2, 10, 7, 16, 3, 14],
   "connection_to": ["A", "A", "B", "C", "B", "B", "A"],
   "val_3": [7, 7, 4, 6, 11, 2, 4],
   "val_4": [8, 6, 3, 6, 13, 0, 3]
})


net_df

The output dataframe looks like 

Link Chart

There are two ways to put up a connection, first lets understand statically and plot the linkage between the connection from and connection to. 

Hard Coded Value 

from pycirclize import Circos


sectors = {"A": 10, "B": 20, "C": 15}
name2color = {"A": "red", "B": "blue", "C": "green"}
circos = Circos(sectors, space=5)
for sector in circos.sectors:
   track = sector.add_track((95, 100))
   track.axis(fc=name2color[sector.name])
   track.text(sector.name, color="black", size=12)
   track.xticks_by_interval(1)


# Plot links
circos.link(("A", 0,0), ("A", 7,8))
circos.link(("A", 1, 2), ("A", 7, 6))
circos.link(("A", 9, 10), ("B", 4, 3))
circos.link(("B", 5, 7), ("C", 6, 6))
circos.link(("B", 18, 16), ("B", 11, 13))
circos.link(("C", 1, 3), ("B", 2, 0))
circos.link(("C", 11.5, 14), ("A", 4, 3))


circos.text("Network")


fig = circos.plotfig()

Dynamically  

result = [((row['Connection_from'], row['val_1'], row['val_2']), 
(row['connection_to'], row['val_3'], row['val_4'])) for index, 
row in net_df.iterrows()]
#print(result)


sectors = {"A": 10, "B": 20, "C": 15}
name2color = {"A": "red", "B": "blue", "C": "green"}
circos = Circos(sectors, space=5)


for sector in circos.sectors:
   track = sector.add_track((95, 100))
   track.axis(fc=name2color[sector.name])
   track.text(sector.name, color="black", size=12)
   track.xticks_by_interval(1)


for link in result:
   circos.link(link[0], link[1])


circos.text("Network")


fig = circos.plotfig()

The output for the both ways is same:

Dynamically  

Rect and text Plot 

This is an exemplar plot which can be used to cluster values or show multidimensional data where rect feature (class) has been used from circos.

from pycirclize import Circos
from pycirclize.utils import ColorCycler
ColorCycler.set_cmap("viridis")


sectors = {"A": 10, "B": 20, "C": 15, "D":10}
circos = Circos(sectors, space=5)
for sector in circos.sectors:
   track1 = sector.add_track((90, 100))
   track1.axis()
   # Plot rect & text (style1)
   for i in range(int(track1.size)):
       start, end = i, i + 1
       track1.rect(start, end, fc=ColorCycler())
       track1.text(str(end), (end + start) / 2)
   # Plot rect & text (style2)
   track2 = sector.add_track((65, 85))
   for i in range(int(track2.size)):
       start, end = i, i + 1
       track2.rect(start, end, fc=ColorCycler(), ec="white", lw=1)
       track2.text(str(end), (end + start) / 2, color="white", orientation="vertical")


  
   # Plot rect & text (style3)
   track3 = sector.add_track((25, 60))
   for i in range(int(track3.size)):
       start, end = i, i + 1
       track3.rect(start, end, fc=ColorCycler(), ec="white", lw=1)
       track3.text(str(end), (end + start) / 2, color="white", orientation="vertical")




circos.text("Gene-324")


fig = circos.plotfig()
Rect and text Plot 

Circos Plot for Genomics 

In genomics, circos plots are strong visualisation tools that let scientists present intricate genomic data in a circle format. These maps are especially helpful for showing connections and contrasts between different genomes or between different areas of the same genome. They allow for a thorough and condensed depiction of big datasets by illuminating a variety of genomic properties, including gene expressions, structural changes, and mutations. In contrast to conventional linear genome maps, this kind of visualisation makes it easier to spot patterns and correlations.

Here is an example :

from pycirclize.utils import fetch_genbank_by_accid
from pycirclize.parser import Genbank


# Download `NC_002483` E.coli plasmid genbank
gbk_fetch_data = fetch_genbank_by_accid("NC_002483")
gbk = Genbank(gbk_fetch_data)


print("Genome Length:",gbk.full_genome_length ,
 "\n___________________________________________________________\n")
print("genome sequence :",gbk.genome_seq, 
"\n___________________________________________________________\n")


print("Records :\n")


for i in gbk.records :
 print(i)
Circos Plot for Genomics 

Plotting Genomic Features



# Initialize Circos instance with genome size
circos = Circos(sectors={gbk.name: gbk.range_size})
circos.text(f"Escherichia coli K-12 plasmid F\n\n{gbk.name}", size=14)
circos.rect(r_lim=(90, 100), fc="lightgrey", ec="none", alpha=0.5)
sector = circos.sectors[0]


# Plot forward strand CDS
f_cds_track = sector.add_track((95, 100))
f_cds_feats = gbk.extract_features("CDS", target_strand=1)
f_cds_track.genomic_features(f_cds_feats, plotstyle="arrow", fc="salmon", lw=0.5)


# Plot reverse strand CDS
r_cds_track = sector.add_track((90, 95))
r_cds_feats = gbk.extract_features("CDS", target_strand=-1)
r_cds_track.genomic_features(r_cds_feats, plotstyle="arrow", fc="skyblue", lw=0.5)


# Plot 'gene' qualifier label if exists
labels, label_pos_list = [], []
for feat in gbk.extract_features("CDS"):
   start = int(str(feat.location.start))
   end = int(str(feat.location.end))
   label_pos = (start + end) / 2
   gene_name = feat.qualifiers.get("gene", [None])[0]
   if gene_name is not None:
       labels.append(gene_name)
       label_pos_list.append(label_pos)
f_cds_track.xticks(label_pos_list, labels, label_size=6, label_orientation="vertical")


# Plot xticks (interval = 10 Kb)
r_cds_track.xticks_by_interval(
   10000, outer=False, label_formatter=lambda v: f"{v/1000:.1f} Kb"
)


fig = circos.plotfig()
Plotting Genomic Features

Conclusion

In conclusion, Pycirclize enhances Python’s visualization landscape, enabling insightful data exploration across various domains. Its flexibility and power make it a valuable tool for analysts and researchers alike. Embrace pycirclize to unlock new dimensions in your data visualization journey.

You can also visit here for more genomic data plotting.

Shikha.sen . 01 Apr 2024

Frequently Asked Questions

Lorem ipsum dolor sit amet, consectetur adipiscing elit,

Responses From Readers

Clear

Related Courses