Title Legend Helper

GitHub Link to Code.

Helper for plot title and legend management.

Provides shared utilities for consistent title wrapping, positioning, and legend creation across density and violin plots.

class mdxplain.plots.helper.title_legend_helper.TitleLegendHelper

Helper class for plot title and legend operations.

Provides static methods for wrapping long titles, adding figure-level titles with consistent positioning, and creating unified legends for DataSelector colors.

Examples

>>> # Wrap long title
>>> wrapped = TitleLegendHelper.wrap_title("Very Long Feature Name", 20)
>>> print(wrapped)
Very Long Feature
Name
>>> # Add figure title
>>> TitleLegendHelper.add_title(fig, "My Plot", title_y=0.98)
>>> # Add DataSelector legend
>>> colors = {"cluster_0": "#FF0000", "cluster_1": "#00FF00"}
>>> TitleLegendHelper.add_legend(fig, colors, "Clusters", None)
static wrap_title(title: str, max_chars_per_line: int = 40) str

Wrap long titles to multiple lines.

Splits title into multiple lines if it exceeds max_chars_per_line, breaking at word boundaries to maintain readability.

Parameters

titlestr

Original title text

max_chars_per_lineint, default=40

Maximum characters per line before wrapping

Returns

str

Title with line breaks inserted

Examples

>>> wrapped = TitleLegendHelper.wrap_title("Very long feature name", 20)
>>> print(wrapped)
Very long feature
name
>>> short = TitleLegendHelper.wrap_title("Short", 20)
>>> print(short)
Short

Notes

Uses textwrap.wrap() which breaks at word boundaries. Empty titles return empty string.

static format_bold_superscript_title(title: str) str

Make mathtext superscripts bold for plot titles.

Parameters

titlestr

Title text that may contain mathtext superscripts like $^{A.H6.54}$.

Returns

str

Title text with superscript content wrapped in \mathbf{...} when applicable.

static estimate_title_height(title: str, max_chars_per_line: int = 80, fontsize: int = 18) tuple

Wrap title and estimate height in inches.

Wraps title text to multiple lines and estimates the vertical space required in inches based on number of lines and fontsize.

Parameters

titlestr

Original title text

max_chars_per_lineint, default=80

Maximum characters per line before wrapping

fontsizeint, default=18

Font size in points (standard matplotlib suptitle default)

Returns

wrapped_titlestr

Title with line breaks inserted

height_inchesfloat

Estimated height in inches required for the wrapped title

Examples

>>> wrapped, height = TitleLegendHelper.estimate_title_height(
...     "Short Title"
... )
>>> print(wrapped)
Short Title
>>> print(f"{height:.3f} inches")
0.200 inches
>>> wrapped, height = TitleLegendHelper.estimate_title_height(
...     "Very Long Title That Will Be Wrapped Into Multiple Lines",
...     max_chars_per_line=30
... )
>>> lines = wrapped.count('\n') + 1
>>> print(f"Lines: {lines}, Height: {height:.3f} inches")
Lines: 3, Height: 0.600 inches

Notes

Height calculation:

  • Converts fontsize from points to inches: fontsize / 72

  • Multiplies by 0.8 to account for line spacing factor

  • Total height = n_lines * (fontsize / 72) * 0.8

This is an approximation. Actual rendering may vary slightly depending on matplotlib backend and font metrics.

static compute_title_max_chars_from_width(subplot_width_inches: float) int

Compute maximum characters per line based on subplot width.

Calculates appropriate title wrapping length based on actual measured subplot width in inches. Uses conservative estimate of character width to ensure titles fit within subplot bounds.

Parameters

subplot_width_inchesfloat

Actual subplot width in inches (measured from GridSpec position)

Returns

int

Maximum characters per line, clamped to range [40, 150]

Examples

>>> # Narrow subplot (5 inches wide)
>>> max_chars = TitleLegendHelper.compute_title_max_chars_from_width(5.0)
>>> print(max_chars)
40
>>> # Medium subplot (10 inches wide)
>>> max_chars = TitleLegendHelper.compute_title_max_chars_from_width(10.0)
>>> print(max_chars)
80
>>> # Wide subplot (15 inches wide)
>>> max_chars = TitleLegendHelper.compute_title_max_chars_from_width(15.0)
>>> print(max_chars)
120

Notes

