pyCirclize

repository·main·Indexed 22 days ago

https://github.com/moshi4/pycirclize

A Python package built on top of matplotlib for circular visualizations. It enables the creation of Circos plots, chord diagrams, radar charts, and specialized bioinformatics visualizations such as genomic and phylogenetic tree plots. Key features include the Circos and Sector classes for managing circular layouts, a Genbank parser for genomic features, and support for interactive tooltips in Jupyter environments.

Tokens
15.7K
Snippets
60
Records
72
Agent score
77%

What's inside pyCirclize

  1. Create a Chord Diagram from From-To Table Data

    main

    If your data is in a long format (a table with 'from', 'to', and 'value' columns) rather than a matrix, use pycirclize.parser.Matrix.parse_fromto_table() to convert it into a format compatible with Circos.chord_diagram().

    Steps:

    1. Create a pandas DataFrame with columns representing the source, target, and value.
    2. Use Matrix.parse_fromto_table(df) to generate a matrix object.
    3. Pass the resulting matrix to Circos.chord_diagram().
    from pycirclize import Circos
    from pycirclize.parser import Matrix
    import pandas as pd
    
    # Create from-to table dataframe
    fromto_table_df = pd.DataFrame(
        [
            ["A", "B", 10],
            ["A", "C", 5],
            ["A", "D", 15],
            ["B", "D", 8],
            ["C", "D", 6],
        ],
        columns=["from", "to", "value"],
    )
    
    # Convert to matrix
    matrix = Matrix.parse_fromto_table(fromto_table_df)
    
    # Initialize Circos instance
    circos = Circos.chord_diagram(
        matrix,
        space=3,
        cmap=dict(A="royalblue", B="orange", C="green", D="red"),
        label_kws=dict(size=12),
    )
    
    fig = circos.plotfig()
  2. Enable interactive tooltips in Jupyter

    main

    To enable interactive tooltip displays in Jupyter (VSCode or JupyterLab), you must install the [tooltip] extra or install ipympl via conda. When plotting, call circos.plotfig(tooltip=True).

    Note: Interactive tooltips require a live Python kernel and are not permanently enabled in the notebook after plotting.

    pip install pycirclize[tooltip]
    # or
    conda install -c conda-forge pycirclize ipympl
    
    # Usage in code
    circos.plotfig(tooltip=True)
  3. Use the Track class to plot data layers

    main

    The Track class is used to manage and plot data layers within a specific Sector of a circular plot. It provides high-level methods to draw various geometric shapes, text, and statistical plots (like bars, lines, and scatter plots) mapped to the circular coordinate system. Most plotting methods accept x (position along the sector) and y (radius/value) coordinates, which the track automatically converts to polar coordinates.

    # Conceptual usage pattern
    track = sector.add_track(r_lim=(10, 12))
    track.rect(start=100, end=200, fc="red")
    track.line(x=[100, 150, 200], y=[5, 10, 5])
    track.scatter(x=[120, 180], y=[7, 8])
  4. Create a Phylogenetic Tree visualization

    main

    Visualize phylogenetic trees by using Circos.initialize_from_tree(). This method takes a Newick tree file and returns a Circos instance and a tree view object (tv). You can use tv.set_node_line_props() to color specific species or groups within the tree.

    from pycirclize import Circos
    from pycirclize.utils import load_example_tree_file, ColorCycler
    from matplotlib.lines import Line2D
    
    # 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(color="lightgrey", lw=1.0),
    )
    
    # Define group-species dict for tree annotation
    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
    ColorCycler.set_cmap("tab10")
    group_name2color = {name: ColorCycler() for name in group_name2species_list.keys()}
    for group_name, species_list in group_name2species_list.items():
        color = group_name2color[group_name]
        tv.set_node_line_props(species_list, color=color, apply_label_color=True)
    
    # Plot figure & set legend on center
    fig = circos.plotfig()
    _ = circos.ax.legend(
        handles=[Line2D([], [], label=n, color=c) for n, c in group_name2color.items()],
        labelcolor=group_name2color.values(),
        fontsize=6,
        loc="center",
        bbox_to_anchor=(0.5, 0.5),
    )
    fig.savefig("example04.png")
  5. Create a Circos Plot for Genomics

    main

    For bioinformatics, use fetch_genbank_by_accid to download data and the Genbank parser to process it. You can initialize Circos using the genome sizes from the Genbank object and plot genomic features (like CDS) onto tracks using genomic_features().

    from pycirclize import Circos
    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)
    
    # 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\n\n{gbk.name}", 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(fc="lightgrey", ec="none", alpha=0.5)
        r_cds_track = sector.add_track((90, 95))
        r_cds_track.axis(fc="lightgrey", ec="none", alpha=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", fc="salmon", lw=0.5)
            else:
                r_cds_track.genomic_features(feature, plotstyle="arrow", fc="skyblue", lw=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.annotate(label_pos, gene_name, label_size=6)
    
        # Plot xticks (interval = 10 Kb)
        r_cds_track.xticks_by_interval(
            10000, outer=False, label_formatter=lambda v: f"{v/1000:.1f} Kb"
        )
    
    circos.savefig("example02.png")
  6. Customize link properties with a handler function

    main

    To highlight specific connections in a Chord Diagram, provide a link_kws_handler function to Circos.chord_diagram(). This function accepts from_label and to_label as arguments and should return a dictionary of properties (like alpha or zorder) to apply to that specific link.

    def link_kws_handler(from_label: str, to_label: str):
        if from_label in ("C", "G"):
            # Highlight specific links by increasing zorder and adjusting alpha
            return dict(alpha=0.5, zorder=1.0)
        else:
            return dict(alpha=0.1, zorder=0)
    
    circos = Circos.chord_diagram(
        matrix_df,
        link_kws_handler=link_kws_handler,
        # ... other arguments
    )
  7. Create a Radar Chart

    main

    Generate radar charts from a pandas DataFrame using Circos.radar_chart(). This is useful for comparing multiple parameters across different categories.

    from pycirclize 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()
    _ = circos.ax.legend(loc="upper right", fontsize=10)
    fig.savefig("example05.png")
  8. Create a Chord Diagram

    main

    Generate a chord diagram from a pandas DataFrame representing an adjacency matrix using Circos.chord_diagram(). This method handles the initialization of the circular layout and the links between sectors automatically.

    from pycirclize 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="tab10",
        label_kws=dict(size=12),
        link_kws=dict(ec="black", lw=0.5, direction=1),
    )
    
    circos.savefig("example03.png")
  9. Create a basic Circos Plot

    main

    To create a custom Circos plot, initialize the Circos class with a dictionary of sectors and their sizes. You can then iterate through sectors to add tracks, plot lines, scatter points, or bars. Use circos.link() to draw connections between different parts of the circle.

    Key steps:

    1. Define sectors: sectors = {"A": 10, "B": 15}
    2. Initialize: circos = Circos(sectors, space=5)
    3. Add tracks: sector.add_track((start_radius, end_radius))
    4. Draw links: circos.link((sector1, start, end), (sector2, start, end))
    from pycirclize 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, 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), color="skyblue")
    circos.link(("C", 2, 5), ("E", 15, 12), color="chocolate", direction=1)
    circos.link(("D", 3, 5), ("D", 18, 15), color="lime", ec="black", lw=0.5, hatch="//", direction=2)
    circos.link(("D", 8, 10), ("E", 2, 8), color="violet", ec="red", lw=1.0, ls="dashed")
    
    circos.savefig("example01.png")
  10. Create a Chord Diagram from Matrix Data

    main

    You can generate a Chord Diagram by passing a pandas DataFrame (representing a matrix) to Circos.chord_diagram().

    Key parameters for Circos.chord_diagram():

    • matrix_df: A pandas DataFrame with row and column names.
    • start, end: Angular range for the plot.
    • space: Space between sectors.
    • r_lim: Tuple defining the radial range for the sectors.
    • cmap: Colormap (string or dictionary) for coloring sectors.
    • label_kws: Dictionary of keyword arguments for sector labels (e.g., r, size, color).
    • link_kws: Dictionary of keyword arguments for the links (e.g., ec, lw, direction).
    • link_kws_handler: A callback function used to dynamically customize link properties based on the source and destination labels.
    from pycirclize import Circos
    import pandas as pd
    
    # Create matrix dataframe
    matrix_df = pd.DataFrame(
        [[4, 14, 13, 17, 5, 2], [7, 1, 6, 8, 12, 15], [9, 10, 3, 16, 11, 18]],
        index=["S1", "S2", "S3"],
        columns=["E1", "E2", "E3", "E4", "E5", "E6"],
    )
    
    # Initialize Circos instance
    circos = Circos.chord_diagram(
        matrix_df,
        start=-265,
        end=95,
        space=5,
        r_lim=(93, 100),
        cmap="tab10",
        label_kws=dict(r=94, size=12, color="white"),
        link_kws=dict(ec="black", lw=0.5),
    )
    
    fig = circos.plotfig()
  11. Cycle through colors with ColorCycler

    main
    The pycirclize.utils.helper.ColorCycler class is a utility for cycling through a predefined or custom set of colors. This is helpful when plotting multiple sectors, lines, or points and you want to ensure each element gets a distinct color from a palette.