Customizing Legend Key Size and Appearance¶
Filled 2D geoms can use override_aes to adjust legend key appearance:
sizechanges the legend key border width.size=0hides the legend key border.widthandheightchange the relative key size.
In [1]:
import pandas as pd
from lets_plot import *
LetsPlot.setup_html()
In [2]:
mpg = pd.read_csv('https://raw.githubusercontent.com/JetBrains/lets-plot-docs/refs/heads/master/data/mpg.csv')
mpg.head(3)
Out[2]:
| Unnamed: 0 | manufacturer | model | displ | year | cyl | trans | drv | cty | hwy | fl | class | |
|---|---|---|---|---|---|---|---|---|---|---|---|---|
| 0 | 1 | audi | a4 | 1.8 | 1999 | 4 | auto(l5) | f | 18 | 29 | p | compact |
| 1 | 2 | audi | a4 | 1.8 | 1999 | 4 | manual(m5) | f | 21 | 29 | p | compact |
| 2 | 3 | audi | a4 | 2.0 | 2008 | 4 | manual(m6) | f | 20 | 31 | p | compact |
size - Border Stroke Thickness¶
Each legend key for a filled 2D geom is a filled rectangle drawn with a border.
By default, the border uses the geom's color with a fixed width.
In [3]:
# Stacked bar: vehicle class counts broken down by drivetrain type.
p = (
ggplot(mpg, aes(x='class', fill='drv'))
+ geom_bar(color='black', size=1.6)
+ labs(x='vehicle class', fill='drivetrain')
+ ggsize(580, 340)
)
p + ggtitle('Default legend style')
Out[3]:
In [4]:
# Use `size` in `override_aes` to control the legend border thickness. You can make it match the plot.
p + guides(
fill=guide_legend(
override_aes={'size': 1.6}
)
) + ggtitle('Legend key stroke adjusted to match the plot')
Out[4]:
In [5]:
# Or you can hide the border completely by setting `size=0` to avoid visual clutter.
p + guides(
fill=guide_legend(
override_aes={'size': 0}
)
) + ggtitle('Borderless legend keys')
Out[5]:
In [6]:
# Prepare tile data
tile_df = (
mpg
.groupby(['class', 'drv'], as_index=False)
.agg(mean_hwy=('hwy', 'mean'))
)
tile_df['mileage'] = pd.cut(
tile_df['mean_hwy'],
bins=[0, 20, 25, 30, 40, 60],
labels=['up to 20', '20-25', '25-30', '30-40', '40+']
)
base_tile = (
ggplot(tile_df, aes('class', 'drv', fill='mileage'))
+ geom_tile(color='gray30', size=1.6, width=0.92, height=0.92)
+ scale_fill_brewer(palette='Pastel1')
+ labs(x='vehicle class', y='drive train', fill='mean highway mpg')
+ ggsize(760, 420)
+ theme(axis_text_x=element_text(angle=35, hjust=1))
)
In [7]:
# Some geoms, such as `geom_tile`, have borderless legend keys by default.
base_tile + ggtitle('Default legend keys')
Out[7]:
In [8]:
# Adding a contrast border makes the legend colors read closer to the plot.
(
base_tile
+ guides(
fill=guide_legend(
override_aes={'size': 0.8}
)
)
+ ggtitle("Legend key border adds visual contrast")
)
Out[8]:
White border¶
In [9]:
# A white geom border can make legend keys look smaller on a white background.
bar_df = mpg[mpg['class'].isin(['subcompact', 'compact', 'midsize', 'suv', 'pickup'])]
bar_plot = (ggplot(bar_df, aes('class', fill='drv'))
+ geom_bar(size=2.0)
+ scale_fill_brewer(palette='Set2')
+ ggtitle('Bar legend keys with a white border')
+ ggsize(620, 360)
)
bar_plot + guides(
fill=guide_legend(
override_aes={'size': 2.0}
)
)
Out[9]:
In [10]:
# In this case, decreasing `size` makes the visible filled area larger.
bar_plot + guides(
fill=guide_legend(
override_aes={'size': 0.5}
)
)
Out[10]:
Relative Width and Height¶
A horizontal legend often reads better with wider, flatter keys. Use width and height in override_aes to change the relative key size.
In [11]:
horizontal_tile = base_tile + theme(legend_position='bottom')
horizontal_tile + ggtitle('Horizontal legend with default key size')
Out[11]:
In [12]:
(
horizontal_tile
+ guides(fill=guide_legend(
override_aes={'width': 2.5, 'height': 0.8}
))
+ ggtitle('Horizontal legend with wider keys')
)
Out[12]:
Different Heights per Category¶
A list of height values can make the legend carry an extra ordered cue. Here, the key height follows engine size: 4, 6, and 8 cylinders.
In [13]:
cylinders = [4, 6, 8]
cylinder_labels = [f'{c} cylinders' for c in cylinders]
cyl_df = mpg[mpg['cyl'].isin(cylinders)].copy()
cyl_df['cylinders'] = pd.Categorical(
cyl_df['cyl'].map(lambda c: f'{c} cylinders'),
categories=cylinder_labels,
ordered=True
)
cyl_df = cyl_df.drop_duplicates(['class', 'cylinders'])
(
ggplot(cyl_df, aes('class', 'cylinders', fill='cylinders'))
+ geom_tile(color='white', size=0.5, width=0.9, height=0.9)
+ scale_fill_manual(
values=['chocolate', 'sea_green', 'royal_blue'],
breaks=cylinder_labels
)
+ guides(fill=guide_legend(
override_aes={'height': [0.8, 1.2, 1.6]} # <----
))
+ labs(x='vehicle class', y='engine size', fill='engine size')
+ ggtitle('Legend key height reflects cylinder count')
+ ggsize(760, 360)
+ theme(axis_text_x=element_text(angle=35, hjust=1), legend_key_spacing=4)
)
Out[13]: