A Grammar of Graphics for Python providing a declarative approach to data visualization similar to R's ggplot2
—
Position adjustments modify the position of geometric objects to handle overlapping data points, create specialized layouts, and improve plot readability. They are essential for bar charts, box plots, and other visualizations where multiple data points share the same coordinate space.
Core position adjustment functions for handling overlapping and grouped data.
def position_identity():
"""
No position adjustment (default for most geoms).
Objects are plotted at their exact data coordinates without any adjustment.
Use when you want precise positioning or when overlapping is desired.
"""
def position_dodge(width=None):
"""
Dodge overlapping objects side-by-side.
Places overlapping objects next to each other horizontally. Essential
for grouped bar charts, box plots, and other grouped visualizations.
Parameters:
- width: float, width of dodging in data units. Default uses 80% of
the resolution of the data (0.8 * smallest difference between values)
Usage: Grouped bar charts, side-by-side box plots, grouped points
"""
def position_dodge2(width=None, preserve='total', padding=0.1, reverse=False):
"""
Enhanced dodging with additional control options.
Improved version of position_dodge with better handling of different
group sizes and more control over positioning behavior.
Parameters:
- width: float, dodging width
- preserve: str, what to preserve when dodging ('total' or 'single')
- padding: float, padding between groups
- reverse: bool, whether to reverse the order of groups
"""
def position_stack():
"""
Stack overlapping objects on top of each other.
Cumulative positioning where each object is placed on top of the previous one.
Standard for stacked bar charts and area plots.
Usage: Stacked bar charts, stacked area plots, cumulative histograms
"""
def position_fill():
"""
Stack objects and normalize to 100%.
Like position_stack, but normalizes the total height to 1.0 (100%),
showing proportions rather than absolute values.
Usage: Proportional stacked bar charts, percentage area plots
"""Position adjustments that add controlled randomness to reduce overplotting.
def position_jitter(width=None, height=None, seed=None):
"""
Add random noise to positions to reduce overplotting.
Adds small random offsets to point positions to reveal overlapping
data points. Essential for discrete variables with many observations.
Parameters:
- width: float, amount of horizontal jittering (data units)
- height: float, amount of vertical jittering (data units)
- seed: int, random seed for reproducible jittering
Default widths/heights are 40% of the resolution of the data.
Usage: Scatter plots with discrete variables, strip charts
"""
def position_jitterdodge(jitter_width=None, jitter_height=None, dodge_width=None,
seed=None):
"""
Combine dodging and jittering.
First dodges groups side-by-side, then adds jittering within each group.
Useful for grouped data with many overlapping points.
Parameters:
- jitter_width: float, width of jittering within groups
- jitter_height: float, height of jittering
- dodge_width: float, width of dodging between groups
- seed: int, random seed
Usage: Grouped scatter plots, grouped strip charts
"""Functions for making small manual adjustments to object positions.
def position_nudge(x=0, y=0):
"""
Nudge objects by constant offsets.
Applies fixed offsets to object positions. Useful for fine-tuning
placement of labels, annotations, or separating overlapping elements.
Parameters:
- x: float, horizontal offset in data units
- y: float, vertical offset in data units
Usage: Label positioning, annotation adjustments, creating separation
"""# Default stacked bar chart
ggplot(data, aes(x='category', y='value', fill='group')) + \
geom_col(position='stack') # or position_stack()
# Side-by-side grouped bars
ggplot(data, aes(x='category', y='value', fill='group')) + \
geom_col(position='dodge') # or position_dodge()
# Proportional stacked bars (100% stacked)
ggplot(data, aes(x='category', y='value', fill='group')) + \
geom_col(position='fill') # or position_fill()
# Custom dodge width
ggplot(data, aes(x='category', y='value', fill='group')) + \
geom_col(position=position_dodge(width=0.7))# Basic jittered scatter plot
ggplot(data, aes(x='group', y='value')) + \
geom_point(position='jitter') # or position_jitter()
# Custom jitter amounts
ggplot(data, aes(x='treatment', y='response')) + \
geom_point(position=position_jitter(width=0.2, height=0))
# Reproducible jittering
ggplot(data, aes(x='category', y='measurement')) + \
geom_point(position=position_jitter(seed=123))
# Box plot with jittered points
ggplot(data, aes(x='group', y='value')) + \
geom_boxplot() + \
geom_point(position=position_jitter(width=0.2), alpha=0.6)# Grouped box plots
ggplot(data, aes(x='treatment', y='response', fill='gender')) + \
geom_boxplot(position='dodge')
# Grouped points with dodging and jittering
ggplot(data, aes(x='category', y='value', color='group')) + \
geom_point(position=position_jitterdodge(dodge_width=0.8, jitter_width=0.2))
# Enhanced dodging for uneven groups
ggplot(data, aes(x='category', y='value', fill='treatment')) + \
geom_col(position=position_dodge2(preserve='single'))# Nudge labels away from points
ggplot(data, aes(x='x', y='y', label='name')) + \
geom_point() + \
geom_text(position=position_nudge(y=0.1))
# Nudge in both directions
ggplot(data, aes(x='score1', y='score2', label='student')) + \
geom_point() + \
geom_text(position=position_nudge(x=0.5, y=-0.2))
# Different nudging for different groups
ggplot(data, aes(x='x', y='y', label='text', color='group')) + \
geom_point() + \
geom_text(data=data.query('group == "A"'),
position=position_nudge(y=0.1)) + \
geom_text(data=data.query('group == "B"'),
position=position_nudge(y=-0.1))# Stacked bars with error bars
summary_data = data.groupby(['category', 'group']).agg({
'value': ['mean', 'std']
}).reset_index()
ggplot(summary_data, aes(x='category', y='value_mean', fill='group')) + \
geom_col(position='stack') + \
geom_errorbar(aes(ymin='value_mean - value_std',
ymax='value_mean + value_std'),
position=position_dodge(width=0.9), width=0.2)
# Multiple position adjustments in layers
ggplot(data, aes(x='treatment', y='response', fill='gender')) + \
geom_boxplot(position='dodge', alpha=0.7) + \
geom_point(position=position_jitterdodge(), alpha=0.3) + \
stat_summary(fun='mean', geom='point',
position=position_dodge(width=0.75),
color='red', size=3)# Stacked histogram
ggplot(data, aes(x='value', fill='group')) + \
geom_histogram(position='stack', bins=20)
# Side-by-side histogram (dodged)
ggplot(data, aes(x='value', fill='group')) + \
geom_histogram(position='dodge', bins=20)
# Identity position for overlapping histograms
ggplot(data, aes(x='value', fill='group')) + \
geom_histogram(position='identity', alpha=0.6, bins=20)
# Fill position for proportional histogram
ggplot(data, aes(x='value', fill='group')) + \
geom_histogram(position='fill', bins=20)# Dodged points in time series
ggplot(time_data, aes(x='date', y='value', color='series')) + \
geom_point(position=position_dodge(width=1)) + \
geom_line(aes(group='series'))
# Jittered time points to reduce overlap
ggplot(events_data, aes(x='timestamp', y='category', color='type')) + \
geom_point(position=position_jitter(height=0.1))# Mean and error bars with dodging
ggplot(data, aes(x='treatment', y='response', fill='gender')) + \
stat_summary(fun='mean', geom='col', position='dodge') + \
stat_summary(fun_data='mean_se', geom='errorbar',
position=position_dodge(width=0.9), width=0.2)
# Violin plots with dodging
ggplot(data, aes(x='group', y='measurement', fill='condition')) + \
geom_violin(position='dodge') + \
geom_boxplot(position=position_dodge(width=0.9), width=0.1)# Fine-tune dodging width
ggplot(data, aes(x='category', y='value', fill='group')) + \
geom_col(position=position_dodge(width=0.8)) + \
geom_text(aes(label='value'),
position=position_dodge(width=0.8), vjust=-0.5)
# Control jitter seed for reproducibility
set.seed(123) # For consistent jittering across runs
ggplot(data, aes(x='treatment', y='response')) + \
geom_boxplot() + \
geom_point(position=position_jitter(width=0.3, seed=123), alpha=0.6)
# Combine multiple position adjustments
ggplot(data, aes(x='category', y='value', color='group')) + \
geom_point(position=position_jitterdodge(
jitter_width=0.2, jitter_height=0.1, dodge_width=0.8
), alpha=0.7) + \
stat_summary(fun='mean', geom='point', size=3,
position=position_dodge(width=0.8))# For categorical x with continuous y and groups:
# - position_dodge: Compare groups side-by-side
# - position_stack: Show totals and contributions
# - position_fill: Show proportions
# For overplotting:
# - position_jitter: Discrete variables
# - position_identity with alpha: Continuous variables
# For labels:
# - position_nudge: Small manual adjustments
# - position_dodge: Align with dodged geoms# Use same dodge width across layers
dodge_width = 0.8
ggplot(data, aes(x='group', y='value', fill='treatment')) + \
geom_col(position=position_dodge(width=dodge_width)) + \
geom_errorbar(aes(ymin='value - se', ymax='value + se'),
position=position_dodge(width=dodge_width), width=0.2) + \
geom_text(aes(label='label'),
position=position_dodge(width=dodge_width), vjust=-0.5)Install with Tessl CLI
npx tessl i tessl/pypi-plotnine