Download notebook (.ipynb)

Plot Tags#

Plot tags are short labels attached to a plot or composite figure. Tags are typically used for:

  • Figure numbering (Figure 1)

  • Panel labeling (A, B, C, …)

  • Hierarchical figure organization

  • Annotating composite plots

The tag text is specified via labs(tag='...') and can be customized via plot_tag* parameters in theme().

from lets_plot import *
import numpy as np
import pandas as pd

LetsPlot.setup_html()

Default appearance#

mpg = pd.read_csv('https://raw.githubusercontent.com/JetBrains/lets-plot-docs/master/data/mpg2.csv')
(ggplot(mpg) 
     + geom_point(aes('vehicle weight (lbs.)', 'miles per gallon', color='miles per gallon'), size=7) 
     + scale_color_viridis(option='B') 
     + ggsize(500, 280)
     + labs(color='MPG', 
            tag='Figure 1')   # <---
)

Annotating composite plots#

Each subplot is labeled using a tag for easy reference.

np.random.seed(7)
n = 500
x = np.random.normal(0, 1.0, n)
y = 0.7 * x + np.random.normal(0, 0.8, n)
grp = np.where(x + y > 0, 'G1', 'G2')
r = np.sqrt(x*x + y*y)

df = pd.DataFrame({'x': x, 'y': y, 'grp': grp, 'r': r})

base = (ggplot(df, aes('x', 'y')) + ggsize(320, 240)
    + theme(
        plot_tag=element_text(size=38, color='light_sky_blue'),
        plot_tag_location='panel',
        plot_tag_position=[0.15, 0.85]
    )
)

# A: correlation pattern (plain scatter + smooth)
pA = (
    base
    + geom_point(alpha=0.35)
    + geom_smooth(method='lm', se=True)
    + labs(title='Trend', subtitle='Linear relationship', tag='A'
      )
)

# B: group separation (color by group)
pB = (
    base
    + geom_point(aes(color='grp'), alpha=0.5)
    + labs(title='Groups', subtitle='Two classes split by x+y', color='Group', tag='B'
      )
)

# C: density / magnitude feature (map r to size)
pC = (
    base
    + geom_point(aes(size='r'), alpha=0.35)
    + labs(title='Magnitude', subtitle='Point size ~ distance from origin', size='r', tag='C'
      )
)

gggrid([pA, pB, pC], ncol=3, align=True, hspace=10) + ggsize(1020, 320)

Annotating composite plots and the composite figure#

You can add tags to mark subplots and also the composite plot itself.

subplot_theme = theme(
    plot_tag=element_text(size=32, color='gray'),
    plot_tag_location='plot',
    plot_tag_position='topleft',
    plot_tag_suffix=')',
    title=element_text(hjust=0.05)
)

(gggrid([pA+subplot_theme, pB+subplot_theme, pC+subplot_theme], ncol=3, align=True, hspace=10)
    + theme(
        plot_tag=element_text(size=24, color='gray'),
        plot_tag_location='margin',
        plot_tag_position='bottom'
    )
    + labs(tag='Figure 1')
    + ggsize(1020, 320)
)

plot_tag_location#

Defines where the tag is placed relative to the plot:

  • 'plot' - inside the full plot area

  • 'panel' - inside the data panel

  • 'margin' - in the outer margin; the layout is adjusted to prevent overlapping with other plot elements

base = (
    ggplot(mpg, aes(x='number of cylinders', y='miles per gallon'))
    + geom_boxplot()
    + labs(tag="A1")
    + theme_gray()
    + theme(
        plot_tag=element_text(color='blue', size=24),
        plot_background=element_rect(color="light_gray", size=1)
      )
    + ggsize(360, 200)
)

p_plot = base + theme(
    plot_tag_location='plot'
) + labs(subtitle="tag_location = 'plot'")

p_panel = base + theme(
    plot_tag_location='panel'
) + labs(subtitle="tag_location = 'panel'")

p_margin = base + theme(
    plot_tag_location='margin'
) + labs(subtitle="tag_location = 'margin'")

gggrid([p_plot, p_panel, p_margin], ncol=3, hspace=6) + ggsize(1000, 320)

plot_tag_position#

Defines the anchor point of the tag inside the selected alignment area.

