UltraPlot
UltraPlot is an object-oriented superset of matplotlib (a maintained fork of
proplot supporting mpl 3.9+). Every matplotlib axes method still works; UltraPlot
adds a unified .format() styling method, smarter subplots(), location-shortcut
colorbars/legends, constructor functions, and auto-layout.
Most of UltraPlot is close enough to matplotlib that you can guess it. This skill documents the conventions and edges that are easy to get subtly wrong — all verified against ultraplot 2.3.
Core conventions (get these right first)
- Import as
uplt, notpltand notpplt:import ultraplot as uplt. (ppltis the old proplot alias; docs/old code may use it butupltis current.) - Don't use the pyplot interface. There is no
uplt.plot()/uplt.gca()/uplt.show()-driven state machine. Always go through explicit figure/axes objects. - Style with
.format(), notset_*calls. Oneax.format(...)/fig.format(...)call replaces dozens ofset_xlabel/set_title/set_xlim/ tick calls.set_*methods still work, but.format()is the idiom.
import numpy as np
import ultraplot as uplt
fig, axs = uplt.subplots(nrows=2, ncols=2, share=True, refwidth=2.0)
fig.format(suptitle="Title", xlabel="x", ylabel="y", abc="a)", abcloc="ul")
axs[0].plot(np.random.rand(20, 3), cycle="538", labels=["a", "b", "c"], legend="ll")
m = axs[3].pcolormesh(np.random.rand(20, 20), cmap="magma")
fig.colorbar(m, loc="r", label="value")
fig.save("out.png", dpi=200)
Figure creation
| Call | Returns |
|---|---|
uplt.subplots(nrows=, ncols=, ...) |
(Figure, SubplotGrid) |
uplt.subplots(array=[[1,1],[2,3]]) |
(Figure, SubplotGrid) — mosaic layout |
uplt.subplot(...) (singular) |
(Figure, CartesianAxes) — one bare axes |
uplt.figure(...) then fig.add_subplots(...) / fig.add_subplot(...) |
Figure, then grid / axes |
subplots()ALWAYS returns aSubplotGrid, even for one subplot. Sofig, axs = uplt.subplots(ncols=1)makesaxsa grid — index it asaxs[0]to get the axes. Use the singularuplt.subplot()when you want one bare axes.- Sizing is automatic. Prefer
refwidth/refheight(per-subplot reference size) +refaspect; the figure size and inter-subplot spacing are computed by auto-layout. Usefigwidth/figheightonly to force the overall size. - Units are flexible on every size arg: numbers in
subplots/format default to inches for figure dims and font-relativeemfor subplot/spacing dims; strings accept'cm','mm','in','pt','em'(e.g.refwidth='4cm',wspace='1em').
SubplotGrid indexing (common trap)
SubplotGrid is a list-like container. Indexing is not numpy-like:
| Index | Result |
|---|---|
axs[i] (1D int) |
a bare axes (CartesianAxes) |
axs[i, j] (2D int) |
a 1-element SubplotGrid, NOT a bare axes |
axs[r, :], axs[:, c], axs[a:b] |
a SubplotGrid |
To get a single axes object, use flat 1D integer indexing axs[i]. 2D and slice
indexing return grids. Methods broadcast over a grid (axs.format(...),
axs[0, :].plot(...) all work), so axs[i, j].plot() works — but axs[i, j] is a
grid, so anything expecting a single Artist/Axes back will misbehave.
The .format() method
Works on axes, figures, and grids. Common keywords (all optional):
ax.format(
title="t", ltitle=..., rtitle=..., ultitle=..., urtitle=..., # corner titles
xlabel="x", ylabel="y", xlim=(0, 10), ylim=..., xscale="log",
xlocator="maxn", xformatter="sci", # string shortcuts -> constructors
xticks=[0, 5, 10], xticklabels=[...], xtickdir="inout", xticklen=5,
xreverse=False, xmargin=0.05,
grid=True, gridminor=True,
abc="a)", abcloc="ul", # auto subplot labels: a) b) c) ...
)
fig.format(
suptitle="figure title",
leftlabels=["row1", "row2"], toplabels=["col1", "col2"], # edge super-labels
# ...also accepts every axes-format kwarg, applied to all subplots
)
axs.format(xlabel="shared", abc="A.") # broadcasts to all axes in the grid
abc=True(or a template like'a)','A.','(a)') turns on automatic subplot labels;abclocplacement uses corner codes'ul' 'uc' 'ur' 'll' 'lc' 'lr'.xlocator/xformatter/xscaleaccept the same strings/tuples as the constructor functions below (e.g.xlocator=('maxn', 5)).
Colorbars & legends (location shortcuts)
ax.colorbar(...), fig.colorbar(...), ax.legend(...), fig.legend(...) all take
a loc of single-letter / corner codes — not matplotlib's 'upper right' style
(though those still work for legends):
| Code | Meaning |
|---|---|
'l' 'r' 't' 'b' |
outer left / right / top / bottom |
'ul' 'ur' 'll' 'lr' |
inset corners |
'c' / 'best' |
centered / auto inset |
m = ax.pcolormesh(z, cmap="viridis")
ax.colorbar(m, loc="r", label="v", length=0.8, width="1.5em")
fig.colorbar(m, loc="r", label="v") # figure-spanning colorbar
ax.legend(loc="ll", ncols=1) # from labeled artists
fig.legend(loc="b", ncols=3)
Easiest path: pass colorbar=/legend= (a loc code) straight to the plotting
command and skip the separate call:
ax.pcolormesh(z, cmap="magma", colorbar="r")
ax.plot(y, labels=["a", "b", "c"], legend="ul")
Constructor functions
These turn strings/tuples/lists into matplotlib objects; the same inputs are
accepted inline by plotting commands and .format().
uplt.Colormap("viridis", left=0.1, right=0.9, reverse=True) # truncate/reverse/edit
uplt.Colormap(["red", "blue"]) # build from colors
uplt.Cycle("538"); uplt.Cycle("viridis", 5); uplt.Cycle(["r","g","b"])
uplt.Norm("diverging", vcenter=0); uplt.Norm(("power", 2)); uplt.Norm("log")
uplt.Locator(("maxn", 5)); uplt.Locator("log")
uplt.Formatter("sci"); uplt.Formatter("frac"); uplt.Formatter("percent")
uplt.Scale(("power", 2)); uplt.Proj("ortho", central_latitude=45)
Colormaps, cycles, colors
Registered on import (case-insensitive; append _r to reverse). Verified names:
- Perceptually-uniform sequential:
viridis,magma,plasma,inferno,cividis. - Diverging:
rdbu,coolwarm,piyg,brbg,spectral. - Color cycles (pass to
cycle=):default,colorblind,colorblind10,538,ggplot,seaborn,bmh,tab10,tab20,Set1,Qual1,Qual2.
List what's available at runtime: uplt.show_cmaps(), uplt.show_cycles(),
uplt.show_colors(), uplt.show_fonts(). Programmatic registry: uplt.colormaps.
Plotting commands (PlotAxes enhancements)
Standard matplotlib commands (plot, scatter, pcolormesh, contourf, ...) plus
extras like ax.heatmap(...). Enhancements:
- Pass
cmap=,cycle=,norm=,levels=,colorbar=,legend=,labels=directly to the plot call. - Multi-column
y→ one line per column (withlabels=[...]per column). - pandas/xarray inputs auto-label axes/legend from names/coords.
Axis sharing & spanning, panels, insets
uplt.subplots(nrows=2, ncols=2,
share=True, # share=sharex=sharey level: 0/False .. 3/True, 4/'all', 'auto'
span=True, # single centered label spanning a row/column (spanx/spany)
align=True) # align labels across subplots (alignx/aligny)
ax.panel("r", width="3em") # attached panel: 'l'/'r'/'t'/'b' (alias panel_axes)
ax.inset([0.5, 0.5, 0.4, 0.4]) # inset in axes-fraction coords (alias inset_axes)
Share levels: 0/False none · 1/'labels' labels only · 2/'limits' limits+ticks ·
3/True + hide inner ticklabels · 4/'all' across all rows/cols · 'auto'.
Geographic & polar axes
fig, ax = uplt.subplots(proj="ortho", proj_kw=dict(central_latitude=45)) # needs cartopy
ax.format(land=True, coast=True, borders=True,
lonlim=(-60, 60), latlim=(-30, 30), lonlocator=20, latlocator=20,
longrid=True, latgrid=True)
fig, ax = uplt.subplots(proj="polar") # PolarAxes, no cartopy needed
ax.format(thetalim=(0, 360), rlim=(0, 1), thetalocator=30, rlabelpos=45)
Geo projections ('ortho', 'merc', 'moll', 'robin', ...) require cartopy;
without it subplots(proj=...) raises. proj="polar" always works.
rc configuration
uplt.rc["font.size"] = 11 # dict access (dotted keys)
uplt.rc.update({"axes.grid": True, "grid.alpha": 0.4})
with uplt.rc.context(fontsize=12, linewidth=1.5): # underscore aliases; auto-reverts
fig, ax = uplt.subplots()
uplt.rc.reset()
Loads defaults → ~/.config/ultraplotrc → ./.ultraplotrc (later overrides earlier).
Saving
fig.save("out.pdf", dpi=300) or fig.savefig(...) (alias). Default format pdf,
default dpi 1000.
Common mistakes
| Mistake | Fix |
|---|---|
import ultraplot as plt / as pplt |
import ultraplot as uplt |
uplt.plot(...) / pyplot state machine |
explicit fig, ax = uplt.subplots(); ax.plot(...) |
Treating axs[i, j] as a single axes |
it's a 1-element grid; use flat axs[i] for one axes |
Assuming subplots(ncols=1) gives a bare axes |
it gives a SubplotGrid; use axs[0] or uplt.subplot() |
ax.colorbar(m, "r") positionally |
ax.colorbar(m, loc="r") |
Many ax.set_xlabel/set_title/set_xlim calls |
one ax.format(xlabel=, title=, xlim=) |
| Manual "a) b) c)" text | fig.format(abc="a)", abcloc="ul") |
proj="ortho" errors |
install cartopy (or use proj="polar" which needs none) |