Download notebook (.ipynb)

Plot Layout Scheme and Theme Parameters#

This demo illustrates the overall plot structure and the effect of various layout-related theme parameters. Understanding how they interact can help fine-tune the appearance and readability of your plots.

  • plot_margin adds outer space around the entire plot, including titles, captions, legends, and the plotting panel.

  • plot_title: margin, plot_subtitle: margin, plot_caption: margin, axis_title_x: margin, axis_title_y: margin control the spacing around the corresponding textual elements. These are passed via the margin argument to element_text().

  • axis_text_spacing_x, axis_text_spacing_y define the spacing between axis ticks and their associated labels.

  • panel_inset adds internal padding between the plotting panel and the axes, shrinking the effective plotting area.

  • plot_inset adds padding inside the plot area (including the plot panel, axes, and axis labels) and other textual elements.

  • legend_box_spacing controls the spacing between the legend section and the plot area.

import numpy as np

from lets_plot import *
LetsPlot.setup_html()
np.random.seed(42)

x = list(range(210, 1000, 97))

# Base rate increases with x (e.g., linear growth for lambda)
lambdas = [5 + (xi - 210) / 100 for xi in x]

y = [np.random.poisson(lam) * 30 + 90 for lam in lambdas]

data = {'x': x, 'y': y}
SCALE_FACTOR = 2
PAD = 11
def labeled_rect(xmin, xmax, ymin, ymax,
                 inner_text='', border_text='',
                 pad=PAD,
                 **kwargs):
    xmin_s = xmin * SCALE_FACTOR + pad
    xmax_s = xmax * SCALE_FACTOR - pad
    ymin_s = ymin * SCALE_FACTOR + pad
    ymax_s = ymax * SCALE_FACTOR - pad

    is_horizontal = (xmax - xmin > (ymax - ymin) / 2)

    rect_layer = geom_rect(
        xmin=xmin_s, xmax=xmax_s, ymin=ymin_s, ymax=ymax_s, size=pad - 3,
        **kwargs
    )

    # Inner text: centered
    x_center = (xmin_s + xmax_s) / 2
    y_center = (ymin_s + ymax_s) / 2
    inner_layer = geom_text(
        x=x_center, y=y_center, label=inner_text,
        angle=0 if is_horizontal else 90,
        color='darkgray'
    )

    # Border text: near top-right (horizontal) or top-left (vertical)
    if is_horizontal:
        x_border = xmax_s
        y_border = ymin_s + pad / 2 - 1
        angle = 0
        vjust = 0
    else:  # vertical
        x_border = xmin_s - pad / 2 - 5
        y_border = ymin_s
        angle = 90
        vjust = 1

    border_layer = geom_text(
        x=x_border, y=y_border, label=border_text,
        angle=angle, hjust=1, vjust=vjust,
        family='Courier',
        color='black'
    )

    return rect_layer + inner_layer + border_layer
def annotation_arrow(x, y, xend, yend, label, hjust):
    x_s = x * SCALE_FACTOR
    y_s = y * SCALE_FACTOR
    xend_s = xend * SCALE_FACTOR
    yend_s = yend * SCALE_FACTOR
    with_arrow = arrow(length=8, ends='last', type='open', angle=20)
    label_x = x_s - 16
    label_y = y_s + 8
    if hjust == 1:
        label_x = x_s + 24
        label_y = y_s - 10

    return geom_segment(x=x_s, y=y_s, xend=xend_s, yend=yend_s, arrow=with_arrow) + \
        geom_text(x=label_x, y=label_y, label=label, hjust=hjust, family='Courier', color='black')
def x_ticks(data, y_base=608, tick_length=8):
    ticks = {
        'x': data['x'],
        'xend': data['x'],
        'y': [y_base] * len(data['x']),
        'yend': [y_base + tick_length] * len(data['x']),
    }
    return geom_segment(aes(x='x', y='y', xend='xend', yend='yend'), data=ticks, color='gray50', size=1)


def y_ticks(ymin=208, ymax=574, n_ticks=6, x_base=168, tick_length=8):
    y_ticks = np.linspace(ymin, ymax, n_ticks)

    ticks = {
        'y': y_ticks,
        'yend': y_ticks,
        'x': [x_base] * n_ticks,
        'xend': [x_base + tick_length] * n_ticks,
    }

    return geom_segment(aes(x='x', y='y', xend='xend', yend='yend'), data=ticks, color='gray50', size=1)
