Skip to main content

Circular visualization in Python, plotly implemented

Project description

pyCirclizely: Circular visualization in Python, plotly implemented

Python3 OS License CI

Table of contents

Overview

pyCirclizely is a circular visualization python package refactored from pyCirclize to use plotly instead of matplotlib, which in turn was inspired by circlize and pyCircos.

It includes useful genome and phylogenetic tree visualization methods for the bioinformatics field. More detailed documentation is available here.

pyCirclizely_gallery.png
Fig.1 pyCirclizely example plot gallery

Installation

Python 3.10 or later is required for installation.

Install PyPI package:

pip install pycirclizely

Install conda-forge package:

conda install -c conda-forge pycirclizely

API Usage

API usage is described in each of the following sections in the document.

Code Example

1. Circos Plot

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

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

for sector in circos.sectors:
    # Plot sector name
    sector.text(f"Sector: {sector.name}", r=110, font=dict(size=15))
    # Create x positions & random y values
    x = np.arange(sector.start, sector.end) + 0.5
    y = np.random.randint(0, 100, len(x))
    # Plot lines
    track1 = sector.add_track((80, 100), r_pad_ratio=0.1)
    track1.xticks_by_interval(interval=1)
    track1.axis()
    track1.line(x, y)
    # Plot points 
    track2 = sector.add_track((55, 75), r_pad_ratio=0.1)
    track2.axis()
    track2.scatter(x, y)
    # Plot bars
    track3 = sector.add_track((30, 50), r_pad_ratio=0.1)
    track3.axis()
    track3.bar(x, y)

# Plot links 
circos.link(("A", 0, 3), ("B", 15, 12))
circos.link(("B", 0, 3), ("C", 7, 11), fillcolor="skyblue")
circos.link(("C", 2, 5), ("E", 15, 12), fillcolor="chocolate", direction=1)
circos.link(
    ("D", 3, 5), ("D", 18, 15), fillcolor="lime", 
    line=dict(color="black", width=0.5), direction=2,
)
circos.link(
    ("D", 8, 10), ("E", 2, 8), fillcolor="violet",
    line=dict(color="red", width=1.0, dash="dash"),
)

fig = circos.plotfig()
fig.write_image("example01.png", format="png")

example01.png

2. Circos Plot (Genomics)

from pycirclizely import Circos
from pycirclizely.utils import fetch_genbank_by_accid
from pycirclizely.parser import Genbank

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

# Initialize Circos instance with genome size
sectors = gbk.get_seqid2size()
space = 0 if len(sectors) == 1 else 2
circos = Circos(sectors, space=space)
circos.text(f"Escherichia coli K-12 plasmid F<br><br>{gbk.name}", font=dict(size=14))

seqid2features = gbk.get_seqid2features(feature_type="CDS")
for sector in circos.sectors:
    # Setup track for features plot
    f_cds_track = sector.add_track((95, 100))
    f_cds_track.axis(fillcolor="lightgrey", line=dict(width=0), opacity=0.5)
    r_cds_track = sector.add_track((90, 95))
    r_cds_track.axis(fillcolor="lightgrey", line=dict(width=0), opacity=0.5)
    # Plot forward/reverse strand CDS
    features = seqid2features[sector.name]
    for feature in features:
        if feature.location.strand == 1:
            f_cds_track.genomic_features(
                feature, plotstyle="arrow", fillcolor="salmon", line=dict(width=0.5)
            )
        else:
            r_cds_track.genomic_features(
                feature, plotstyle="arrow", fillcolor="skyblue", line=dict(width=0.5)
            )
    # Plot 'gene' qualifier label if exists
    labels, label_pos_list = [], []
    for feature in features:
        start = int(feature.location.start)
        end = int(feature.location.end)
        label_pos = (start + end) / 2
        gene_name = feature.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_orientation="vertical", text_kws=dict(font=dict(size=8))
    )

    # 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()
fig.write_image("example02.png", format="png")

example02.png

3. Chord Diagram

from pycirclizely import Circos
import pandas as pd

# Create matrix dataframe (3 x 6)
row_names = ["F1", "F2", "F3"]
col_names = ["T1", "T2", "T3", "T4", "T5", "T6"]
matrix_data = [
    [10, 16, 7, 7, 10, 8],
    [4, 9, 10, 12, 12, 7],
    [17, 13, 7, 4, 20, 4],
]
matrix_df = pd.DataFrame(matrix_data, index=row_names, columns=col_names)

# Initialize Circos instance for chord diagram plot
circos = Circos.chord_diagram(
    matrix_df,
    space=5,
    cmap="T10",
    label_kws=dict(font=dict(size=12)),
    link_kws=dict(line=dict(color="black", width=0.5), direction=1),
)

fig = circos.plotfig()
fig.write_image("example03.png", format="png")

example03.png

4. Phylogenetic Tree

