Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Bump to v1.6.0 #72

Merged
merged 7 commits into from
Jun 1, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
23 changes: 19 additions & 4 deletions docs/plot_tips.ipynb

Large diffs are not rendered by default.

3 changes: 1 addition & 2 deletions pyproject.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[tool.poetry]
name = "pyCirclize"
version = "1.5.0"
version = "1.6.0"
description = "Circular visualization in Python"
authors = ["moshi4"]
license = "MIT"
Expand All @@ -19,7 +19,6 @@ classifiers = [
"Topic :: Scientific/Engineering :: Bio-Informatics",
"Framework :: Matplotlib",
]
include = ["tests"]

[tool.pytest.ini_options]
minversion = "6.0"
Expand Down
2 changes: 1 addition & 1 deletion src/pycirclize/__init__.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
from pycirclize.circos import Circos

__version__ = "1.5.0"
__version__ = "1.6.0"

__all__ = [
"Circos",
Expand Down
45 changes: 34 additions & 11 deletions src/pycirclize/circos.py
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,12 @@
class Circos:
"""Circos Visualization Class"""

# By default, after saving a figure using the `savefig()` method, figure object is
# automatically deleted to avoid memory leaks (no display on jupyter notebook)
# If you want to display the figure on jupyter notebook using `savefig()` method,
# set clear_savefig=False.
clear_savefig: bool = True

def __init__(
self,
sectors: Mapping[str, int | float | tuple[float, float]],
Expand Down Expand Up @@ -522,8 +528,10 @@ def initialize_from_tree(

Returns
-------
circos, tv : tuple[Circos, TreeViz]
Circos & TreeViz instances initialized from tree
circos : Circos
Circos instance
tv : TreeViz
TreeViz instance
"""
# Initialize circos sector with tree size
tree = TreeViz.load_tree(tree_data, format=format)
Expand Down Expand Up @@ -946,7 +954,9 @@ def colorbar(
vmax: float = 1,
cmap: str | Colormap = "bwr",
orientation: str = "vertical",
label: str | None = None,
colorbar_kws: dict[str, Any] | None = None,
label_kws: dict[str, Any] | None = None,
tick_kws: dict[str, Any] | None = None,
) -> None:
"""Plot colorbar
Expand All @@ -964,27 +974,35 @@ def colorbar(
<https://matplotlib.org/stable/tutorials/colors/colormaps.html>
orientation : str, optional
Colorbar orientation (`vertical`|`horizontal`)
label : str | None, optional
Colorbar label. If None, no label shown.
colorbar_kws : dict[str, Any] | None, optional
Colorbar properties (e.g. `dict(label="name", format="%.1f", ...)`)
Colorbar properties (e.g. `dict(format="%.1f", ...)`)
<https://matplotlib.org/stable/api/colorbar_api.html>
label_kws : dict[str, Any] | None, optional
Text properties (e.g. `dict(size=15, color="red", ...)`)
<https://matplotlib.org/stable/api/_as_gen/matplotlib.axes.Axes.text.html>
tick_kws : dict[str, Any] | None, optional
Axes.tick_params properties (e.g. `dict(labelsize=12, colors="red", ...)`)
<https://matplotlib.org/stable/api/_as_gen/matplotlib.axes.Axes.tick_params.html>
"""
colorbar_kws = {} if colorbar_kws is None else deepcopy(colorbar_kws)
label_kws = {} if label_kws is None else deepcopy(label_kws)
tick_kws = {} if tick_kws is None else deepcopy(tick_kws)

def plot_colorbar(ax: PolarAxes) -> None:
axin: Axes = ax.inset_axes(bounds)
norm = Normalize(vmin=vmin, vmax=vmax)
Colorbar(
cb = Colorbar(
axin,
cmap=cmap, # type: ignore
norm=norm,
orientation=orientation, # type: ignore
**colorbar_kws,
)
axin.tick_params(**tick_kws)
if label:
cb.set_label(label, **label_kws)

self._plot_funcs.append(plot_colorbar)

Expand Down Expand Up @@ -1055,9 +1073,6 @@ def savefig(
) -> None:
"""Save figure to file

`circos.savefig("result.png")` is alias for
`circos.plotfig().savefig("result.png")`

Parameters
----------
savefile : str | Path
Expand All @@ -1068,6 +1083,11 @@ def savefig(
Figure size
pad_inches : float, optional
Padding inches

Warnings
--------
To plot a figure that settings a user-defined legend, subtracks, or annotations,
call `fig.savefig()` instead of `gv.savefig()`.
"""
fig = self.plotfig(dpi=dpi, figsize=figsize)
fig.savefig(
Expand All @@ -1077,8 +1097,9 @@ def savefig(
bbox_inches="tight",
)
# Clear & close figure to suppress memory leak
fig.clear()
plt.close(fig)
if self.clear_savefig:
fig.clear()
plt.close(fig)

############################################################
# Private Method
Expand Down Expand Up @@ -1136,8 +1157,10 @@ def _initialize_figure(

Returns
-------
fig, ax : tuple[Figure, PolarAxes]
Figure, PolarAxes
fig : Figure
Figure
ax : PolarAxes
PolarAxes
"""
fig = plt.figure(figsize=figsize, dpi=dpi, tight_layout=True)
ax = fig.add_subplot(projection="polar")
Expand Down
12 changes: 8 additions & 4 deletions src/pycirclize/parser/genbank.py
Original file line number Diff line number Diff line change
Expand Up @@ -148,8 +148,10 @@ def calc_gc_skew(

Returns
-------
gc_skew_result_tuple : tuple[NDArray[np.int64], NDArray[np.float64]]
Position list & GC skew list
pos_list : NDArray[np.int64]
Position list
gc_skew_list : NDArray[np.float64]
GC skew list
"""
pos_list, gc_skew_list = [], []
seq = self.genome_seq if seq is None else seq
Expand Down Expand Up @@ -200,8 +202,10 @@ def calc_gc_content(

Returns
-------
gc_content_result_tuple : tuple[NDArray[np.int64], NDArray[np.float64]]
Position list & GC content list
pos_list : NDArray[np.int64]
Position list
gc_content_list : NDArray[np.float64]
GC content list
"""
pos_list, gc_content_list = [], []
seq = self.genome_seq if seq is None else seq
Expand Down
16 changes: 12 additions & 4 deletions src/pycirclize/parser/gff.py
Original file line number Diff line number Diff line change
Expand Up @@ -271,8 +271,12 @@ def _parse_gff(

Returns
-------
gff_records, start, end : tuple[list[GffRecord], int, int]
GFF record list, start, end
gff_records : list[GffRecord]
GFF record list
start : int
Start position of target_seqid record
end : int
End position of target_seqid record
"""
gff_file = Path(gff_file)
if gff_file.suffix == ".gz":
Expand Down Expand Up @@ -308,8 +312,12 @@ def _parse_gff_textio(

Returns
-------
gff_records, start, end : tuple[list[GffRecord], int, int]
GFF record list, start, end
gff_records : list[GffRecord]
GFF record list
start : int
Start position of target_seqid record
end : int
End position of target_seqid record
"""
# Parse GFF lines
gff_all_lines = handle.read().splitlines()
Expand Down
15 changes: 9 additions & 6 deletions src/pycirclize/parser/matrix.py
Original file line number Diff line number Diff line change
Expand Up @@ -202,16 +202,19 @@ def to_links(
) -> list[tuple[tuple[str, float, float], tuple[str, float, float]]]:
"""Convert matrix to links data for `circos.link()` method

>>> # Example usage
Returns
-------
link_target1 : tuple[str, float, float]
name1, start1, end1
link_target2 : tuple[str, float, float]
name2, start2, end2

Examples
--------
>>> matrix = Matrix(matrix_file)
>>> circos = Circos(matrix.to_sectors())
>>> for link in matrix.to_links():
>>> circos.link(*link)

Returns
-------
links : list[tuple[tuple[str, float, float], tuple[str, float, float]]]
List of link `((name1, start1, end1), (name2, end2, start2))`
"""
return self._links

Expand Down
6 changes: 4 additions & 2 deletions src/pycirclize/track.py
Original file line number Diff line number Diff line change
Expand Up @@ -1318,8 +1318,10 @@ def _to_arc_radr(

Returns
-------
arc_rad, arc_r : tuple[list[float], list[float]]
Arc radian list, Ard radius list
arc_rad : list[float]
Arc radian list
arc_r : list[float]
Arc radius list
"""
all_arc_rad, all_arc_r = [], []
for i in range(len(rad) - 1):
Expand Down
6 changes: 4 additions & 2 deletions src/pycirclize/tree.py
Original file line number Diff line number Diff line change
Expand Up @@ -442,8 +442,10 @@ def _set_uniq_innode_name(self, tree: Tree) -> tuple[Tree, list[str]]:

Returns
-------
tree, uniq_node_names: tuple[Tree, list[str]]
Unique node name set tree object & set unique node names
tree : Tree
Tree (set unique node names)
uniq_node_names : list[str]
Unique node names
"""
tree = deepcopy(tree)
uniq_innode_names: list[str] = []
Expand Down
3 changes: 2 additions & 1 deletion src/pycirclize/utils/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
load_example_tree_file,
load_prokaryote_example_file,
)
from pycirclize.utils.helper import ColorCycler, calc_group_spaces
from pycirclize.utils.helper import ColorCycler, calc_group_spaces, is_pseudo_feature

__all__ = [
"plot",
Expand All @@ -17,4 +17,5 @@
"load_example_tree_file",
"ColorCycler",
"calc_group_spaces",
"is_pseudo_feature",
]
8 changes: 6 additions & 2 deletions src/pycirclize/utils/dataset.py
Original file line number Diff line number Diff line change
Expand Up @@ -96,8 +96,12 @@ def load_eukaryote_example_dataset(

Returns
-------
chr_bed_file, cytoband_file, chr_links : tuple[Path, Path, list[ChrLink]]
BED file, Cytoband file, Chromosome links
chr_bed_file : Path
BED file
cytoband_file : Path
Cytoband file
chr_links : list[ChrLink]
Chromosome links
"""
# Check specified name dataset exists or not
if name not in config.EUKARYOTE_DATASET:
Expand Down
18 changes: 18 additions & 0 deletions src/pycirclize/utils/helper.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

import matplotlib as mpl
import numpy as np
from Bio.SeqFeature import SeqFeature
from matplotlib.colors import Colormap, to_hex


Expand Down Expand Up @@ -129,3 +130,20 @@ def calc_group_spaces(
return spaces
else:
return spaces[:-1]


def is_pseudo_feature(feature: SeqFeature) -> bool:
"""Check target feature is pseudo or not from qualifiers tag

Parameters
----------
feature : SeqFeature
Target feature

Returns
-------
check_result : bool
pseudo check result
"""
quals = feature.qualifiers
return True if "pseudo" in quals or "pseudogene" in quals else False
Loading