COORDS = {
    'PLOT_XMIN': 0, 'PLOT_XMAX': 620, 'PLOT_YMIN': 0, 'PLOT_YMAX': 420,
    'PANEL_XMIN': 88, 'PANEL_XMAX': 506, 'PANEL_YMIN': 90, 'PANEL_YMAX': 304,
    'TITLE_YMIN': 11, 'TITLE_YMAX': 44.5,
    'SUBTITLE_YMIN': 44, 'SUBTITLE_YMAX': 78.5,
    'X_TITLE_YMIN': 346.5, 'X_TITLE_YMAX': 378,
    'Y_TITLE_XMIN': 11, 'Y_TITLE_XMAX': 44.5,
    'X_TEXT_XMIN': 98, 'X_TEXT_XMAX': 498, 'X_TEXT_YMIN': 320, 'X_TEXT_YMAX': 334,
    'Y_TEXT_XMIN': 56, 'Y_TEXT_XMAX': 72, 'Y_TEXT_YMIN': 98, 'Y_TEXT_YMAX': 294,
    'PLOT_INSET_XMIN': 44.5, 'PLOT_INSET_XMAX': 517, 'PLOT_INSET_YMIN': 79, 'PLOT_INSET_YMAX': 346,
    'CAPTION_YMIN': 378, 'CAPTION_YMAX': 410,
    'LEGEND_XMIN': 532, 'LEGEND_XMAX': 610, 'LEGEND_YMIN': 137, 'LEGEND_YMAX': 254,
    'LEGEND_SPACING_XMIN': 518, 'LEGEND_SPACING_XMAX': 532,
    'FRAME_XMIN': 86.5, 'FRAME_XMAX': 509.5, 'FRAME_YMIN': 86, 'FRAME_YMAX': 305.5,
}

plotting_area = labeled_rect(xmin=COORDS['PLOT_XMIN'], xmax=COORDS['PLOT_XMAX'],
                             ymin=COORDS['PLOT_YMIN'], ymax=COORDS['PLOT_YMAX'],
                             inner_text='', border_text='',
                             pad=1, fill='white', color='gold', alpha=0.0)

plot_margin = labeled_rect(xmin=COORDS['PLOT_XMIN'], xmax=COORDS['PLOT_XMAX'],
                           ymin=COORDS['PLOT_YMIN'], ymax=COORDS['PLOT_YMAX'],
                           inner_text='', border_text='plot_margin',
                           fill='white', color='lemon_chiffon', alpha=0.9)

title_area = labeled_rect(xmin=COORDS['PANEL_XMIN'], xmax=COORDS['PANEL_XMAX'],
                          ymin=COORDS['TITLE_YMIN'], ymax=COORDS['TITLE_YMAX'],
                          inner_text='TITLE', border_text='plot_title: margin',
                          fill='light_blue', color='light_blue', alpha=0.3)

subtitle_area = labeled_rect(xmin=COORDS['PANEL_XMIN'], xmax=COORDS['PANEL_XMAX'],
                             ymin=COORDS['SUBTITLE_YMIN'], ymax=COORDS['SUBTITLE_YMAX'],
                             inner_text='SUBTITLE', border_text='plot_subtitle: margin',
                             fill='light_green', color='light_green', alpha=0.3)

y_title_area = labeled_rect(xmin=COORDS['Y_TITLE_XMIN'], xmax=COORDS['Y_TITLE_XMAX'],
                            ymin=COORDS['PANEL_YMIN'], ymax=COORDS['PANEL_YMAX'],
                            inner_text='Y-AXIS TITLE', border_text='axis_title_y: margin',
                            fill='peach_puff', color='peach_puff', alpha=0.2)

x_title_area = labeled_rect(xmin=COORDS['PANEL_XMIN'], xmax=COORDS['PANEL_XMAX'],
                            ymin=COORDS['X_TITLE_YMIN'], ymax=COORDS['X_TITLE_YMAX'],
                            inner_text='X-AXIS TITLE', border_text='axis_title_x: margin',
                            fill='peach_puff', color='peach_puff', alpha=0.2)

