Source code for pymaftools.plot.SignatureTablePlot

"""
SignatureTablePlot Module

Plotting functionality specific to SignatureTable objects.
Reachable via ``signature_table.plot``.
"""

from __future__ import annotations

import matplotlib.pyplot as plt
import pandas as pd

from .PivotStatsPlot import PivotStatsPlot


[docs] class SignatureTablePlot(PivotStatsPlot): """ Plotting interface for SignatureTable objects. Inherits the shared plotters from :class:`PivotStatsPlot` (``oncoplot``, ``plot_pca_samples``, ``plot_boxplot_with_annot``) and adds the signature-specific exposure landscape (:meth:`stacked_bar`). A :class:`~pymaftools.core.SignatureTable.SignatureTable` is oriented signatures (rows) × samples (columns), with each value the activity / exposure of that signature in that sample. """
[docs] def stacked_bar( self, *, normalize: bool = True, ax=None, figsize: tuple[float, float] = (20, 6), colormap: str = "tab20", legend: bool = True, width: float = 1.0, show_sample_labels: bool | None = None, ): """Per-sample signature exposure as a stacked bar chart. Samples run along the x-axis; each bar stacks its signature contributions (one colour per signature). This is the canonical "signature landscape" view of an exposure matrix. Parameters ---------- normalize : bool, default True Scale each sample's bar to sum to 1 (relative contributions). When False, bars use the raw exposure totals (variable bar heights). ax : matplotlib.axes.Axes, optional Axes to draw on; a new figure/axes is created when None. figsize : tuple, default (20, 6) Figure size, used only when ``ax`` is None. colormap : str, default "tab20" Matplotlib colormap mapping signatures to colours. legend : bool, default True Draw the signature legend (placed outside, upper-right). width : float, default 0.9 Bar width passed to the pandas bar plot. Returns ------- matplotlib.axes.Axes The axes the bars were drawn on. Examples -------- >>> signature_table.plot.stacked_bar() # relative >>> signature_table.plot.stacked_bar(normalize=False) # raw exposures """ # SignatureTable is signatures × samples; transpose so each row is a # sample (a bar) and each column a signature (a stacked segment). Wrap # in a plain DataFrame first: PivotTable overrides ``.plot`` with the # accessor, which would otherwise shadow pandas' bar plotting. df = pd.DataFrame(self.pivot_table).T.astype(float) if normalize: totals = df.sum(axis=1) df = df.div(totals.where(totals != 0, 1), axis=0) from matplotlib.colors import to_hex from . import style # Explicit colour per signature, so the stacked bars and the legend card # share one mapping. cmap = plt.get_cmap(colormap) sig_colors = {sig: to_hex(cmap(i % cmap.N)) for i, sig in enumerate(df.columns)} legend_ax = None if ax is None: self.fig, ax, legend_ax = style.fig_with_legend(figsize, legend_width=0.12) else: self.fig = ax.figure df.plot( kind="bar", stacked=True, ax=ax, color=[sig_colors[s] for s in df.columns], width=width, legend=False, ) ax.set_xlabel(f"sample (n={len(df)})") ax.set_ylabel("relative exposure" if normalize else "exposure") ax.margins(x=0) style.style_axes(ax) style.apply_sample_xticklabels(ax, df.index, show=show_sample_labels) if legend and legend_ax is not None: style.draw_legend_cards(legend_ax, {"signature": sig_colors}) elif legend: ax.legend( title="signature", bbox_to_anchor=(1.01, 1), loc="upper left", frameon=False, ) return ax