Source code for RNApysoforms.make_plot

import plotly.graph_objects as go
from plotly.subplots import make_subplots
import polars as pl
from typing import List, Optional
import warnings

[docs] def make_plot( traces: List[go.Trace], subplot_titles: List[str] = ["Transcript Structure"], horizontal_spacing: float = 0.02, vertical_spacing: float = 0.02, showlegend: bool = True, height: int = 900, width: int = 1800, template: str = "plotly_white", boxgroupgap: float = 0.8, boxgap: float = 0.2, vert_grid_transcript_structure_plot: bool = True, horz_grid_transcript_structure_plot: bool = True, vert_grid_expression_plot: bool = True, horz_grid_expression_plot: bool = True, legend_font_size: int = 12, xaxis_font_size: int = 12, yaxis_font_size: int = 12, subplot_title_font_size: int = 16, legend_title_font_size: int = 14, hover_font_size: int = 12, hovermode: str = "closest", column_widths: list = None ) -> go.Figure: """ Creates a multi-panel Plotly figure combining transcript structure plots and expression data plots. This function constructs a Plotly figure with multiple subplots arranged horizontally. It accepts a list of Plotly traces representing different data types (e.g., transcript structures, expression data) and organizes them into subplots. The function customizes the layout, axes, and appearance of each subplot based on the provided parameters, allowing for flexible visualization of genomic data alongside expression metrics. **Expected Structure of `traces`:** - The `traces` list should contain multiple sublists, each representing traces for a subplot. - The last element in `traces` must be a dictionary (`y_dict`) mapping transcript identifiers to y-axis positions. Parameters ---------- traces : List[List[go.Trace]] or List[List[dict]] A list where each element is a list of Plotly traces (either `go.Trace` objects or dictionaries for transcript structures). The last element must be a dictionary mapping transcript identifiers to y-axis positions. subplot_titles : List[str], optional A list of titles for each subplot. Default is ["Transcript Structure"]. horizontal_spacing : float, optional Horizontal spacing between subplots as a fraction of the average subplot width. Default is 0.02. vertical_spacing : float, optional Vertical spacing between subplots as a fraction of the average subplot height. Default is 0.02. showlegend : bool, optional Whether to display the legend. Default is True. height : int, optional The height of the figure in pixels. Default is 800. width : int, optional The width of the figure in pixels. Default is 1800. template : str, optional The Plotly template to use for styling. Default is "plotly_white". boxgroupgap : float, optional Gap between grouped boxes in box or violin plots. Default is 0.8. boxgap : float, optional Gap between individual boxes in box or violin plots. Default is 0.2. vert_grid_transcript_structure_plot : bool, optional Whether to show vertical grid lines in transcript structure plots. Default is True. horz_grid_transcript_structure_plot : bool, optional Whether to show horizontal grid lines in transcript structure plots. Default is True. vert_grid_expression_plot : bool, optional Whether to show vertical grid lines in expression plots. Default is True. horz_grid_expression_plot : bool, optional Whether to show horizontal grid lines in expression plots. Default is True. legend_font_size : int, optional Font size for legend text. Default is 12. xaxis_font_size : int, optional Font size for x-axis labels. Default is 12. yaxis_font_size : int, optional Font size for y-axis labels. Default is 12. subplot_title_font_size : int, optional Font size for subplot titles. Default is 16. legend_title_font_size : int, optional Font size for the legend title. Default is 14. hover_font_size : int, optional Font size for hover text labels. Default is 12. hovermode : str, optional Hover mode for the figure. Options include "closest", "x", "y", "x unified", "y unified", etc. Default is "closest". column_widths: list, optional A list of floats containing the same number of items as the number of subplots you are trying to generate. For a figure with three subplots you could pass [0.4, 0.3, 0.3] to make the first subplot take up 40% of the horizontal space and the second and third subplot each take 30% of the horizontal space. By default it is set to `None` which will make the subplots have proportional sizes. Returns ------- go.Figure A Plotly Figure object containing the assembled subplots with customized layout and styling. Raises ------ ValueError If `traces` does not contain at least one set of traces and a `y_dict` mapping. Examples -------- Create a figure with transcript structure and expression data: >>> import plotly.graph_objects as go >>> from RNApysoforms import make_plot >>> fig = make_plot( ... traces=traces, ... subplot_titles=["Transcript Structure", "Expression Levels"], ... height=600, ... width=1200 ... ) >>> fig.show() Notes ----- - The function expects the last element of `traces` to be a dictionary (`y_dict`) mapping transcript identifiers to y-axis positions. - Traces are classified into transcript structure traces or expression data traces based on their content: - Transcript traces are expected to be dictionaries (usually shapes or annotations). - Expression traces are expected to be Plotly trace objects with a 'type' attribute (e.g., `go.Box`, `go.Violin`). - The function dynamically assigns traces to subplots and customizes axes and layout based on the type of data. - The y-axis is shared across subplots to align transcript structures with their corresponding expression data. - Hover settings can be customized using the `hovermode` parameter. """ ## Separate traces and dictionary y_dict = traces[-1] full_trace_list = traces[:-1] ## Define column widths if column_widths == None: column_width = (1/len(full_trace_list)) column_widths = [column_width] * len(full_trace_list) elif (len(column_widths) != len(full_trace_list)) or (not isinstance(column_widths, list)): warnings.warn("The `column_widths` parameter must be a list of the same length as the number of subplots being generated" "\nMaking all subplots have the same size as default option") column_width = (1/len(full_trace_list)) column_widths = [column_width] * len(full_trace_list) # Initialize the subplot figure with shared y-axes fig = make_subplots( rows=1, cols=len(full_trace_list), subplot_titles=subplot_titles, horizontal_spacing=horizontal_spacing, vertical_spacing=vertical_spacing, column_widths=column_widths, shared_yaxes=True, # Share y-axes across all subplots ) # Initialize lists to separate transcript and expression traces and their subplot indexes transcript_traces = [] expression_traces = [] transcript_indexes = [] expression_indexes = [] # Classify traces into transcript or expression traces based on their content index = 1 # Start subplot index from 1 for trace in full_trace_list: if hasattr(trace[0], 'type'): # If the trace has a 'type' attribute, it's an expression trace (e.g., go.Box, go.Violin) expression_traces.extend(trace) expression_indexes.append(index) elif isinstance(trace[0], dict): # If the trace is a dictionary, it's a transcript trace (e.g., shapes or annotations) transcript_traces.append(trace) transcript_indexes.append(index) index += 1 # Increment subplot index # Add all traces to their respective subplots for i, subplot_traces in enumerate(full_trace_list, start=1): fig.add_traces( subplot_traces, rows=[1] * len(subplot_traces), cols=[i] * len(subplot_traces) ) # Customize axes and layout for transcript structure subplots for i in transcript_indexes: # Customize x-axes for transcript structure plots (hide tick labels) fig.update_xaxes( showticklabels=False, title="", row=1, col=i, showgrid=vert_grid_transcript_structure_plot ) # Customize y-axes for transcript structure plots (show transcript labels) fig.update_yaxes( showticklabels=False, tickvals=list(y_dict.values()), ticktext=list(y_dict.keys()), tickfont=dict(size=10, family='DejaVu Sans', color='black'), title="", # Optional title for y-axis row=1, col=i, showgrid=horz_grid_transcript_structure_plot ) # Customize axes and layout for expression data subplots for i in expression_indexes: # Customize x-axes for expression plots (show tick labels) fig.update_xaxes( showticklabels=True, title="", # Optional title for x-axis row=1, col=i, showgrid=vert_grid_expression_plot ) # Customize y-axes for expression plots (hide tick labels) fig.update_yaxes( showticklabels=False, tickvals=list(y_dict.values()), ticktext=list(y_dict.keys()), ticks='', # Hide ticks row=1, col=i, range=[-0.8, (len(y_dict) - 0.2)], # Adjust y-axis range to align with transcript plots showgrid=horz_grid_expression_plot ) # Ensure the first subplot's y-axis shows tick labels (transcript identifiers) fig.update_yaxes( showticklabels=True, row=1, col=1 ) # Update overall layout settings fig.update_layout( title_text="", # Optional overall title showlegend=showlegend, height=height, width=width, hovermode=hovermode, hoverlabel=dict(font=dict(size=hover_font_size)), margin=dict(l=100, r=50, t=100, b=50), # Adjust margins as needed boxmode='group', legend_tracegroupgap=7, boxgroupgap=boxgroupgap, boxgap=boxgap, violinmode='group', violingroupgap=boxgroupgap, violingap=boxgap, yaxis=dict( range=[-0.8, (len(y_dict) - 0.2)], tickfont=dict(size=yaxis_font_size) ), legend=dict(font=dict(size=legend_font_size), grouptitlefont=dict(size=legend_title_font_size)), template=template, annotations=[dict(font=dict(size=subplot_title_font_size)) for annotation in fig['layout']['annotations']]) """ There is a bug in plotly and the tickfont for the x-axis has to be updated in this manner, it can't be done using the `update_layout` function. See: https://github.com/plotly/plotly.py/issues/2922 """ fig.update_xaxes(tickfont_size=(xaxis_font_size)) return fig # Return the assembled figure