Character width estimation:

  • Assumes ~8 characters per inch at typical font sizes (conservative)

  • Includes safety margin to prevent text overflow

  • Clamps result to reasonable range [40, 150] characters

This estimate works well for proportional fonts at standard matplotlib title font sizes (14-18pt).

static add_title(fig: Figure, title: str | None, title_y: float = 0.98, default_title: str = 'Feature Importance Plot', fontsize: int = 18) None

Add figure-level title with consistent positioning.

Parameters

figFigure

Matplotlib figure to add title to

titlestr, optional

Custom title text. If None, uses default_title.

title_yfloat, default=0.98

Y-position of title (relative to figure height, 0-1)

default_titlestr, default=”Feature Importance Plot”

Fallback title when title is None

fontsizeint, default=18

Font size for title text

Returns

None

Modifies fig in place by adding suptitle

Examples

>>> fig = plt.figure()
>>> TitleLegendHelper.add_title(fig, "My Analysis")
>>> # With default title
>>> TitleLegendHelper.add_title(fig, None)
>>> # Custom positioning
>>> TitleLegendHelper.add_title(fig, "Title", title_y=0.95)

Notes

Title is added using fig.suptitle() with:

  • fontsize (configurable, default=18)

  • fontweight=’bold’

  • y=title_y for vertical positioning

static add_legend(fig: Figure, data_selector_colors: Dict[str, str], legend_title: str | None, legend_labels: Dict[str, str] | None, contact_threshold: float | None = None, legend_x: float = 0.98, legend_y: float = 0.94, fontsize: int = 14, title_fontsize: int = 16, additional_handles: List[Any] | None = None) None

Add figure-wide legend for DataSelectors and contact threshold.

Creates unified legend showing DataSelector names with their colors and optionally a contact threshold line. Legend is positioned in figure coordinates for consistent placement.

Parameters

figFigure

Matplotlib figure to add legend to

data_selector_colorsDict[str, str]

Mapping of DataSelector name to color hex code

legend_titlestr, optional

Custom legend title. If None, uses “DataSelectors”.

legend_labelsDict[str, str], optional

Custom display names for DataSelectors. Maps original names to display names. Example: {“cluster_0”: “Inactive”, “cluster_1”: “Active”}

contact_thresholdfloat, optional

Contact threshold value in Angstrom. If provided, adds a dashed red line entry to legend showing the threshold.

legend_xfloat, default=0.98

X-position of legend (in figure coordinates, 0-1)

legend_yfloat, default=0.94

Y-position of legend (in figure coordinates, 0-1)

fontsizeint, default=14

Font size for legend entries

title_fontsizeint, default=16

Font size for legend title

additional_handlesList, optional

Additional pre-built matplotlib legend handles appended to the DataSelector legend (for example vertical marker labels).

Returns

None

Modifies fig in place by adding legend

Examples

>>> colors = {"cluster_0": "#FF0000", "cluster_1": "#00FF00"}
>>> TitleLegendHelper.add_legend(fig, colors, None, None)
>>> # With custom labels and threshold
>>> labels = {"cluster_0": "Active", "cluster_1": "Inactive"}
>>> TitleLegendHelper.add_legend(
...     fig, colors, "States", labels,
...     contact_threshold=4.5
... )
>>> # Custom positioning
>>> TitleLegendHelper.add_legend(
...     fig, colors, "Clusters", None,
...     contact_threshold=None,
...     legend_x=0.95, legend_y=0.90
... )

Notes

Legend is created with:

  • Sorted DataSelector names (alphabetical)

  • Colored patches (alpha=0.7)

  • Optional contact threshold line (red, dashed)

  • fontsize (configurable, default=14)

  • title_fontsize (configurable, default=16), title in bold

  • framealpha=0.9 for visibility

  • loc=”upper left” with bbox_to_anchor for precise positioning

static get_side_legend_anchor(fig: Figure, rightmost_ax_first_row, gap_inches: float = 0.1, y_offset: float = 0.0) Tuple[float, float]

Compute side-legend anchor from the first row’s rightmost subplot.

Parameters

figFigure

Figure hosting the subplots.

rightmost_ax_first_rowmatplotlib.axes.Axes

Rightmost subplot in the first row.

gap_inchesfloat, default=0.1

Horizontal gap in inches between axes and legend.

y_offsetfloat, default=0.0

Additional offset in figure coordinates applied to y.

Returns

Tuple[float, float]

(legend_x, legend_y) in figure coordinates.