A Grammar of Graphics for Python providing a declarative approach to data visualization similar to R's ggplot2
—
Faceting creates multiple panels (subplots) to show different subsets of your data based on categorical variables. This powerful technique allows you to explore patterns across groups and compare distributions, trends, and relationships within different data segments. Plotnine provides flexible faceting functions that support both grid and wrap layouts.
Core functions for creating multi-panel plots based on data groupings.
def facet_null():
"""
No faceting (single panel).
This is the default faceting behavior - creates a single plot panel.
Useful for explicitly specifying no faceting when needed.
"""
def facet_wrap(facets, nrow=None, ncol=None, scales='fixed', shrink=True,
labeller='label_value', as_table=True, drop=True, dir='h'):
"""
Wrap 1D ribbon of panels into 2D layout.
Creates subplots arranged in a rectangular grid based on levels of
one or more variables.
Parameters:
- facets: str or list, variables to facet by (e.g., 'var' or ['var1', 'var2'])
- nrow: int, number of rows in grid
- ncol: int, number of columns in grid
- scales: str, whether scales are shared across panels
('fixed', 'free', 'free_x', 'free_y')
- shrink: bool, whether to shrink scales to fit data in each panel
- labeller: str or function, how to format facet labels
- as_table: bool, whether to fill panels like a table (top-to-bottom)
- drop: bool, whether to drop unused factor levels
- dir: str, direction to fill panels ('h' for horizontal, 'v' for vertical)
"""
def facet_grid(rows=None, cols=None, scales='fixed', shrink=True,
labeller='label_value', as_table=True, drop=True):
"""
Create 2D grid of panels based on row and column variables.
Creates a matrix of subplots where rows represent levels of one variable
and columns represent levels of another variable.
Parameters:
- rows: str or list, variable(s) for panel rows
- cols: str or list, variable(s) for panel columns
- scales: str, scale sharing ('fixed', 'free', 'free_x', 'free_y')
- shrink: bool, whether to shrink scales to fit data
- labeller: str or function, label formatting
- as_table: bool, whether to arrange like table (TRUE: top-to-bottom)
- drop: bool, whether to drop unused factor levels
"""Functions for controlling how facet labels are displayed and formatted.
def labeller(**kwargs):
"""
Create custom labelling functions for facets.
Parameters:
- **kwargs: variable-specific labellers or general labelling options
Usage:
- labeller(variable=label_both) for specific variable
- labeller(.default=label_both) for all variables
"""
def as_labeller(x, default=None):
"""
Convert various objects to labeller functions.
Parameters:
- x: dict, function, or other object to convert
- default: default labeller if conversion fails
Usage:
- as_labeller({'A': 'Group A', 'B': 'Group B'})
- as_labeller(lambda x: f'Value: {x}')
"""
def label_value(x):
"""
Default labeller that shows variable values only.
Displays just the factor level (e.g., 'A', 'B', 'C').
"""
def label_both(x):
"""
Labeller that shows both variable names and values.
Displays variable name and value (e.g., 'group: A', 'group: B').
"""
def label_context(x):
"""
Context-aware labelling.
Chooses appropriate labelling based on the faceting context.
"""# Facet by single variable
ggplot(data, aes(x='x', y='y')) + \
geom_point() + \
facet_wrap('category')
# Facet by multiple variables
ggplot(data, aes(x='x', y='y')) + \
geom_point() + \
facet_wrap(['category', 'group'])
# Control layout dimensions
ggplot(data, aes(x='x', y='y')) + \
geom_point() + \
facet_wrap('category', ncol=3)# Rows and columns for different variables
ggplot(data, aes(x='x', y='y')) + \
geom_point() + \
facet_grid(rows='treatment', cols='gender')
# Facet by rows only (creates vertical strip)
ggplot(data, aes(x='x', y='y')) + \
geom_point() + \
facet_grid(rows='category')
# Facet by columns only (creates horizontal strip)
ggplot(data, aes(x='x', y='y')) + \
geom_point() + \
facet_grid(cols='group')# Free scales for each panel
ggplot(data, aes(x='x', y='y')) + \
geom_point() + \
facet_wrap('category', scales='free')
# Free x-axis only
ggplot(data, aes(x='x', y='y')) + \
geom_point() + \
facet_wrap('category', scales='free_x')
# Free y-axis only
ggplot(data, aes(x='x', y='y')) + \
geom_point() + \
facet_wrap('category', scales='free_y')
# Fixed scales (default)
ggplot(data, aes(x='x', y='y')) + \
geom_point() + \
facet_wrap('category', scales='fixed')# Custom label dictionary
label_dict = {'A': 'Treatment A', 'B': 'Treatment B', 'C': 'Control'}
ggplot(data, aes(x='x', y='y')) + \
geom_point() + \
facet_wrap('treatment', labeller=as_labeller(label_dict))
# Show both variable name and value
ggplot(data, aes(x='x', y='y')) + \
geom_point() + \
facet_wrap('category', labeller=label_both)
# Custom labeller function
def custom_labeller(x):
return f'Group {x} Data'
ggplot(data, aes(x='x', y='y')) + \
geom_point() + \
facet_wrap('group', labeller=as_labeller(custom_labeller))# Multiple variables with custom layout
ggplot(data, aes(x='value', fill='category')) + \
geom_histogram(bins=20) + \
facet_wrap(['region', 'year'], ncol=4, scales='free_y') + \
theme(strip_text=element_text(size=8))
# Grid with different aspects
ggplot(data, aes(x='date', y='sales')) + \
geom_line() + \
facet_grid(rows='product', cols='quarter', scales='free') + \
theme(axis_text_x=element_text(angle=45))
# Time series by group
ggplot(time_data, aes(x='date', y='value', color='metric')) + \
geom_line() + \
facet_wrap('country', scales='free_y', ncol=3) + \
theme_minimal() + \
theme(legend_position='bottom')# Faceted plot with smoothing
ggplot(data, aes(x='x', y='y', color='treatment')) + \
geom_point(alpha=0.6) + \
geom_smooth(method='lm', se=False) + \
facet_wrap('site', scales='free') + \
scale_color_brewer(type='qual') + \
theme_bw()
# Box plots across multiple grouping variables
ggplot(data, aes(x='treatment', y='response', fill='treatment')) + \
geom_boxplot() + \
facet_grid(rows='timepoint', cols='gender') + \
scale_fill_manual(values=['lightblue', 'lightcoral']) + \
theme(legend_position='none')# Drop unused factor levels (default)
ggplot(data, aes(x='x', y='y')) + \
geom_point() + \
facet_wrap('category', drop=True)
# Keep unused factor levels (creates empty panels)
ggplot(data, aes(x='x', y='y')) + \
geom_point() + \
facet_wrap('category', drop=False)# Fill panels horizontally (default)
ggplot(data, aes(x='x', y='y')) + \
geom_point() + \
facet_wrap('category', nrow=2, dir='h')
# Fill panels vertically
ggplot(data, aes(x='x', y='y')) + \
geom_point() + \
facet_wrap('category', nrow=2, dir='v')
# Table-style layout (top-to-bottom, left-to-right)
ggplot(data, aes(x='x', y='y')) + \
geom_point() + \
facet_wrap('category', as_table=True)
# Non-table layout (bottom-to-top, left-to-right)
ggplot(data, aes(x='x', y='y')) + \
geom_point() + \
facet_wrap('category', as_table=False)# Density plots by group
ggplot(data, aes(x='value', fill='category')) + \
geom_density(alpha=0.7) + \
facet_wrap('region') + \
scale_fill_brewer(type='qual') + \
theme_minimal()
# Regression by groups
ggplot(data, aes(x='x', y='y')) + \
geom_point() + \
geom_smooth(method='lm', se=True) + \
facet_grid(rows='treatment', cols='timepoint') + \
theme_bw()# Create faceting variable on-the-fly
ggplot(data, aes(x='x', y='y')) + \
geom_point() + \
facet_wrap('value > median(value)', labeller=as_labeller({
'True': 'Above Median', 'False': 'Below Median'
}))# Different geoms highlight different aspects in each panel
ggplot(data, aes(x='x', y='y')) + \
geom_point(data=data.query('category == "A"')) + \
geom_line(data=data.query('category == "B"')) + \
facet_wrap('category', scales='free')Install with Tessl CLI
npx tessl i tessl/pypi-plotnine