diff --git a/src/pyranges_plot/core.py b/src/pyranges_plot/core.py index 4de3a63..333c4e3 100644 --- a/src/pyranges_plot/core.py +++ b/src/pyranges_plot/core.py @@ -1,4 +1,5 @@ import pandas as pd +import importlib from pyranges.core.names import END_COL from .names import CUM_DELTA_COL @@ -9,6 +10,15 @@ ) +def check4dependency(name): + """Check if a module is installed""" + try: + importlib.import_module(name) + return True + except ImportError: + return False + + # CORE FUNCTIONS # id_col ID_COL = None diff --git a/src/pyranges_plot/data_preparation.py b/src/pyranges_plot/data_preparation.py index 8bea388..87e287d 100644 --- a/src/pyranges_plot/data_preparation.py +++ b/src/pyranges_plot/data_preparation.py @@ -1,11 +1,25 @@ import numpy as np from intervaltree import IntervalTree -import matplotlib.pyplot as plt -import matplotlib.colors as mcolors import pyranges as pr -import plotly.colors as pc from pyranges.core.names import CHROM_COL, START_COL, END_COL +# Check for matplotlib +try: + import matplotlib.pyplot as plt + import matplotlib.colors as mcolors + + missing_plt_flag = 0 +except ImportError: + missing_plt_flag = 1 +# Check for plotly +try: + import plotly.colors as pc + + missing_ply_flag = 0 +except ImportError: + missing_ply_flag = 1 + + from .names import ( PR_INDEX_COL, SHRTHRES_COL, @@ -14,7 +28,7 @@ COLOR_TAG_COL, BORDER_COLOR_COL, ) -from .core import cumdelting, get_engine, get_warnings +from .core import cumdelting, get_engine, get_warnings, check4dependency from .matplotlib_base.core import plt_popup_warning @@ -110,28 +124,34 @@ def update_y(genesmd_df, exon_height, v_spacer): ###colors for genes def is_pltcolormap(colormap_string): """Checks whether the string given is a valid plt colormap name.""" - try: - colormap = plt.colormaps[colormap_string] - if colormap is not None and isinstance(colormap, mcolors.Colormap): - return True - else: + + if check4dependency("matplotlib"): + try: + colormap = plt.colormaps[colormap_string] + if colormap is not None and isinstance(colormap, mcolors.Colormap): + return True + else: + return False + + except KeyError: return False - except KeyError: + else: return False def is_plycolormap(colormap_string): """Checks whether the string given is a valid plotly color object name.""" - if hasattr(pc.sequential, colormap_string): - return True - elif hasattr(pc.diverging, colormap_string): - return True - elif hasattr(pc.cyclical, colormap_string): - return True - elif hasattr(pc.qualitative, colormap_string): - return True + if check4dependency("plotly"): + if hasattr(pc.sequential, colormap_string): + return True + elif hasattr(pc.diverging, colormap_string): + return True + elif hasattr(pc.cyclical, colormap_string): + return True + elif hasattr(pc.qualitative, colormap_string): + return True def get_plycolormap(colormap_string): @@ -168,11 +188,11 @@ def subdf_assigncolor(subdf, colormap, color_col, exon_border): colormap = get_plycolormap(colormap) else: raise Exception( - "The provided string does not match any plt or plotly colormap." + "The provided string does not match any installed dependency colormap." ) # 1-plt colormap to list - if isinstance(colormap, mcolors.ListedColormap): + if not missing_plt_flag and isinstance(colormap, mcolors.ListedColormap): colormap = list(colormap.colors) # colors of plt object colormap = [ "rgb({}, {}, {})".format(int(r * 255), int(g * 255), int(b * 255)) diff --git a/src/pyranges_plot/matplotlib_base/plot_exons_plt.py b/src/pyranges_plot/matplotlib_base/plot_exons_plt.py index 1a6df60..537d6cd 100644 --- a/src/pyranges_plot/matplotlib_base/plot_exons_plt.py +++ b/src/pyranges_plot/matplotlib_base/plot_exons_plt.py @@ -1,5 +1,6 @@ import matplotlib.pyplot as plt import pandas as pd +from matplotlib.patches import Rectangle from pyranges.core.names import CHROM_COL, START_COL, END_COL, STRAND_COL from .core import plt_popup_warning, coord2percent, rgb_string_to_tuple @@ -8,7 +9,7 @@ apply_gene_bridge, plot_introns, ) -from ..names import PR_INDEX_COL, BORDER_COLOR_COL, COLOR_INFO +from ..names import PR_INDEX_COL, BORDER_COLOR_COL, COLOR_INFO, COLOR_TAG_COL arrow_style = "round" @@ -22,7 +23,6 @@ def plot_exons_plt( chrmd_df, chrmd_df_grouped, ts_data, - legend_item_d, id_col, max_shown=25, transcript_str=False, @@ -65,6 +65,16 @@ def plot_exons_plt( x_ticks = feat_dict["x_ticks"] # Create figure and axes + # check for legend + # Create legend items list + if legend: + legend_item_d = ( + subdf.groupby(COLOR_TAG_COL)[COLOR_INFO] + .apply(lambda x: Rectangle((0, 0), 1, 1, color=list(x)[0])) + .to_dict() + ) + else: + legend_item_d = {} # pixel in inches px = 1 / plt.rcParams["figure.dpi"] x = file_size[0] * px diff --git a/src/pyranges_plot/plot_features.py b/src/pyranges_plot/plot_features.py index 5ed02fe..0a7d8ae 100644 --- a/src/pyranges_plot/plot_features.py +++ b/src/pyranges_plot/plot_features.py @@ -1,5 +1,17 @@ import copy +ori_l = ["#f05f89", "#f0db36", "#7bc45f", "#5e4699", "#f7943a", "#537ebf", "#ee3a36"] +darker_l = ["#9b3c59", "#a28b22", "#4d6e3a", "#3c285f", "#a46327", "#345a7d", "#9c2523"] +lighter_l = [ + "#ffadc9", + "#ffee76", + "#a8e89a", + "#816bb9", + "#ffc56b", + "#82b3ff", + "#ff7a74", +] +prp_cmap = ori_l + lighter_l + darker_l plot_features_dict = { "arrow_color": ("grey", "Color of the arrow indicating strand.", " "), @@ -14,7 +26,7 @@ " ", ), "colormap": ( - "Alphabet", + prp_cmap, "Sequence of colors to assign to every group of intervals sharing the same “color_col” value. It can be provided as a Matplotlib colormap, a Plotly color sequence (built as lists), a string naming the previously mentioned color objects from Matplotlib and Plotly, or a dictionary with the following structure {color_column_value1: color1, color_column_value2: color2, ...}. When a specific color_col value is not specified in the dictionary it will be colored in black.", " ", ), diff --git a/src/pyranges_plot/plot_main.py b/src/pyranges_plot/plot_main.py index f04e248..5bcfc7a 100644 --- a/src/pyranges_plot/plot_main.py +++ b/src/pyranges_plot/plot_main.py @@ -1,5 +1,5 @@ import pandas as pd -from matplotlib.patches import Rectangle + # import pyranges as pr from .core import ( @@ -21,8 +21,6 @@ subdf_assigncolor, ) from .introns_off import introns_resize, recalc_axis -from .matplotlib_base.plot_exons_plt import plot_exons_plt -from .plotly_base.plot_exons_ply import plot_exons_ply from pyranges.core.names import ( CHROM_COL, START_COL, @@ -38,11 +36,25 @@ CUM_DELTA_COL, EXON_IX_COL, TEXT_PAD_COL, - COLOR_TAG_COL, - COLOR_INFO, THICK_COL, ) +# Check for matplotlib +try: + from .matplotlib_base.plot_exons_plt import plot_exons_plt + + missing_plt_flag = 0 +except ImportError: + missing_plt_flag = 1 + +# Check for plotly +try: + from .plotly_base.plot_exons_ply import plot_exons_ply + + missing_ply_flag = 0 +except ImportError: + missing_ply_flag = 1 + def plot( data, @@ -495,66 +507,66 @@ def getvalue(key): # print(subdf) if engine in ["plt", "matplotlib"]: - # Create legend items list - if legend: - legend_item_d = ( - subdf.groupby(COLOR_TAG_COL)[COLOR_INFO] - .apply(lambda x: Rectangle((0, 0), 1, 1, color=list(x)[0])) - .to_dict() + if not missing_plt_flag: + plot_exons_plt( + subdf=subdf, + depth_col=depth_col, + tot_ngenes_l=tot_ngenes_l, + feat_dict=feat_dict, + genesmd_df=genesmd_df, + chrmd_df=chrmd_df, + chrmd_df_grouped=chrmd_df_grouped, + ts_data=ts_data, + max_shown=max_shown, + id_col=ID_COL, + transcript_str=thick_cds, + tooltip=tooltip, + legend=legend, + y_labels=y_labels, + text=text, + title_chr=title_chr, + packed=packed, + to_file=to_file, + file_size=file_size, + warnings=warnings, + tick_pos_d=tick_pos_d, + ori_tick_pos_d=ori_tick_pos_d, ) + else: - legend_item_d = {} - - plot_exons_plt( - subdf=subdf, - depth_col=depth_col, - tot_ngenes_l=tot_ngenes_l, - feat_dict=feat_dict, - genesmd_df=genesmd_df, - chrmd_df=chrmd_df, - chrmd_df_grouped=chrmd_df_grouped, - ts_data=ts_data, - legend_item_d=legend_item_d, - max_shown=max_shown, - id_col=ID_COL, - transcript_str=thick_cds, - tooltip=tooltip, - legend=legend, - y_labels=y_labels, - text=text, - title_chr=title_chr, - packed=packed, - to_file=to_file, - file_size=file_size, - warnings=warnings, - tick_pos_d=tick_pos_d, - ori_tick_pos_d=ori_tick_pos_d, - ) + raise Exception( + "Make sure to install matplotlib dependecies by running `pip install pyranges-plot[plt]`" + ) - elif engine == "ply" or engine == "plotly": - plot_exons_ply( - subdf=subdf, - depth_col=depth_col, - feat_dict=feat_dict, - genesmd_df=genesmd_df, - chrmd_df=chrmd_df, - chrmd_df_grouped=chrmd_df_grouped, - ts_data=ts_data, - max_shown=max_shown, - id_col=ID_COL, - transcript_str=thick_cds, - tooltip=tooltip, - legend=legend, - y_labels=y_labels, - text=text, - title_chr=title_chr, - packed=packed, - to_file=to_file, - file_size=file_size, - warnings=warnings, - tick_pos_d=tick_pos_d, - ori_tick_pos_d=ori_tick_pos_d, - ) + elif engine in ["ply", "plotly"]: + if not missing_ply_flag: + plot_exons_ply( + subdf=subdf, + depth_col=depth_col, + feat_dict=feat_dict, + genesmd_df=genesmd_df, + chrmd_df=chrmd_df, + chrmd_df_grouped=chrmd_df_grouped, + ts_data=ts_data, + max_shown=max_shown, + id_col=ID_COL, + transcript_str=thick_cds, + tooltip=tooltip, + legend=legend, + y_labels=y_labels, + text=text, + title_chr=title_chr, + packed=packed, + to_file=to_file, + file_size=file_size, + warnings=warnings, + tick_pos_d=tick_pos_d, + ori_tick_pos_d=ori_tick_pos_d, + ) + else: + raise Exception( + "Make sure to install plotly dependecies by running `pip install pyranges-plot[plotly]`" + ) else: raise Exception("Please define engine with set_engine().")