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)