panel_area = labeled_rect(xmin=COORDS['PANEL_XMIN'], xmax=COORDS['PANEL_XMAX'],
                          ymin=COORDS['PANEL_YMIN'], ymax=COORDS['PANEL_YMAX'],
                          inner_text='PLOT\nPANEL', border_text='panel_inset',
                          fill='gray', color='light_gray', alpha=0.5)

y_text_area = labeled_rect(xmin=COORDS['Y_TEXT_XMIN'], xmax=COORDS['Y_TEXT_XMAX'],
                           ymin=COORDS['Y_TEXT_YMIN'], ymax=COORDS['Y_TEXT_YMAX'],
                           inner_text='y-axis labels', border_text='',
                           pad=0, fill='pink', color='light_pink', alpha=0.3)

y_text_area2 = labeled_rect(xmin=COORDS['Y_TEXT_XMAX'], xmax=COORDS['Y_TEXT_XMAX'] + PAD + 1,
                            ymin=COORDS['Y_TEXT_YMIN'], ymax=COORDS['Y_TEXT_YMAX'],
                            inner_text='', border_text='axis_text_spacing_y',
                            fill='pink', color='light_pink', alpha=0.3)

x_text_area = labeled_rect(xmin=COORDS['X_TEXT_XMIN'], xmax=COORDS['X_TEXT_XMAX'],
                           ymin=COORDS['X_TEXT_YMIN'], ymax=COORDS['X_TEXT_YMAX'],
                           inner_text='x-axis labels', border_text='',
                           pad=0, fill='pink', color='light_pink', alpha=0.3)

x_text_area2 = labeled_rect(xmin=COORDS['X_TEXT_XMIN'], xmax=COORDS['X_TEXT_XMAX'],
                            ymin=COORDS['X_TEXT_YMIN'] - PAD - 1, ymax=COORDS['X_TEXT_YMAX'] - PAD - 4,
                            inner_text='', border_text='axis_text_spacing_x',
                            fill='pink', color='light_pink', alpha=0.3)

caption_area = labeled_rect(xmin=COORDS['PANEL_XMIN'], xmax=COORDS['PANEL_XMAX'],
                            ymin=COORDS['CAPTION_YMIN'], ymax=COORDS['CAPTION_YMAX'],
                            inner_text='CAPTION', border_text='plot_caption: margin',
                            fill='sky_blue', color='sky_blue', alpha=0.3)

legend_area = labeled_rect(xmin=COORDS['LEGEND_XMIN'], xmax=COORDS['LEGEND_XMAX'],
                           ymin=COORDS['LEGEND_YMIN'], ymax=COORDS['LEGEND_YMAX'],
                           pad=1, inner_text='LEGEND', border_text='',
                           fill='chocolate', color='chocolate', alpha=0.1)

legend_spacing = labeled_rect(xmin=COORDS['LEGEND_SPACING_XMIN'], xmax=COORDS['LEGEND_SPACING_XMAX'],
                              ymin=COORDS['LEGEND_YMIN'], ymax=COORDS['LEGEND_YMAX'],
                              pad=11, inner_text='', border_text='legend_box_spacing',
                              fill='gray93', color='gray93')

plot_inset_area = labeled_rect(xmin=COORDS['PLOT_INSET_XMIN'], xmax=COORDS['PLOT_INSET_XMAX'],
                               ymin=COORDS['PLOT_INSET_YMIN'], ymax=COORDS['PLOT_INSET_YMAX'],
                               inner_text='', border_text='plot_inset',
                               fill='white', color='plum', alpha=0.5)
ggplot(data, aes('x', 'y')) + \
plot_margin + \
plotting_area + \
plot_inset_area + \
title_area + \
subtitle_area + \
y_title_area + \
y_text_area + y_text_area2 + \
x_title_area + \
x_text_area + x_text_area2 + \
panel_area + \
caption_area + \
legend_spacing + \
legend_area + \
annotation_arrow(x=529, y=322, xend=500, yend=306, label='axis tick area', hjust=0) + \
annotation_arrow(x=75, y=74, xend=86, yend=97, label='axis tick area', hjust=1) + \
theme_void() + \
scale_y_reverse() + \
geom_point(color='dodgerblue', alpha=1) + geom_line(color='dodgerblue', alpha=1) + \
x_ticks(data) + \
y_ticks() + \
ggsize(COORDS['PLOT_XMAX'] * SCALE_FACTOR, COORDS['PLOT_YMAX'] * SCALE_FACTOR)