from plotly import graph_objects as go
from pycirclizely import Circos
from pycirclizely.utils import load_example_tree_file, ColorCycler

# Initialize Circos from phylogenetic tree
tree_file = load_example_tree_file("large_example.nwk")
circos, tv = Circos.initialize_from_tree(
    tree_file,
    r_lim=(30, 100),
    leaf_label_size=5,
    line_kws=dict(line=dict(color="lightgrey", width=1.0)),
)

# Define group-species dict for tree annotation
# In this example, set minimum species list to specify group's MRCA node
group_name2species_list = dict(
    Monotremata=["Tachyglossus_aculeatus", "Ornithorhynchus_anatinus"],
    Marsupialia=["Monodelphis_domestica", "Vombatus_ursinus"],
    Xenarthra=["Choloepus_didactylus", "Dasypus_novemcinctus"],
    Afrotheria=["Trichechus_manatus", "Chrysochloris_asiatica"],
    Euarchontes=["Galeopterus_variegatus", "Theropithecus_gelada"],
    Glires=["Oryctolagus_cuniculus", "Microtus_oregoni"],
    Laurasiatheria=["Talpa_occidentalis", "Mirounga_leonina"],
)
# Set tree line color & label color
color_cycler = ColorCycler("T10")
groups = list(group_name2species_list.keys())
colors = color_cycler.get_colors(len(groups))[::-1]
group_name2color = {name: color for name, color in zip(groups, colors)}
for group_name, species_list in group_name2species_list.items():
    color = group_name2color[group_name]
    tv.set_node_line_props(
        species_list, line=dict(color=color), apply_label_color=True
    )

# Plot figure & set legend on center
fig = circos.plotfig()
for group_name, color in group_name2color.items():
    # Use a dummy trace to create a legend entry
    fig.add_trace(
        go.Scatter(
            x=[None],
            y=None,
            mode="markers",
            marker=dict(color=color, size=10),
            name=group_name,
            showlegend=True,
        )
    )

fig.update_layout(
    legend=dict(
        x=0.5,
        y=0.47,
        xanchor="center",
        yanchor="middle",
        font=dict(size=10),
        bgcolor="rgba(255,255,255,0.0)",
    )
)

fig.write_image("example04.png", format="png", scale=2)

example04.png

5. Radar Chart

from pycirclizely import Circos
import pandas as pd

# Create RPG jobs parameter dataframe (3 jobs, 7 parameters)
df = pd.DataFrame(
    data=[
        [80, 80, 80, 80, 80, 80, 80],
        [90, 20, 95, 95, 30, 30, 80],
        [60, 90, 20, 20, 100, 90, 50],
    ],
    index=["Hero", "Warrior", "Wizard"],
    columns=["HP", "MP", "ATK", "DEF", "SP.ATK", "SP.DEF", "SPD"],
)

# Initialize Circos instance for radar chart plot
circos = Circos.radar_chart(
    df,
    vmax=100,
    marker_size=6,
    grid_interval_ratio=0.2,
)

# Plot figure & set legend on upper right
fig = circos.plotfig()
fig.write_image("example05.png", format="png", scale=2)

example05.png

Not Implemented Features

Further features to be implemented are the plotly built-in subplot and legend (complementary to the hover text) framework in the package.

Moreover, support for other types of hover info besides hover text will improve the user-plot interaction.

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

pycirclizely-1.0.0.tar.gz (3.3 MB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

pycirclizely-1.0.0-py3-none-any.whl (79.6 kB view details)

Uploaded Python 3

File details

Details for the file pycirclizely-1.0.0.tar.gz.

File metadata

  • Download URL: pycirclizely-1.0.0.tar.gz
  • Upload date:
  • Size: 3.3 MB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.1.0 CPython/3.13.3

File hashes

Hashes for pycirclizely-1.0.0.tar.gz
Algorithm Hash digest
SHA256 010295f8cd541421c69d94da048e618b7f0ef9614475f0f32da349f133f9f3bb
MD5 6dea371d907aaa4400fad7ff5c6190e5
BLAKE2b-256 98d187a86b3b3c7cdcdd98540642c1d7e48b8f4bf91d96206ee75667016d1f1f

See more details on using hashes here.

File details

Details for the file pycirclizely-1.0.0-py3-none-any.whl.

File metadata

  • Download URL: pycirclizely-1.0.0-py3-none-any.whl
  • Upload date:
  • Size: 79.6 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.1.0 CPython/3.13.3

File hashes

Hashes for pycirclizely-1.0.0-py3-none-any.whl
Algorithm Hash digest
SHA256 3c34b4f79cd9b2b6e3b0ab9c4344ca63c7a480afa3699a8dd5ca648bc8570f3e
MD5 cbe30ac1a281aeb1cd98411198db8223
BLAKE2b-256 9be68687c0ce76f3c2ca6fd6d920db098038606eceae55824b01ce1e7c564480

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page