Accepted values:

  • Named positions: 'topleft' (default), 'top', 'topright', 'left', 'right', 'bottomleft', 'bottom', 'bottomright'

  • Numeric position: [x, y] – normalized coordinates

    • (0, 0) = bottom-left

    • (1, 1) = top-right

When plot_tag_location='margin', only predefined position names are supported. Use hjust and vjust in element_text() to fine-tune alignment along the corresponding edge.

data = {'name': ['a', 'b', 'c', 'd', 'b'], 'value': [40, 90, 10, 50, 20]}

base = (
    ggplot(data)
    + geom_pie(aes(slice='value', fill='name'), stat='identity')
    + theme_void()
    + theme(
        plot_tag=element_text(size=24, color='red'),
        plot_background=element_rect(color="light_gray", size=1)
    )
)

p1 = base + labs(title='margin + top', tag='A') \
    + theme(plot_tag_location='margin', 
            plot_tag_position='top')

p2 = base + labs(title='panel + [0.8, 0.1]', tag='B') \
    + theme(plot_tag_location='panel', 
            plot_tag_position=[0.8, 0.1])

p3 = base + labs(title='plot + left', tag="C") \
    + theme(plot_tag_location='plot',
            plot_tag_position="left")

p4 = base + labs(title='margin + right', tag='D') \
    + theme(plot_tag_location='margin', 
            plot_tag_position='right'
           )

gggrid([p1, p2, p3, p4], ncol=2, align=True, hspace=16, vspace=12) + ggsize(600, 400)

plot_tag_prefix / plot_tag_suffix#

To avoid repeating common parts of a tag, use plot_tag_prefix and plot_tag_suffix.

(gggrid([base + labs(tag='1'), base + labs(tag='2')]) 
    + theme(plot_tag_location='margin', 
            plot_tag_position='bottom',
            plot_tag_prefix='Figure ',    # <---
            plot_tag_suffix='.'           # <---
           )
    + ggsize(400, 200)
)

Fine tuning#

When using plot_tag_location='plot' or 'panel', other plot elements may overlap the tag, or the tag may overlap them. The 'margin' option reserves space for the tag and you can tune how exactly other elements are relocated.

grid = pd.DataFrame([(i, j) for i in range(10) for j in range(7)], columns=['x', 'y'])
grid['v'] = np.sin(grid['x'] / 1.4) + np.cos(grid['y'] / 1.1)

base = (
    ggplot(grid, aes('x', 'y'))
    + geom_tile(aes(fill='v'))
    + scale_fill_gradient(low='steelblue', high='firebrick')
    + theme(plot_margin=[14, 14, 14, 14],
           plot_tag=element_text(size=18),
           plot_background=element_rect(color="lightgray", size=2)
       )
)

# 1. Default: plot + topleft (no layout shift)
p1 = (
    base
    + labs(
        title='Default: plot + topleft',
        subtitle='No layout shift (tag over plot area).',
        tag='Tag'
    )
    + theme(
        plot_tag_location='plot',
        plot_tag_position='topleft'
    )
)

# 2. margin + topleft (shifts both directions)
p2 = (
    base
    + labs(
        title='margin + topleft',
        subtitle='Shifts BOTH: top + left margins reserved.',
        tag='Tag'
    )
    + theme(
        plot_tag_location='margin',
        plot_tag_position='topleft'
    )
)

# 3. margin + top, hjust=0 (vertical shift only)
p3 = (
    base
    + labs(
        title='margin + top, hjust=0',
        subtitle='Shifts VERTICAL only.',
        tag='Tag'
    )
    + theme(
        plot_tag=element_text(hjust=0.0),
        plot_tag_location='margin',
        plot_tag_position='top'
    )
)

# 4. margin + left, vjust=1 (horizontal shift only)
p4 = (
    base
    + labs(
        title='margin + left, vjust=1',
        subtitle='Shifts HORIZONTAL only.',
        tag='Tag'
    )
    + theme(
        plot_tag=element_text(vjust=1.0),
        plot_tag_location='margin',
        plot_tag_position='left'
    )
)

gggrid([p1, p2, p3, p4], ncol=2, align=True, hspace=12, vspace=12) + ggsize(760, 560)