What’s new?#
v2.4.1: UltraPlot v2.4.1: figure state fixes and subplot manager groundwork (2026-07-14)#
Maintenance release. Bug fixes and internal restructuring; no new public API.
Fixes
- Figure.clear() no longer leaves stale state behind. A cleared figure kept handing out destroyed axes via subplotgrid and _iter_axes, held on to the old gridspec and subplot counter, leaked figure panels, and raised AttributeError from the next format(suptitle=...). clear() (and its clf() alias) now resets subplots, panels, layout flags, and the figure-level label artists, so a cleared figure is reusable. (#760)
- Sensible defaults for missing font symbols. (#752)
Internal
- Subplot creation, gridspec ownership, and projection parsing moved out of Figure into a dedicated SubplotManager, the first step toward Figure as a thin interface over focused collaborators (#677). Public API is unchanged, but the private Figure._subplot_dict, _subplot_counter, and _gridspec attributes are gone — use Figure.subplotgrid / Figure.gridspec instead. (#759, #698)
- ultraplot.ui now derives projection keywords from SubplotManager, fixing uplt.subplot(proj=...) silently routing the projection to the figure. (#760)
Release plumbing
- Zenodo archiving is handled by the Zenodo GitHub integration; the version and DOI are no longer hand-maintained in CITATION.cff. (#761)
What's Changed
- [hotfix] add defaults for missing symbols (https://github.com/Ultraplot/UltraPlot/pull/752)
- Chore: refactor figure with new subplot manager (#698) (https://github.com/Ultraplot/UltraPlot/pull/759)
- Zenodo fix (https://github.com/Ultraplot/UltraPlot/pull/761)
- Zenodo fix 2 (https://github.com/Ultraplot/UltraPlot/pull/762)
- Fix Figure.clear leaving stale state and decouple ui kwarg routing (https://github.com/Ultraplot/UltraPlot/pull/760)
Full Changelog: https://github.com/Ultraplot/UltraPlot/compare/v2.4.0...v2.4.1
v2.4.0: Taylor diagrams, inset colorbars, improved semantic legends (2026-07-04)#
UltraPlot v2.4.0
New Features
- Taylor Diagram Projection (TaylorAxes): Added a brand new polar-style axes projection for Taylor diagrams (
proj='taylor'). - Implemented helper plotting methods:
plot_corrandscatter_corrto plot points using correlation coefficient and standard-deviation coordinates. - Added documentation guide, examples gallery, and integration test coverage.
Code Snippet
```python models = ("Control", "Physics A", "Physics B", "Ensemble") correlation = np.array([0.73, 0.84, 0.91, 0.96]) stddev = np.array([0.82, 1.18, 1.05, 0.93]) colors = ("blue7", "orange7", "green7", "violet7") fig, ax = uplt.subplots(proj="taylor", refwidth=4.2) ax.format( title="Model skill summary", xlabel="Standard deviation", ylabel="", corrlabel="Correlation", rlim=(0, 1.5), rlines=0.25, corrlines=(1, 0.95, 0.9, 0.8, 0.6, 0.4, 0.2, 0), ) ## Centered RMS-difference contours around the reference point at (corr=1, std=1). theta = np.linspace(0, np.pi / 2, 160) radius = np.linspace(0, 1.5, 160) theta_grid, radius_grid = np.meshgrid(theta, radius) rms = np.sqrt(1 + radius_grid**2 - 2 * radius_grid * np.cos(theta_grid)) contours = ax.contour( theta_grid, radius_grid, rms, levels=(0.25, 0.5, 0.75, 1.0, 1.25), cmap="tokyo", lw=0.9, ls="--", ) ax.clabel(contours, levels=(0.5, 1.0), inline=True, fontsize=8, fmt="%.1f") ax.plot_corr(1, 1, marker="*", markersize=12, color="red7", label="Reference") for name, corr, std, color in zip(models, correlation, stddev, colors): ax.scatter_corr( corr, std, s=75, color=color, edgecolor="white", lw=0.8, zorder=4, label=name, ) ax.legend(loc="b", ncols=3, frame=False) fig.show() ```- Side-Attached Inset Colorbars: Enabled colorbars to attach to the sides of inset axes.
- Side colorbar requests now map dynamically to side-appropriate default orientations relative to the inset.
- Added support for stacking and aligned placement similar to standard axes colorbars.
Code Snippet
```python import ultraplot as uplt fig, ax = uplt.subplots() inset = ax.inset([0.5, 0.5, 0.4, 0.4]) ## Attach colorbar directly to the side of the inset axes rather than standard subplot panels inset.colorbar(mappable, loc="right", label="Value") ```- Axes Styling Enhancements:
- Allowed
axesec/axesedgecolorandaxeslw/axeslinewidthaliases to control global axes boundary/frame styling. - Added the parameter mapping
size/sizesaliases to scatter-plotsparameter for matching collection interfaces.
Bug Fixes
- Frame Style Retention: Preserved explicit axes frame styling across layout reformatting passes.
- Title Space Calculation: Fixed space reserving calculations for external container titles (
ExternalAxesContainer) to prevent unwanted overlapping when using ABC-style sub-labels. - Legend Handle Formatting: Fixed single-point Line2D handlers in legends to correctly hide line connectors for marker-only plots.
- Single Axis Title Sharing: Prevented alignment crashes on figures when sharing titles for single Cartesian/Geographic axis systems.
Maintenance & Internals
- Actions updates: Bumped
actions/checkout,actions/cache, andcodecov/codecov-actionin CI. - Metadata: Cleaned up styling/formatting in
CITATION.cff.
What's Changed
- Allow colorbars to attach to inset axes (https://github.com/Ultraplot/UltraPlot/pull/738)
- Abc title space fix (https://github.com/Ultraplot/UltraPlot/pull/741)
- Preserve explicit axes frame styling across reformatting (https://github.com/Ultraplot/UltraPlot/pull/742)
- Implement TaylorAxes (https://github.com/Ultraplot/UltraPlot/pull/743)
- Bump the github-actions group with 3 updates (https://github.com/Ultraplot/UltraPlot/pull/748)
- Align the parameters to be more in line with user expectation (https://github.com/Ultraplot/UltraPlot/pull/746)
- Automatic inference for semantic legends for marker/scatter size (https://github.com/Ultraplot/UltraPlot/pull/749)
Full Changelog: https://github.com/Ultraplot/UltraPlot/compare/v2.3.0...v2.4.0
What's Changed
- Allow colorbars to attach to inset axes (https://github.com/Ultraplot/UltraPlot/pull/738)
- Abc title space fix (https://github.com/Ultraplot/UltraPlot/pull/741)
- Preserve explicit axes frame styling across reformatting (https://github.com/Ultraplot/UltraPlot/pull/742)
- Implement TaylorAxes (https://github.com/Ultraplot/UltraPlot/pull/743)
- Bump the github-actions group with 3 updates (https://github.com/Ultraplot/UltraPlot/pull/748)
- Align the parameters to be more in line with user expectation (https://github.com/Ultraplot/UltraPlot/pull/746)
- Automatic inference for semantic legends for marker/scatter size (https://github.com/Ultraplot/UltraPlot/pull/749)
Full Changelog: https://github.com/Ultraplot/UltraPlot/compare/v2.3.0...v2.4.0
v2.3.0: UltraPlot v2.3.0: Enhanced semantic legends, improved polar labels placements, and geo label fixing (2026-06-10)#
This release introduces significant enhancements to the semantic legend system, improved geographic plotting formatting, and various bug fixes and performance improvements.
Enhanced Semantic Legends
The semantic legend system has been unified and expanded. You can now create legends from semantic mappings with even more control over marker styles, including custom paths, CapStyle, JoinStyle, and arbitrary transforms.
Example: Custom Marker Styles
import matplotlib.transforms as mtransforms
import numpy as np
from matplotlib.markers import CapStyle, JoinStyle, MarkerStyle
from matplotlib.path import Path
import ultraplot as uplt
star = Path.unit_regular_star(6)
circle = Path.unit_circle()
star_path = Path.unit_regular_star(5)
cut_star = Path(
vertices=np.concatenate([circle.vertices, star.vertices[::-1, ...]]),
codes=np.concatenate([circle.codes, star.codes]),
)
fig, ax = uplt.subplots()
## upper left legend with custom mark
ax.catlegend(
["star", "cus_star"],
marker=[star_path, cut_star],
markersize=10,
add=True,
loc="ul",
title="Paths",
ncols=1,
)
## upper right legend with advanced CapStyle and JoinStyle
ax.catlegend(
["butt / round", "round / miter", "projecting / bevel"],
marker="1",
markersize=10,
markeredgecolor=list("gbr"),
markeredgewidth=4,
markerfacecoloralt="none",
marker_capstyle=[
CapStyle.butt,
CapStyle.round,
CapStyle.projecting,
],
marker_joinstyle=[
JoinStyle.round,
JoinStyle.miter,
JoinStyle.bevel,
],
marker_transform=[mtransforms.Affine2D().rotate_deg(x) for x in [0, 30, 60]],
title="Cap & Join Style",
add=True,
loc="ur",
ncols=1,
)
## center geolegend with different styles
ax.geolegend(
["rect", "tri", "hex", "AU"],
facecolor=["tab:red", "r", "k", "tab:blue"],
ec=["k", "g", "orange", "bright pink"],
loc="c",
title="geolegend",
ew=[0.5, 2, 1, 0.5],
markersize=10,
ncols=4,
handletextpad=0.1,
columnspacing=0.7,
)
## lower left legend with TeX symbols and rotation transform
ax.catlegend(
["\\infty", "\\sum", "\\int"],
marker=[r"$\infty$", r"$\sum$", r"$\int$"],
s=[6, 18, 9], # ms/markersize=[6,8,10]
title="TeX symbols\nwith rotation",
marker_transform=[mtransforms.Affine2D().rotate_deg(x) for x in [30, 90, 45]],
add=True,
loc="ll",
ncols=1,
)
## lower right legend with different fill style
ax.catlegend(
["top", "bottom", "left", "right"],
marker="o",
markersize=10,
mfc=["r", "g", "b", "c"],
markerfacecoloralt="lightsteelblue",
markeredgecolor=["k", "r", "y", "b"],
fillstyle=["top", "bottom", "left", "right"],
title="Half filled",
add=True,
loc="lr",
ncols=1,
)
ax.axis("off")
fig.show()
Geographic Plotting Improvements
Fixed an issue where geographic grid label styling options (like labelsize) were silently ignored when formatting through SubplotGrid.format() or Figure.format().
Example: Geographic Formatting
import ultraplot as uplt
import cartopy.crs as ccrs
fig, axs = uplt.subplots(proj="merc", ncols=2)
## styling labelsize now works correctly through Figure.format
fig.format(
labels=True,
labelsize=14,
labelweight="bold",
grid=True,
coast=True
)
fig.show()
Polar Label Improvements
Polar axes now support curved polar-aware axis labels via thetalabel and rlabel. These labels follow the outer theta arc or a radial spoke, respect sector and annular layouts, and stay correctly offset under theta transforms and redraws. This work also finishes the removal of generic x/y label handling from polar formatting.
Example: Polar Axis Labels
import ultraplot as uplt
fig, ax = uplt.subplots(proj="polar")
ax.format(
thetalim=(0, 120),
rlim=(0.3, 1.0),
thetalabel="Azimuth",
rlabel="Radius",
thetalabelloc=60,
rlabelloc="left",
)
fig.show()
Bug Fixes and General Improvements
Various bug fixes including resolved int/list size errors in bar plots and consistent style application ordering.
Example: Bar Plot fix for pandas Series
import ultraplot as uplt
import pandas as pd
import numpy as np
data = pd.Series(np.random.rand(5), index=list("abcde"))
fig, ax = uplt.subplots()
ax.bar(data, color="blue7") # Previously might trigger size error
ax.format(title="Fixed Pandas Series Bar Plot")
fig.show()
What's Changed
- Example/semantic legend rm suffix (#735) by @lukas-schoen-qut
- Add example of semantic plot to gallery (#734) by @lukas-schoen-qut
- Unify semantic legend params. (#727) by @lukas-schoen-qut
- Fix ordering of applying styles (#725) by @lukas-schoen-qut
- Fix int/list has no size error, for bar plot of pd.Series (#732) by @lukas-schoen-qut
- Change rectangle to non-square for geolegend (#730) by @lukas-schoen-qut
- Fix duplicate import in colors.py (#728) by @lukas-schoen-qut
- Fix geographic grid label styling in SubplotGrid/Figure.format (#724) by @lukas-schoen-qut
- Add polar-aware
thetalabel/rlabelsupport and remove generic x/y label handling from polar format by @lukas-schoen-qut
What's Changed
- Chore: remove hardcoded comp with main for running tests (https://github.com/Ultraplot/UltraPlot/pull/700)
- Improve docs search ranking for API queries (https://github.com/Ultraplot/UltraPlot/pull/701)
- Fix: panel axis upgraded when sharing axes. (https://github.com/Ultraplot/UltraPlot/pull/704)
- Fix uncertainty legend glyphs for errorbar-based mean plots (https://github.com/Ultraplot/UltraPlot/pull/705)
- Fix scaling of title (https://github.com/Ultraplot/UltraPlot/pull/709)
- Bump softprops/action-gh-release from 2 to 3 in the github-actions group (https://github.com/Ultraplot/UltraPlot/pull/710)
- Temporarily disable the Zenodo release job (https://github.com/Ultraplot/UltraPlot/pull/712)
- Fix bar tick labels for xarray DataArray with string coordinate (https://github.com/Ultraplot/UltraPlot/pull/711)
- Add extra ultraplot styles (https://github.com/Ultraplot/UltraPlot/pull/719)
- Suppress sharing warnings when no sharing is possible (https://github.com/Ultraplot/UltraPlot/pull/715)
- Fix render backend issues for animating graphs (https://github.com/Ultraplot/UltraPlot/pull/720)
- Feature: figure semantic legends (https://github.com/Ultraplot/UltraPlot/pull/707)
- docs: add AI contribution policy (https://github.com/Ultraplot/UltraPlot/pull/662)
- Fix tick visibility leaking from styles in alternative axes (https://github.com/Ultraplot/UltraPlot/pull/721)
- Feat true black dark bg (https://github.com/Ultraplot/UltraPlot/pull/722)
- Fix GeoAxes grid label formatting through SubplotGrid and Figure format dispatch (https://github.com/Ultraplot/UltraPlot/pull/724)
- [pre-commit.ci] pre-commit autoupdate (https://github.com/Ultraplot/UltraPlot/pull/733)
- Fix duplicate import in colors.py (https://github.com/Ultraplot/UltraPlot/pull/728)
- Change rectangle to non-square for geolegend (https://github.com/Ultraplot/UltraPlot/pull/730)
- Fix int/list has no size error, for bar plot of pd.Series (https://github.com/Ultraplot/UltraPlot/pull/732)
- Fix ordering of applying styles (https://github.com/Ultraplot/UltraPlot/pull/725)
- Unify semantic legend params. (https://github.com/Ultraplot/UltraPlot/pull/727)
- Add example of semantic plot to gallery (https://github.com/Ultraplot/UltraPlot/pull/734)
- Example/semantic legend rm suffix (https://github.com/Ultraplot/UltraPlot/pull/735)
- Add polar-aware rlabel and thetalabel support (https://github.com/Ultraplot/UltraPlot/pull/714)
New Contributors
- @kinyatoride made their first contribution in https://github.com/Ultraplot/UltraPlot/pull/711
Full Changelog: https://github.com/Ultraplot/UltraPlot/compare/v2.2.0...v2.3.0
v2.2.0: UltraPlot 2.2.0: Precision Placement — colorbars that span, norms that flex, labels that stay (2026-04-20)#
UltraPlot v2.2.0
What's New
Spanning colorbars across subplot slots
Colorbars can now span a specific range of columns or rows using the span parameter, rather than stretching across the entire figure edge. This gives much finer control over colorbar placement in multi-panel figures.
Example
import ultraplot as uplt
import numpy as np
rng = np.random.default_rng(42)
data = rng.random((20, 20))
fig, axs = uplt.subplots(nrows=2, ncols=3, share=False)
for ax in axs:
m = ax.pcolormesh(data, cmap="batlow")
## A single colorbar spanning only the first two columns
fig.colorbar(m, loc="bottom", span=(1, 2), label="Shared metric")
axs.format(
suptitle="Spanning colorbar across selected columns",
abc="[a.]",
grid=False,
)
Flexible normalization inputs
Norms can now be specified as strings alongside vmin/vmax kwargs, or as compact tuple/list specs like ('linear', 0.1, 0.9). Previously, passing a string norm with explicit vmin/vmax raised an error.
Example
import ultraplot as uplt
import numpy as np
rng = np.random.default_rng(0)
data = rng.random((30, 30))
fig, axs = uplt.subplots(ncols=3, share=False)
## String norm with explicit vmin/vmax kwargs
axs[0].pcolormesh(data, norm="linear", vmin=0.2, vmax=0.8, cmap="fire")
axs[0].format(title="String + vmin/vmax")
## Tuple form bundles everything together
axs[1].pcolormesh(data, norm=("linear", 0.2, 0.8), cmap="fire")
axs[1].format(title="Tuple form")
## Works with log norms too
axs[2].pcolormesh(data + 0.01, norm=("log", 0.01, 1), cmap="fire")
axs[2].format(title="Log tuple form")
axs.format(suptitle="Flexible norm specifications", abc="[a.]", grid=False)
Bug Fixes
Title border path effects properly cleared
Disabling titleborder=False now correctly removes the stroke effect from title text. Previously, calling ax.format(titleborder=False) after a title border had been applied would leave the border visible.
Example
import ultraplot as uplt
import numpy as np
rng = np.random.default_rng(0)
fig, axs = uplt.subplots(ncols=2)
for ax in axs:
ax.pcolormesh(rng.random((20, 20)), cmap="batlow")
## Left: border on (default for inset titles)
axs[0].format(title="With border", titleloc="upper left", titleborder=True)
## Right: border explicitly off — now correctly removed
axs[1].format(title="Without border", titleloc="upper left", titleborder=False)
axs.format(suptitle="Title border toggle fix", grid=False)
Outer legends no longer hide shared tick labels
Adding an outer legend (loc='r') no longer suppresses y-tick labels on neighboring axes when using sharey='labs'. The hidden panel backing the legend was incorrectly being counted as a sharing participant.
Example
import ultraplot as uplt
import numpy as np
x = np.linspace(0, 4 * np.pi, 200)
fig, axs = uplt.subplots(ncols=3, sharey="labs")
for i, ax in enumerate(axs):
for j in range(3):
ax.plot(x, np.sin(x + j) * (i + 1), label=f"Wave {j+1}")
## Outer legend on the middle panel — y-tick labels stay visible on all axes
axs[1].legend(loc="r")
axs.format(
suptitle="Outer legend with shared y-labels",
xlabel="Phase",
ylabel="Amplitude",
abc="[a.]",
)
Other Changes
- Zenodo publishing fix — corrected metadata for DOI generation (#686)
- Figure initialization refactor — internal cleanup of figure setup (#687)
- What's New page generation fix — documentation build improvements (#697)
Full Changelog: https://github.com/Ultraplot/UltraPlot/compare/v2.1.9...v2.2.0
What's Changed
- Hotfix/publish zenodo fix (https://github.com/Ultraplot/UltraPlot/pull/686)
- Refactor init figure (https://github.com/Ultraplot/UltraPlot/pull/687)
- Feature/span cbar slot based (https://github.com/Ultraplot/UltraPlot/pull/688)
- Fix patheffects affecting recall of titleborder (https://github.com/Ultraplot/UltraPlot/pull/691)
- Fix outer legend hiding y-tick labels with sharey='labs' (#694) (https://github.com/Ultraplot/UltraPlot/pull/696)
- Fix/whats new page generation (https://github.com/Ultraplot/UltraPlot/pull/697)
- Fix/norm inputs (https://github.com/Ultraplot/UltraPlot/pull/693)
Full Changelog: https://github.com/Ultraplot/UltraPlot/compare/v2.1.9...v2.2.0
v2.1.9: UltraPlot v2.1.9: bugs, nans, and improved title sharing. (2026-04-14)#
With v2.1.9 we add nan support for curved_quiver, and allow for using axes slicing to set titles.
Flexible title setting through axes slicing
We intend to enhance capabilities to offer strong and emphatic controls to the user. The format method gives a succinct localized entry point to format matplotlib axes. We extend the functionality that we added to colorbars and legend by now allowing titles to be spannend across subgroupings.
snippet
import ultraplot as uplt
fig, ax =uplt.subplots(ncols = 3, nrows = 2)
ax[0, :2].format(title = "Hello world!")
fig.show()
What's Changed
- Bump the github-actions group with 2 updates (https://github.com/Ultraplot/UltraPlot/pull/671)
- [pre-commit.ci] pre-commit autoupdate (https://github.com/Ultraplot/UltraPlot/pull/674)
- Feature: Add nan support for curved_quiver (https://github.com/Ultraplot/UltraPlot/pull/676)
- Use format() for shared subplot slice titles (https://github.com/Ultraplot/UltraPlot/pull/652)
- Fix: axes aspect shifting on pixel snapping after drawn (https://github.com/Ultraplot/UltraPlot/pull/680)
- Fix regression of spanning colorbars (https://github.com/Ultraplot/UltraPlot/pull/681)
Full Changelog: https://github.com/Ultraplot/UltraPlot/compare/v2.1.5...v2.1.9
What's Changed
- Chore: redo zenodo sync (https://github.com/Ultraplot/UltraPlot/pull/685)
Full Changelog: https://github.com/Ultraplot/UltraPlot/compare/v2.1.8...v2.1.9
v2.1.5: UltraPlot v2.1.5: Choropleth, Custom labels semantic plots, and bug fixes (2026-03-30)#
UltraPlot v2.1.5
The biggest additions are richer semantic size legends and first-class choropleth support for geographic axes, alongside typing, plotting, CI, and documentation improvements.
Highlights
Custom labels for Axes.sizelegend
sizelegend can now describe marker magnitudes in domain language instead of
just echoing the raw numeric levels.
Snippet
import numpy as np
import ultraplot as uplt
np.random.seed(42)
cities = [
"Tokyo",
"Delhi",
"Shanghai",
"Sao Paulo",
"Mumbai",
"Cairo",
"Beijing",
"Dhaka",
"Osaka",
"Lagos",
"Istanbul",
"London",
]
population = np.array(
[37.4, 32.9, 29.2, 22.4, 21.7, 21.3, 20.9, 23.2, 19.1, 16.6, 15.8, 9.5]
)
gdp_pc = np.array([42, 8, 23, 12, 7, 4, 22, 3, 38, 3, 14, 55])
growth = np.array([0.2, 2.8, 0.5, 0.7, 1.1, 1.9, 0.4, 3.1, 0.1, 3.5, 1.4, 0.8])
fig, ax = uplt.subplots(refwidth=4.5, refaspect=1.1)
ax.scatter(
gdp_pc,
growth,
s=population * 12,
c="cherry red",
edgecolor="gray8",
linewidth=0.5,
alpha=0.85,
absolute_size=True,
)
for i, city in enumerate(cities):
offset = (5, 5)
if city == "Osaka":
offset = (5, -10)
elif city == "Beijing":
offset = (-5, 8)
ax.annotate(
city,
(gdp_pc[i], growth[i]),
fontsize=6,
textcoords="offset points",
xytext=offset,
color="gray8",
)
ax.sizelegend(
[10 * 12, 20 * 12, 35 * 12],
labels={10 * 12: "10M", 20 * 12: "20M", 35 * 12: "35M"},
title="Population",
loc="ur",
frameon=False,
color="gray6",
edgecolor="gray8",
)
ax.format(
title="Megacities: Wealth vs Growth",
xlabel="GDP per capita (k USD)",
ylabel="Annual growth rate (%)",
xgrid=True,
ygrid=True,
xlim=(-2, 62),
ylim=(-0.3, 4.2),
)
fig.show()
GeoAxes.choropleth for thematic maps
You can now color countries and polygon features directly from numeric values while keeping the same UltraPlot formatting and colorbar workflow used on cartesian plots.
Snippet
import numpy as np
import ultraplot as uplt
values = {
"United States of America": 83.6,
"Canada": 81.7,
"Mexico": 75.1,
"Brazil": 75.9,
"Argentina": 76.7,
"United Kingdom": 81.0,
"France": 82.5,
"Germany": 80.9,
"Italy": 83.5,
"Spain": 83.4,
"Norway": 83.2,
"Sweden": 83.0,
"Russia": 73.2,
"China": 78.2,
"Japan": 84.8,
"South Korea": 83.7,
"India": 70.8,
"Australia": 83.3,
"New Zealand": 82.1,
"South Africa": 64.9,
"Nigeria": 53.9,
"Egypt": 72.1,
"Saudi Arabia": 76.5,
"Turkey": 76.0,
"Indonesia": 71.9,
"Thailand": 78.7,
}
fig, ax = uplt.subplots(proj="merc", proj_kw={"lon0": 10}, refwidth=5.5)
m = ax.choropleth(
values,
country=True,
cmap="Glacial",
vmin=50,
vmax=88,
edgecolor="none",
linewidth=0,
colorbar="b",
colorbar_kw={"label": "Life expectancy (years)", "length": 0.7},
missing_kw={"facecolor": "gray8", "hatch": "///", "edgecolor": "gray5"},
)
ax.format(
title="Global Life Expectancy (2023)",
land=True,
landcolor="gray2",
ocean=True,
oceancolor="gray1",
coast=True,
coastcolor="gray4",
coastlinewidth=0.3,
borders=True,
borderscolor="gray4",
borderslinewidth=0.2,
longrid=False,
latgrid=False,
)
fig.show()
Other changes
- Better static-analysis support for the lazy top-level API.
- Numeric scatter plots with explicit numeric colors now respect
cmap. - Shared boxplot tick labels no longer duplicate.
SubplotGridsingle-item 2D slices now keep returningSubplotGrid.- Helper and release-metadata coverage expanded and the CI flow was tightened.
What's Changed
- Honor cmap for numeric scatter colors (https://github.com/Ultraplot/UltraPlot/pull/616)
- Fix release metadata and Zenodo flow (https://github.com/Ultraplot/UltraPlot/pull/620)
- Publish Zenodo releases via API (https://github.com/Ultraplot/UltraPlot/pull/625)
- Support Python 3.10 TOML loading (https://github.com/Ultraplot/UltraPlot/pull/626)
- Bump dorny/paths-filter from 3 to 4 in the github-actions group (https://github.com/Ultraplot/UltraPlot/pull/624)
- Add choropleth support to GeoAxes (https://github.com/Ultraplot/UltraPlot/pull/623)
- CI: re-add Codecov upload (https://github.com/Ultraplot/UltraPlot/pull/633)
- Fix duplicate shared boxplot tick labels (https://github.com/Ultraplot/UltraPlot/pull/630)
- CI: restore PR Codecov uploads (https://github.com/Ultraplot/UltraPlot/pull/635)
- add pytest tag (https://github.com/Ultraplot/UltraPlot/pull/637)
- Increase coverage to 85% with targeted tests (https://github.com/Ultraplot/UltraPlot/pull/636)
- Docs: cache jupytext conversion and restore incremental html (https://github.com/Ultraplot/UltraPlot/pull/603)
- Improve gallery widget and thumbnail backgrounds (https://github.com/Ultraplot/UltraPlot/pull/644)
- Support custom labels in sizelegend (https://github.com/Ultraplot/UltraPlot/pull/629)
- fix: Refresh outdated contributor setup instructions (https://github.com/Ultraplot/UltraPlot/pull/646)
- Refresh constructor registries after ticker reload (https://github.com/Ultraplot/UltraPlot/pull/645)
- Honor patch linewidth rc for edgefix (https://github.com/Ultraplot/UltraPlot/pull/649)
- Add typing block for inspection (https://github.com/Ultraplot/UltraPlot/pull/659)
- Fix issue where single object returns object itself (https://github.com/Ultraplot/UltraPlot/pull/666)
- Fix/gridspec indexing (https://github.com/Ultraplot/UltraPlot/pull/667)
- Fix choropleth horizontal line artifacts on projected maps (https://github.com/Ultraplot/UltraPlot/pull/668)
New Contributors
- @JiwaniZakir made their first contribution in https://github.com/Ultraplot/UltraPlot/pull/646
Full Changelog: https://github.com/Ultraplot/UltraPlot/compare/v2.1.3...v2.1.5
What's Changed
- Honor cmap for numeric scatter colors (https://github.com/Ultraplot/UltraPlot/pull/616)
- Fix release metadata and Zenodo flow (https://github.com/Ultraplot/UltraPlot/pull/620)
- Publish Zenodo releases via API (https://github.com/Ultraplot/UltraPlot/pull/625)
- Support Python 3.10 TOML loading (https://github.com/Ultraplot/UltraPlot/pull/626)
- Bump dorny/paths-filter from 3 to 4 in the github-actions group (https://github.com/Ultraplot/UltraPlot/pull/624)
- Add choropleth support to GeoAxes (https://github.com/Ultraplot/UltraPlot/pull/623)
- CI: re-add Codecov upload (https://github.com/Ultraplot/UltraPlot/pull/633)
- Fix duplicate shared boxplot tick labels (https://github.com/Ultraplot/UltraPlot/pull/630)
- CI: restore PR Codecov uploads (https://github.com/Ultraplot/UltraPlot/pull/635)
- add pytest tag (https://github.com/Ultraplot/UltraPlot/pull/637)
- Increase coverage to 85% with targeted tests (https://github.com/Ultraplot/UltraPlot/pull/636)
- Docs: cache jupytext conversion and restore incremental html (https://github.com/Ultraplot/UltraPlot/pull/603)
- Improve gallery widget and thumbnail backgrounds (https://github.com/Ultraplot/UltraPlot/pull/644)
- Support custom labels in sizelegend (https://github.com/Ultraplot/UltraPlot/pull/629)
- fix: Refresh outdated contributor setup instructions (https://github.com/Ultraplot/UltraPlot/pull/646)
- Refresh constructor registries after ticker reload (https://github.com/Ultraplot/UltraPlot/pull/645)
- Honor patch linewidth rc for edgefix (https://github.com/Ultraplot/UltraPlot/pull/649)
- Add typing block for inspection (https://github.com/Ultraplot/UltraPlot/pull/659)
- Fix issue where single object returns object itself (https://github.com/Ultraplot/UltraPlot/pull/666)
New Contributors
- @JiwaniZakir made their first contribution in https://github.com/Ultraplot/UltraPlot/pull/646
Full Changelog: https://github.com/Ultraplot/UltraPlot/compare/v2.1.3...v2.1.5
v2.1.3 (2026-03-11)#
This is a small patch release focused on plotting and legend fixes.
Highlights
-
Restored
frame/frameonhandling for colorbars. Outer colorbars now again respectframeas a backwards-compatible alias for outline visibility, and inset colorbars no longer fail during layout reflow whenframe=False. -
Preserved hatching in geometry legend proxies. Legends generated from geographic geometry artists now carry hatch styling through to the legend handle, alongside facecolor, edgecolor, linewidth, and alpha.
-
Enabled graph plotting on 3D axes. This restores graph plotting support for 3D plots.
Other changes
- Updated GitHub Actions dependencies in the workflow configuration.
Included pull requests
#605Enable graph plotting on 3D axes#610Restore colorbar frame handling#612Preserve hatches in geometry legend proxies#604GitHub Actions dependency updates
Full Changelog: https://github.com/Ultraplot/UltraPlot/compare/V2.1.2...v2.1.3
v2.1.2: Fix colorbar framing and extra legend entries on slicing (2026-02-26)#
What's Changed
- Internal: cache inspect.signature used by pop_params (https://github.com/Ultraplot/UltraPlot/pull/596)
- Bugfix: Deduplicate spanning axes in SubplotGrid slicing (https://github.com/Ultraplot/UltraPlot/pull/598)
- Fix inset colorbar frame reflow for refaspect (https://github.com/Ultraplot/UltraPlot/pull/593)
- Exclude ultraplot/demos.py from coverage reports (https://github.com/Ultraplot/UltraPlot/pull/602)
- Fix contour level color mapping with explicit limits (https://github.com/Ultraplot/UltraPlot/pull/599)
Full Changelog: https://github.com/Ultraplot/UltraPlot/compare/V2.1.0...V2.1.2
v2.1.0: Tricontour fix projections (2026-02-25)#
This release hotfixes two bugs.
1. It fixes a bug where the dpi would be changed by external packages that create figures using matplotlib axes
2. It fixes a bug where the projection was assumed to be PlateCaree for tri-related functions
What's Changed
- Update build-states to new test-map.yml (https://github.com/Ultraplot/UltraPlot/pull/590)
- Fix/preserve dpi draw without rendering (https://github.com/Ultraplot/UltraPlot/pull/591)
- Fix cartopy tri default transform for Triangulation inputs (https://github.com/Ultraplot/UltraPlot/pull/595)
Full Changelog: https://github.com/Ultraplot/UltraPlot/compare/v2.0.1...V2.1.0
v2.0.1: New Plot Types, Semantic Legends, More flexible Colorbars and Smarter Layouts (2026-02-18)#
UltraPlot v2.0.1
UltraPlot v2.0.1 is our biggest release yet. Since v1.72.0, we have rebuilt core parts of the library around semantic legends, more reliable layout behavior, stronger guide architecture, and a much more stable CI pipeline. We also launched a brand-new documentation site at https://ultraplot.readthedocs.io/ with a gallery that gives a bird’s-eye view of Matplotlib’s key capabilities through UltraPlot. On the performance side, import times are significantly lower thanks to a new lazy-loading system. And for complex figure composition, sharing logic is now smarter about which axes should be linked, so multi-panel layouts behave more predictably with less manual tweaking.
snippet
import numpy as np
import ultraplot as uplt
rng = np.random.default_rng(7)
fig, ax = uplt.subplots(refwidth=4, refheight = 2)
t = np.linspace(0.0, 8.0 * np.pi, 700)
signal = 0.50 * np.sin(t) + 0.20 * np.sin(0.35 * t + 0.8)
trend = 0.55 * np.cos(0.50 * t)
ax.plot(t, signal, c="blue7", lw=2.2, label="Signal", zorder=-1)
ax.plot(t, trend, c="gray6", lw=1.4, ls="--", alpha=0.8, label="Trend", zorder=-1)
ax.fill_between(t, signal - 0.11, signal + 0.11, color="blue2", alpha=0.30, lw=0)
cats = np.array(["A", "B", "C"])
cat_markers = {"A": "o", "B": "s", "C": "^"}
cat_colors = {"A": "blue7", "B": "orange7", "C": "green7"}
n = 85
xp = np.sort(rng.choice(t, size=n, replace=False))
cp = rng.choice(cats, size=n, p=[0.35, 0.40, 0.25])
yp = np.interp(xp, t, signal)
amp = np.interp(xp, t, np.abs(signal))
sizes = 24 + 220 * amp
score = np.clip(0.15 + 0.85 * amp + 0.07 * rng.normal(size=n), 0, 1)
for cat in cats:
mask = cp == cat
ax.scatter(
xp[mask],
yp[mask],
c=score[mask],
cmap="viko",
vmin=0,
vmax=1,
s=sizes[mask],
marker=cat_markers[cat],
ec="black",
lw=0.45,
alpha=0.9,
)
ax.curvedtext(
t,
signal + 0.16,
"UltraPlot v2.0",
ha="center",
va="bottom",
color="black",
size=10,
weight="bold",
)
ax.format(
title="Semantic Legends + Curved Text + Smart Layout",
xlabel="Phase",
ylabel="Amplitude",
xlim=(0, 8 * np.pi),
ylim=(-1.2, 1.2),
grid=True,
gridalpha=0.22,
)
ax.catlegend(
cats,
colors=cat_colors,
markers=cat_markers,
line=False,
loc="l",
title="Category",
frameon=False,
handle_kw={"ms": 8.5, "ec": "black", "mew": 0.8},
ncols=1,
)
ax.sizelegend(
[25, 90, 180],
color="gray7",
loc="b",
align="l",
title="Magnitude",
frameon=False,
handle_kw={"ec": "black", "linewidths": 0.8},
)
ax.numlegend(
vmin=0,
vmax=1,
n=5,
cmap="viko",
loc="r",
align="b",
title="Score",
frameon=False,
handle_kw={"edgecolors": "black", "linewidths": 0.4},
ncols=1,
)
ax.entrylegend(
[
("Reference", {"line": True, "lw": 2.2, "ls": "-", "c": "blue7"}),
("Samples", {"line": False, "m": "o", "ms": 7, "fc": "white", "ec": "black"}),
],
loc="r",
align="t",
title="Glyph key",
frameon=False,
ncols=1,
)
inax = ax.inset((0.75, 0.75, 0.2, 0.2), zoom=0, projection="ortho")
inax.format(land=1, ocean=1, landcolor="mushroom", oceancolor="ocean blue")
fig.show()
Highlights
- New Layout Solver. We have replaced the layout solver to provide snappier, and better layout handling to make the even tighter.
- New semantic legend system with categorical, size, numeric, and geographic legend builders (#586).
- New legend primitives: LegendEntry and improved legend handling for wedge/pie artists (#571).
- Major legend internals refactor via a dedicated UltraLegend builder (#570).
- Colorbar architecture refactor: colorbars are now decoupled from axes internals through UltraColorbar and UltraColorbarLayout (#529).
New Features
- Top-aligned ribbon flow plot type (#559).
- Curved annotation support (#550).
- Ridgeline histogram histtype support (#557).
- Compatibility-aware auto-share defaults (#560).
- PyCirclize integration for circular/network workflows (#495).
Layout, Rendering, and Geo Improvements
- Multiple UltraLayout fixes for spanning axes, gaps, and shared labels (#555, #532, #584).
- Improved inset colorbar frame handling (#554 and related follow-ups).
- Better suptitle spacing in non-bottom vertical alignments (#574).
- Polar tight-layout fixes (#534).
- Geo tick/label robustness improvements (#579, related geo labeling fixes).
- Opt-in subplot pixel snapping plus follow-up adjustments (#561, #567).
Stability, Tooling, and Compatibility
- Python 3.14 support (#385).
- Improved CI matrix coverage and determinism (#587, #580, #577, #545 and related CI fixes).
- pytest-mpl style/baseline stabilization and improved test selection behavior (#528, #533, #535).
- Docs and theme updates, including warnings cleanup and presentation improvements (#585, #552).
Upgrade Notes
- Legend and colorbar internals were significantly refactored. Public usage remains familiar, but extensions relying on internals should be reviewed.
- Semantic legends now have a clearer API surface and are ready for richer per-entry styling workflows.
Full Changelog
v1.72.0...v2.0.1 https://github.com/Ultraplot/UltraPlot/compare/v1.72.0...v2.0.1
v1.72.0: UltraPlot v1.72.0: Sankey diagrams and Ternary plots (2026-01-27)#
This release is marked by the addition of Sankey diagrams and ternary plots (powered by mpltern).
Sankey diagrams
Sankey diagrams are flow charts that visualize the movement of quantities (like energy, money, or users) between different stages or categories, where the width of the connecting arrows is proportional to the flow's magnitude, making major transfers visually obvious. Named after Captain Sankey, they effectively show distributions, energy efficiency, material flows, user journeys, and budget breakdowns, helping to identify dominant paths within a system
Ternary plots
A ternary plot (also known as a ternary graph, triangle plot, or simplex plot) is a barycentric plot on an equilateral triangle. It is used to represent the relative proportions of three variables that sum to a constant—usually 100% or 1.0.
Because the three variables are interdependent (if you know the value of two, the third is automatically determined), a 3D dataset can be visualized in a 2D space without losing information. The plot is commonly used in field such as (evollutionary) game theory. We are harnessing the power of mpltern by wrapping their axes ax external. This gives the best of both worlds where the functionality of the ternary plot is provided by mpltern while allow thing formatting flexibility of UltraPlot.
Note that this feature is introduced now, but marked as experimental. The underlying changes are embedding a different axes inside a container, and there are likely for bugs to emerge from this -- so any feedback or reports are highly appreciated.
snippet
import mpltern
from mpltern.datasets import get_shanon_entropies, get_spiral
import ultraplot as uplt, numpy as np
import networkx as nx
t, l, r, v = get_shanon_entropies()
layout = [[1, 3], [2, 3]]
fig, ax = uplt.subplots(layout, projection=["cartesian", "ternary", "cartesian"], share = 0, hspace = 10)
## Show some noise
ax[0].imshow(np.random.rand(10, 10), cmap = "Fire", colorbar = "r",
colorbar_kw = dict(title = "Random\nnoise", length = 0.333, align = "t"),)
## Ternary plot mock data
vmin = 0.0
vmax = 1.0
levels = np.linspace(vmin, vmax, 7)
cs = ax[1].tripcolor(t, l, r, v, cmap="lapaz_r", shading="flat", vmin=vmin, vmax=vmax)
ax[1].plot(*get_spiral(), color="white", lw=1.25)
colorbar = ax[1].colorbar(
cs,
loc="b",
align="c",
title="Entropy",
length=0.33,
)
## Show a network
g = nx.random_geometric_graph(101, 0.2, seed = 1)
nc = []
min_deg = min(g.degree(), key=lambda x: x[1])[1]
max_deg = max(g.degree(), key=lambda x: x[1])[1]
for node in g.nodes():
intensity = (g.degree(node) - min_deg)/ (max_deg - min_deg)
nc.append(uplt.Colormap("plasma")(intensity))
ax[2].graph(g, node_kw = dict(node_size = 32, node_color = nc))
ax.format(title = ["Hello", "there", "world!"], abc = True)
fig.show()
What's Changed
- Add two files and one folder from doc building to git ignoring (https://github.com/Ultraplot/UltraPlot/pull/482)
- Refactor format in sensible blocks (https://github.com/Ultraplot/UltraPlot/pull/484)
- Fix: Correct label size calculation in _update_outer_abc_loc (https://github.com/Ultraplot/UltraPlot/pull/485)
- Fix: Isolate format() scope for insets and panel (https://github.com/Ultraplot/UltraPlot/pull/486)
- Fix: Move locator back to top level (https://github.com/Ultraplot/UltraPlot/pull/490)
- Fix baseline cache invalidation for PR comparisons (https://github.com/Ultraplot/UltraPlot/pull/492)
- Fix/baseline cache refresh 2 (https://github.com/Ultraplot/UltraPlot/pull/493)
- Feature: Sankey diagrams (https://github.com/Ultraplot/UltraPlot/pull/478)
- Feature: Add container to encapsulate external axes (https://github.com/Ultraplot/UltraPlot/pull/422)
- Fix font lazy load (https://github.com/Ultraplot/UltraPlot/pull/498)
- Fix test_get_size_inches_rounding_and_reference_override (https://github.com/Ultraplot/UltraPlot/pull/499)
- Remove -x from mpl pytest runs (https://github.com/Ultraplot/UltraPlot/pull/500)
- Ci test selection (https://github.com/Ultraplot/UltraPlot/pull/502)
- Update GitHub workflows (https://github.com/Ultraplot/UltraPlot/pull/505)
- CI: make baseline comparison non-blocking (https://github.com/Ultraplot/UltraPlot/pull/508)
- CI: remove redundant pytest run (https://github.com/Ultraplot/UltraPlot/pull/509)
- Fix log formatter tickrange crash (https://github.com/Ultraplot/UltraPlot/pull/507)
- Add return to
test_colorbar_log_formatter_no_tickrange_error(https://github.com/Ultraplot/UltraPlot/pull/510) - CI: set default mpl image tolerance (https://github.com/Ultraplot/UltraPlot/pull/511)
- Fix pytest-mpl default tolerance hook (https://github.com/Ultraplot/UltraPlot/pull/512)
- Delegate external axes methods with guardrails (https://github.com/Ultraplot/UltraPlot/pull/514)
- Fix ternary tri* delegation and add example (https://github.com/Ultraplot/UltraPlot/pull/513)
Full Changelog: https://github.com/Ultraplot/UltraPlot/compare/v1.71.0...v1.72.0
v1.71.0: UltraPlot v1.71: Ridgelines and Smarter Legends ✨ (2026-01-16)#
This release focuses on two user-facing improvements: a new ridgeline plot type and more flexible figure-level legend placement.
Under the hood, import-time work shifted from eager loading to lazy loading, cutting startup overhead by about 98%.
Highlights
- Ridgeline (joyplot) support for stacked distribution comparisons.
- Figure-level legends now accept
ref=for span inference and consistent placement. - External context mode for integration-heavy workflows where UltraPlot should defer on-the-fly guide creation.
- New Copernicus journal width presets to standardize publication sizing.
- Faster startup via lazy-loading of top-level imports.
snippet
from pathlib import Path
import numpy as np
import ultraplot as uplt
outdir = Path("release_assets/v1.71.0")
outdir.mkdir(parents=True, exist_ok=True)
Ridgeline plots
Ridgeline plots (joyplots) are now built-in. This example uses KDE ridges with a colormap and overlap control.
snippet
rng = np.random.default_rng(12)
data = [rng.normal(loc=mu, scale=0.9, size=1200) for mu in range(5)]
labels = [f"Group {i + 1}" for i in range(len(data))]
fig, ax = uplt.subplots(refwidth="11cm", refaspect=1.6)
ax.ridgeline(
data,
labels=labels,
cmap="viridis",
overlap=0.65,
alpha=0.8,
linewidth=1.1,
)
ax.format(
xlabel="Value",
ylabel="Group",
title="Ridgeline plot with colormap",
)
fig.savefig(outdir / "ridgeline.png", dpi=200)
Figure-level legend placement with ref=
Figure legends can now infer their span from a reference axes or axes group.
This removes the need to manually calculate span, rows, or cols for many
layouts.
snippet
x = np.linspace(0, 2 * np.pi, 256)
layout = [[1, 2, 3], [1, 4, 5]]
fig, axs = uplt.subplots(layout)
cycle = uplt.Cycle("bmh")
for idx, axi in enumerate(axs):
axi.plot(
x,
np.sin((idx + 1) * x),
color=cycle.get_next()["color"],
label=f"sin({idx+1}x)",
)
axs.format(xlabel="x", ylabel=r"sin($\alpha x)")
## Place legend of the first 2 axes on the bottom of the last plot
fig.legend(ax=axs[:2], ref=axs[-1], loc="bottom", ncols=2, frame=False)
## Place legend of the last 2 plots on the bottom of the first column
fig.legend(ax=axs[-2:], ref=axs[:, 1], loc="left", ncols=1, frame=False)
## Collect all labels in a singular legend
fig.legend(ax=axs, loc="bottom", frameon=0)
fig.savefig(outdir / "legend_ref.png", dpi=200)
v1.70.0: 🚀 UltraPlot v1.70.0: Smart Layouts, Better Maps, and Scientific Publishing Support (2026-01-04)#
High-Level Overview: This release focuses on intelligent layout management, geographic plotting enhancements, and publication-ready features. Geographic plots receive improved boundary label handling and rotation capabilities, while new Copernicus Publications standard widths support scientific publishing workflows. Various bug fixes and documentation improvements round out this release.
Major Changes:
1. Geographic Plot Enhancements
## Improved boundary labels and rotation
fig, ax = uplt.subplots(projection="cyl")
ax.format(
lonlim=(-180, 180),
latlim=(-90, 90),
lonlabelrotation=45, # new parameter
labels=True,
land=True,
)
## Boundary labels now remain visible and can be rotated
2. Copernicus Publications Support
## New standard figure widths for scientific publishing
fig = uplt.figure(journal = "cop1")
## Automatically sets appropriate width for Copernicus Publications
3. Legend Placement Improvements
import numpy as np
import ultraplot as uplt
np.random.seed(0)
fig, ax = uplt.subplots(ncols=2, nrows=2)
handles = []
for idx, axi in enumerate(ax):
noise = np.random.randn(100) * idx
angle = np.random.rand() * 2 * np.pi
t = np.linspace(0, 2 * np.pi, noise.size)
y = np.sin(t * angle) + noise[1]
(h,) = axi.plot(t, y, label=f"$f_{idx}$")
handles.append(h)
## New: spanning legends
fig.legend(handles=handles, ax=ax[0, :], span=(1, 2), loc="b")
fig.show()
What's Changed
- Bump actions/checkout from 5 to 6 in the github-actions group (https://github.com/Ultraplot/UltraPlot/pull/415)
- [pre-commit.ci] pre-commit autoupdate (https://github.com/Ultraplot/UltraPlot/pull/416)
- Add placement of legend to axes within a figure (https://github.com/Ultraplot/UltraPlot/pull/418)
- There's a typo about zerotrim in doc. (https://github.com/Ultraplot/UltraPlot/pull/420)
- Fix references in documentation for clarity (https://github.com/Ultraplot/UltraPlot/pull/421)
- fix links to apply_norm (https://github.com/Ultraplot/UltraPlot/pull/423)
- [Feature] add lon lat labelrotation (https://github.com/Ultraplot/UltraPlot/pull/426)
- Fix: Boundary labels now visible when setting lonlim/latlim (https://github.com/Ultraplot/UltraPlot/pull/429)
- Add Copernicus Publications figure standard widths (https://github.com/Ultraplot/UltraPlot/pull/433)
- Fix 2D indexing for gridpec (https://github.com/Ultraplot/UltraPlot/pull/435)
- Fix GeoAxes panel alignment with aspect-constrained projections (https://github.com/Ultraplot/UltraPlot/pull/432)
- Bump the github-actions group with 2 updates (https://github.com/Ultraplot/UltraPlot/pull/444)
- Fix dualx alignment on log axes (https://github.com/Ultraplot/UltraPlot/pull/443)
- Subset label sharing and implicit slice labels for axis groups (https://github.com/Ultraplot/UltraPlot/pull/440)
- Preserve log formatter when setting log scales (https://github.com/Ultraplot/UltraPlot/pull/437)
- Feature: added inference of labels for spanning legends (https://github.com/Ultraplot/UltraPlot/pull/447)
New Contributors
- @gepcel made their first contribution in https://github.com/Ultraplot/UltraPlot/pull/420
- @Holmgren825 made their first contribution in https://github.com/Ultraplot/UltraPlot/pull/433
Full Changelog: https://github.com/Ultraplot/UltraPlot/compare/v1.66.0...v1.70.0
v1.66.0: New feature: External Contexts, and bug splats 🐛 (2025-11-22)#
Release Notes
This release introduces two key improvements to enhance compatibility and consistency.
External Contexts
UltraPlot provides sensible defaults by controlling matplotlib's internal mechanics and applying overrides when needed. While this approach works well in isolation, it can create conflicts when integrating with external libraries.
We've introduced a new external context that disables UltraPlot-specific features when working with third-party libraries. Currently, this context prevents conflicts with internally generated labels in Seaborn plots. We plan to extend this functionality to support broader library compatibility in future releases.
Example usage with Seaborn:
import seaborn as sns
import ultraplot as uplt
## Load example dataset
tips = sns.load_dataset("tips")
## Use external context to avoid label conflicts
fig, ax = uplt.subplots()
with ax.external():
sns.lineplot(data=tips, x="size", y="total_bill", hue="day", ax = ax)
Standardized Binning Functions
We've standardized the default aggregation function across all binning operations to use sum. This change affects hexbin, which previously defaulted to averaging values. All binning functions now consistently use sum as the default, though you can specify any custom aggregation function via the reduce_C_function parameter.
What's Changed
- Hotfix: unsharing causes excessive draw in jupyter (https://github.com/Ultraplot/UltraPlot/pull/411)
- Hotfix: bar labels cause limit to reset for unaffected axis. (https://github.com/Ultraplot/UltraPlot/pull/413)
- fix: change default
reduce_C_functiontonp.sumforhexbin(https://github.com/Ultraplot/UltraPlot/pull/408) - Add external context mode for axes (https://github.com/Ultraplot/UltraPlot/pull/406)
Full Changelog: https://github.com/Ultraplot/UltraPlot/compare/v1.65.1...v1.66.0
v1.65.1: Hot-fix: add minor issue where boxpct was not parsed properly (2025-11-02)#
What's Changed
- Bump the github-actions group with 2 updates (https://github.com/Ultraplot/UltraPlot/pull/398)
- Fix missing s on input parsing for boxpercentiles (https://github.com/Ultraplot/UltraPlot/pull/400)
Full Changelog: https://github.com/Ultraplot/UltraPlot/compare/v1.65.0...v1.65.1
v1.65.0: Enhanced Grid Layouts and Multi-Span Colorbars (2025-10-31)#
:art: UltraPlot v1.65 release notes
This release introduces substantial improvements to subplot layout flexibility and configuration management for scientific visualization.
Key Features
Non-Rectangular Grid Layouts with Side Labels (#376)
Asymmetric subplot arrangements now support proper axis labeling, enabling complex multi-panel figures without manual positioning workarounds.
Multi-Span Colorbars (#394)
Colorbars can span multiple subplots, eliminating redundant color scales in comparative visualizations.
RC-Configurable Color Cycles (#378)
Cycle objects can be set via rc configuration, enabling consistent color schemes across figures and projects.
Improved Label Sharing (#372, #387)
Enhanced logic for axis label sharing in complex grid configurations with expanded test coverage.
Infrastructure
- Automatic version checking (#377). Users can now get informed when a new version is available by setting
uplt.rc["ultraplot.check_for_latest_version"] = Truewhich will drop a warning if a newer version is available. - Demo gallery unit tests (#386)
- Optimized CI/CD workflow (#388, #389, #390, #391)
Impact
These changes address common pain points in creating publication-quality multi-panel figures, particularly for comparative analyses requiring consistent styling and efficient use of figure space.
What's Changed
- Allow non-rectangular grids to use side labels (https://github.com/Ultraplot/UltraPlot/pull/376)
- Test/update label sharing tests (https://github.com/Ultraplot/UltraPlot/pull/372)
- Add version checker for UltraPlot (https://github.com/Ultraplot/UltraPlot/pull/377)
- Feature: allow cycle objects to be set on rc (https://github.com/Ultraplot/UltraPlot/pull/378)
- Add unittest for demos (https://github.com/Ultraplot/UltraPlot/pull/386)
- Increase timeout on GHA (https://github.com/Ultraplot/UltraPlot/pull/388)
- bump to 60 minutes (https://github.com/Ultraplot/UltraPlot/pull/389)
- Skip test_demos on gha (https://github.com/Ultraplot/UltraPlot/pull/391)
- Hotfix: minor update in sharing logic (https://github.com/Ultraplot/UltraPlot/pull/387)
- Housekeeping for
ultraplot-build.yml(https://github.com/Ultraplot/UltraPlot/pull/390) - Feature: Allow multi-span colorbars (https://github.com/Ultraplot/UltraPlot/pull/394)
Full Changelog: https://github.com/Ultraplot/UltraPlot/compare/v1.63.0...v1.65.0
v1.63.0: 🌀 New Feature: Curved Quiver (2025-10-14)#
This release introduces curved_quiver, a new plotting primitive that renders compact, curved arrows following the local direction of a vector field. It’s designed to bridge the gap between quiver (straight, local glyphs) and streamplot (continuous, global trajectories): you retain the discrete arrow semantics of quiver, but you gain local curvature that more faithfully communicates directional change.
What it does
Under the hood, the implementation follows the same robust foundations as matplotlib’s streamplot, adapted to generate short, curved arrow segments instead of full streamlines. As such it can be seen as in between streamplot and quiver plots, see figure below and above.
The core types live in ultraplot/axes/plot_types/curved_quiver.py and are centered on CurvedQuiverSolver, which coordinates grid/coordinate mapping, seed point generation, trajectory integration, and spacing control:
-
_CurvedQuiverGridvalidates and models the input grid. It ensures the x grid is rectilinear with equal rows and the y grid with equal columns, computesdx/dy, and exposes grid shape and extent. This meanscurved_quiveris designed for rectilinear grids where rows/columns ofx/yare consistent, matching the expectations of stream/line-based vector plotting. -
_DomainMapmaintains transformations among data-, grid-, and mask-coordinates. Velocity components are rescaled into grid-coordinates for integration, and speed is normalized to axes-coordinates so that step sizes and error metrics align with the visual output (this is important for smooth curves at different figure sizes and grid densities). It also owns bookkeeping for the spacing mask. -
_StreamMaskenforces spacing between trajectories at a coarse mask resolution, much likestreamplotspacing. As a trajectory advances, the mask is filled where the curve passes, preventing new trajectories from entering already-occupied cells. This avoids over-plotting and stabilizes density in a way that feels consistent withstreamplotoutput while still generating discrete arrows. -
Integration is handled by a second-order Runge–Kutta method with adaptive step sizing, implemented in
CurvedQuiverSolver.integrate_rk12. This “improved Euler” approach is chosen for a balance of speed and visual smoothness. It uses an error metric in axes-coordinates to adapt the step sizeds. A maximum step (maxds) is also enforced to prevent skipping mask cells. The integration proceeds forward from each seed point, terminating when any of the following hold: the curve exits the domain, an intermediate integration step would go out of bounds (in which case a single Euler step to the boundary is taken for neatness), a local zero-speed region is detected, or the path reaches the target arc length set by the visual resolution. Internally, that arc length is bounded by a threshold proportional to the mean of the sampled magnitudes along the curve, which is howscaleeffectively maps to a “how far to bend” control in physical units. -
Seed points are generated uniformly over the data extent via
CurvedQuiverSolver.gen_starting_points, usinggrains × grainspositions. Increasinggrainsincreases the number of potential arrow locations and produces smoother paths because more micro-steps are used to sample curvature. During integration, the solver marks the mask progressively via_DomainMap.update_trajectory, and very short trajectories are rejected with_DomainMap.undo_trajectory()to avoid clutter. -
The final artist returned to you is a
CurvedQuiverSet(a small dataclass aligned withmatplotlib.streamplot.StreamplotSet) exposinglines(the curved paths) andarrows(the arrowheads). This mirrors familiarstreamplotergonomics. For example, you can attach a colorbar to.lines, as shown in the figures.
From a user perspective, you call ax.curved_quiver(X, Y, U, V, ...) just as you would quiver, optionally passing color as a scalar field to map magnitude, cmap for color mapping, arrow_at_end=True and arrowsize to emphasize direction, and the two most impactful shape controls: grains and scale. Use curved_quiver when you want to reveal local turning behavior—vortices, shear zones, near saddles, or flow deflection around obstacles—without committing to global streamlines. If your field is highly curved in localized pockets where straight arrows are misleading but streamplot feels too continuous or dense, curved_quiver is the right middle ground.
Performance
Performance-wise, runtime scales with the number of glyphs and the micro-steps (grains). The default values are a good balance for most grids; for very dense fields, you can either reduce grains or down-sample the input grid. The API is fully additive and doesn’t introduce any breaking changes, and it integrates with existing colorbar and colormap workflows.
Parameters
There are two main parameters that affect the plots visually. The grainsparameters controls the density of the grid by interpolating between the input grid. Setting a higher grid will fill the space with more streams. See for a full function description the documentation.
The size parameter will multiply the magnitude of the stream. Setting this value higher will make it look more similar to streamplot.
Acknowledgements
Special thanks to @veenstrajelmer for his implementation (https://github.com/Deltares/dfm_tools) and @Yefee for his suggestion to add this to UltraPlot! And as always @beckermr for his review.
What's Changed
- Add
curved_quiver— Curved Vector Field Arrows for 2D Plots (https://github.com/Ultraplot/UltraPlot/pull/361) - Add Colormap parsing to curved-quiver (https://github.com/Ultraplot/UltraPlot/pull/369)
Suggestions or feedback
Do you have suggestion or feedback? Checkout our discussion on this release.
Full Changelog: https://github.com/Ultraplot/UltraPlot/compare/v1.62.0...v1.63.0
v1.62.0: 🚀 New Release: Configurator Handler Registration (2025-10-13)#
This release introduces a powerful extension point to the configuration system — Configurator.register_handler() — enabling dynamic responses to configuration changes.
✨ New Feature: register_handler
You can now register custom handlers that execute automatically when specific settings are modified.
This is particularly useful for settings that require derived logic or side-effects, such as updating related Matplotlib parameters.
register_handler(name: str, func: Callable[[Any], Dict[str, Any]]) -> None
````
**Example (enabled by default):**
```python
def _cycle_handler(value):
# Custom logic to create a cycler object from the value
return {'axes.prop_cycle': new_cycler}
rc.register_handler('cycle', _cycle_handler)
Each handler function receives the new value of the setting and must return a dictionary mapping valid Matplotlib rc keys to their corresponding values. These updates are applied automatically to the runtime configuration.
🧩 Why It Matters
This addition:
- Fixes an issue where
cycle:entries inultraplotrcwere not properly applied. - Decouples configuration logic from Matplotlib internals.
- Provides a clean mechanism for extending the configuration system with custom logic — without circular imports or hard-coded dependencies.
🔧 Internal Improvements
- Refactored configuration update flow to support handler callbacks.
- Simplified
rcmanagement by delegating side-effectful updates to registered handlers.
💡 Developer Note
This API is designed to be extensible. Future handlers may include dynamic color normalization, font synchronization, or interactive theme updates — all powered through the same mechanism.
v1.61.1: 🚀 Release: CFTime Support and Integration (2025-10-08)#
Highlights
CFTime Axis Support:
We’ve added robust support for CFTime objects throughout ultraplot. This enables accurate plotting and formatting of time axes using non-standard calendars (e.g., noleap, gregorian, standard), which are common in climate and geoscience datasets.
Automatic Formatter and Locator Selection:
ultraplot now automatically detects CFTime axes and applies the appropriate formatters and locators, ensuring correct tick placement and labeling for all supported calendar types.
Seamless Integration with xarray:
These features are designed for direct use with xarray datasets and dataarrays. When plotting data with CFTime indexes, ultraplot will handle all time axis formatting and tick generation automatically—no manual configuration required.
Intended Use
This release is aimed at users working with climate, weather, and geoscience data, where time coordinates may use non-standard calendars. The new CFTime functionality ensures that plots generated from xarray and other scientific libraries display time axes correctly, regardless of calendar type.
Example Usage
import xarray as xr
import numpy as np
import cftime
import ultraplot as uplt
## Create a sample xarray DataArray with CFTime index
times = [cftime.DatetimeNoLeap(2001, 1, i+1) for i in range(10)]
data = xr.DataArray(np.random.rand(10), coords=[times], dims=["time"])
fig, ax = uplt.subplots()
data.plot(ax=ax)
## CFTime axes are automatically formatted and labeled
ax.set_title("CFTime-aware plotting with ultraplot")
uplt.show()
Migration and Compatibility
- No changes are required for existing code using standard datetime axes.
- For datasets with CFTime indexes (e.g., from
xarray), simply plot as usual—ultraplotwill handle the rest.
We welcome feedback and bug reports as you explore these new capabilities!
What's Changed
- Fix edgecolor not set on scatter plots with single-row DataFrame data (https://github.com/Ultraplot/UltraPlot/pull/325)
- Add suptitle_kw alignment support to UltraPlot (https://github.com/Ultraplot/UltraPlot/pull/327)
- Update Documentation for
abcParameter in Subplots and Format Command (https://github.com/Ultraplot/UltraPlot/pull/328) - Fix subplots docs (https://github.com/Ultraplot/UltraPlot/pull/330)
- Add members to api (https://github.com/Ultraplot/UltraPlot/pull/332)
- rm show from tests (https://github.com/Ultraplot/UltraPlot/pull/335)
- Revert "Fix edge case where vcenter is not properly set for diverging norms" (https://github.com/Ultraplot/UltraPlot/pull/337)
- Bump the github-actions group with 2 updates (https://github.com/Ultraplot/UltraPlot/pull/339)
- Extra tests for geobackends (https://github.com/Ultraplot/UltraPlot/pull/334)
- Fix some links for docs (https://github.com/Ultraplot/UltraPlot/pull/341)
- Fix links docs (https://github.com/Ultraplot/UltraPlot/pull/342)
- Lazy loading colormaps (https://github.com/Ultraplot/UltraPlot/pull/343)
- Set warning level for mpl to error (https://github.com/Ultraplot/UltraPlot/pull/350)
- Sanitize pad and len formatters on Cartesian Axes (https://github.com/Ultraplot/UltraPlot/pull/346)
- Fix order of label transfer (https://github.com/Ultraplot/UltraPlot/pull/353)
- Bump the github-actions group with 2 updates (https://github.com/Ultraplot/UltraPlot/pull/354)
- Add cftime support for non-standard calendars (https://github.com/Ultraplot/UltraPlot/pull/344)
New Contributors
- @Copilot made their first contribution in https://github.com/Ultraplot/UltraPlot/pull/325
Full Changelog: https://github.com/Ultraplot/UltraPlot/compare/v1.60.2...v1.61.0
Note: v1.61.0 is yanked from pypi as it contained a debug statement. This merely removes the debug.
v1.60.2: Hotfix: double depth decorator that affected geoplots (2025-08-18)#
What's Changed
- Handle non homogeneous arrays (https://github.com/Ultraplot/UltraPlot/pull/318)
- Update Cartopy references (https://github.com/Ultraplot/UltraPlot/pull/322)
- Fix inhomogeneous violin test (https://github.com/Ultraplot/UltraPlot/pull/323)
- Fix issue where double decorator does not parse function name (https://github.com/Ultraplot/UltraPlot/pull/320)
New Contributors
- @rcomer made their first contribution in https://github.com/Ultraplot/UltraPlot/pull/322
Full Changelog: https://github.com/Ultraplot/UltraPlot/compare/v1.60.1...v1.60.2
v1.60.1: Hotfixes for colors and colormaps (2025-08-08)#
Minor bug fixes
What's Changed
- Fix edge case where vcenter is not properly set for diverging norms (https://github.com/Ultraplot/UltraPlot/pull/314)
- Fix color parsing when color is not string (https://github.com/Ultraplot/UltraPlot/pull/315)
Full Changelog: https://github.com/Ultraplot/UltraPlot/compare/v1.60.0...v1.60.1
v1.57.2: Bug fixes for Geo dms coordinates and reverse colors/colormaps (2025-07-02)#
What's Changed
- Add citation metadata (CITATION.cff, .zenodo.json) to support scholarly use (https://github.com/Ultraplot/UltraPlot/pull/284)
- Update CITATON.cff (https://github.com/Ultraplot/UltraPlot/pull/286)
- Add citation links to README. (https://github.com/Ultraplot/UltraPlot/pull/287)
- Mv dynamic function to the subplotgrid (https://github.com/Ultraplot/UltraPlot/pull/281)
- add downloads badge (https://github.com/Ultraplot/UltraPlot/pull/290)
- replace color to orange (https://github.com/Ultraplot/UltraPlot/pull/291)
- Hotfix add all locations to colorbar label (https://github.com/Ultraplot/UltraPlot/pull/295)
- Fix DMS not set on some projections (https://github.com/Ultraplot/UltraPlot/pull/293)
- Bump mamba-org/setup-micromamba from 2.0.4 to 2.0.5 in the github-actions group (https://github.com/Ultraplot/UltraPlot/pull/299)
- fix late binding and proper reversal for funcs (https://github.com/Ultraplot/UltraPlot/pull/296)
Full Changelog: https://github.com/Ultraplot/UltraPlot/compare/v1.57.1...v1.57.2
v1.57.1: Zenodo release (2025-06-24)#
This PR integrates Zenodo with the UltraPlot repository to enable citation via DOI.
From now on, every GitHub release will be archived by Zenodo and assigned a unique DOI, allowing researchers and users to cite UltraPlot in a standardized, persistent way.
We’ve also added a citation file and BibTeX entry for convenience. Please refer to the GitHub “Cite this repository” section or use the provided BibTeX in your work.
This marks an important step in making UltraPlot more visible and citable in academic and scientific publications.
🔗 DOI: https://doi.org/10.5281/zenodo.15733565
Cite as
@software{vanElteren2025,
author = {Casper van Elteren and Matthew R. Becker},
title = {UltraPlot: A succinct wrapper for Matplotlib},
year = {2025},
version = {1.57.1},
publisher = {GitHub},
url = {https://github.com/Ultraplot/UltraPlot}
}
What's Changed
- Fix a few tests (https://github.com/Ultraplot/UltraPlot/pull/267)
- set rng per test (https://github.com/Ultraplot/UltraPlot/pull/268)
- Add xdist to image compare (https://github.com/Ultraplot/UltraPlot/pull/266)
- Fix issue where view is reset on setting ticklen (https://github.com/Ultraplot/UltraPlot/pull/272)
- Racing condition xdist fix (https://github.com/Ultraplot/UltraPlot/pull/273)
- Replace spring with forceatlas2 (https://github.com/Ultraplot/UltraPlot/pull/275)
- Revert xdist addition (https://github.com/Ultraplot/UltraPlot/pull/277)
- fix: pass layout_kw in network test function (https://github.com/Ultraplot/UltraPlot/pull/278)
- fix: this one needs a seed too (https://github.com/Ultraplot/UltraPlot/pull/279)
- rm paren (https://github.com/Ultraplot/UltraPlot/pull/280)
Full Changelog: https://github.com/Ultraplot/UltraPlot/compare/v1.57...v1.57.1
v1.57: Support matplotlib 3.10 and python 3.13 (2025-06-16)#
What's Changed
- Fix unused parameters being passed to pie chart (https://github.com/Ultraplot/UltraPlot/pull/260)
- Update return requirements pytest 8.4.0 (https://github.com/Ultraplot/UltraPlot/pull/265)
- Bump python to 3.13 (https://github.com/Ultraplot/UltraPlot/pull/264)
- Update matplotlib to mpl 3.10 (https://github.com/Ultraplot/UltraPlot/pull/263)
Full Changelog: https://github.com/Ultraplot/UltraPlot/compare/v1.56...v1.57
v1.56: 🐝 Feature addition: Beeswarm plot (2025-06-13)#
We are introducing a new plot type with this release: a beeswarm plot. A beeswarm plot is a data visualization technique that displays individual data points in a way that prevents overlap while maintaining their relationship to categorical groups, creating a distinctive "swarm" pattern that resembles bees clustering around a hive.
Unlike traditional box plots or violin plots that aggregate data, beeswarm plots show every individual observation, making them ideal for datasets with moderate sample sizes where you want to see both individual points and overall distribution patterns, identify outliers clearly, and compare distributions across multiple categories without losing any information through statistical summaries.
This plot mimics the beeswarm from SHAP library, but lacks the more sophisticated patterns they apply such as inline group clustering. UltraPlot does not aim to add these features but instead provide an interface that is simpler that users can tweak to their hearts desires.
snippet
import ultraplot as uplt, numpy as np
## Create mock data
n_points, n_features = 50, 4
features = np.arange(n_features)
data = np.empty((n_points, n_features))
feature_values = np.repeat(
features,
n_points,
).reshape(data.shape)
for feature in features:
data[:, feature] = np.random.normal(feature * 1.5, 0.6, n_points)
cmap = uplt.Colormap(uplt.rc["cmap.diverging"])
## Create plot and style
fig, (left, right) = uplt.subplots(ncols=2, share=0)
left.beeswarm(
data,
orientation="vertical",
alpha=0.7,
cmap=cmap,
)
left.format(
title="Traditional Beeswarm Plot",
xlabel="Category",
ylabel="Value",
xticks=features,
xticklabels=["Group A", "Group B", "Group C", "Group D"],
)
right.beeswarm(
data,
feature_values=feature_values,
cmap=cmap,
colorbar="right",
)
right.format(
title="Feature Value Beeswarm Plot",
xlabel="SHAP Value",
yticks=features,
yticklabels=["A", "B", "C", "D"],
ylabel="Feature",
)
uplt.show(block=1)
What's Changed
- Hotfix GeoAxes indicate zoom. (https://github.com/Ultraplot/UltraPlot/pull/249)
- Feature: Beeswarm plot (https://github.com/Ultraplot/UltraPlot/pull/251)
- GeoTicks not responsive (https://github.com/Ultraplot/UltraPlot/pull/253)
- add top level ignores for local testing (https://github.com/Ultraplot/UltraPlot/pull/255)
- Update .gitignore (https://github.com/Ultraplot/UltraPlot/pull/257)
- Refactor beeswarm (https://github.com/Ultraplot/UltraPlot/pull/254)
Full Changelog: https://github.com/Ultraplot/UltraPlot/compare/V1.55...v1.56
v1.55: . Bug fixes. (2025-06-04)#
This release continues our ongoing mission to squash pesky bugs and make your plotting experience smoother and more intuitive.
✨ New Features
-
Centered Labels for pcolormesh You can now enable center_labels when using pcolormesh, making it easier to annotate discrete diverging colormaps—especially when including zero among the label values. Ideal for visualizing data with meaningful central thresholds.
-
Direct Bar Labels for bar and hbar Bar labels can now be added directly via the bar and hbar commands. No more extra steps—just call the method and get your labeled bars out of the box.
🐞 Bug Fixes
Various internal improvements and minor bug fixes aimed at ensuring a more robust and predictable plotting experience.
As always, thank you for using UltraPlot! Feedback, issues, and contributions are welcome.
What's Changed
- Cartesian docs links fixed (https://github.com/Ultraplot/ultraplot/pull/226)
- minor fix for mpl3.10 (https://github.com/Ultraplot/ultraplot/pull/229)
- Adjust the ticks to center on 'nice' values (https://github.com/Ultraplot/ultraplot/pull/228)
- rm unnecessary show (https://github.com/Ultraplot/ultraplot/pull/241)
- Feat bar labels (https://github.com/Ultraplot/ultraplot/pull/240)
- Fix links for 1d plots in docs (https://github.com/Ultraplot/ultraplot/pull/242)
- Deprecate basemap (https://github.com/Ultraplot/ultraplot/pull/243)
- Hotfix get_border_axes (https://github.com/Ultraplot/ultraplot/pull/236)
- Hotfix panel (https://github.com/Ultraplot/ultraplot/pull/238)
- Hot fix twinned y labels (https://github.com/Ultraplot/ultraplot/pull/246)
Full Changelog: https://github.com/Ultraplot/ultraplot/compare/v1.50.2...V1.55
v1.50.2 (2025-05-20)#
What's Changed
- perf: run comparison tests at the same time as the main tests (https://github.com/Ultraplot/ultraplot/pull/213)
- fix cycler setting to 1 when only 1 column is parsed (https://github.com/Ultraplot/ultraplot/pull/218)
- Skip sharing logic when colorbar is added to GeoPlots. (https://github.com/Ultraplot/ultraplot/pull/219)
- Restore redirection for tricontourf for GeoPlotting (https://github.com/Ultraplot/ultraplot/pull/222)
- Fix numerous geo docs visuals (https://github.com/Ultraplot/ultraplot/pull/223)
- Allow rasterization on GeoFeatures. (https://github.com/Ultraplot/ultraplot/pull/220)
- more fixes (https://github.com/Ultraplot/ultraplot/pull/224)
- Docs fix3 (https://github.com/Ultraplot/ultraplot/pull/225)
Full Changelog: https://github.com/Ultraplot/ultraplot/compare/v1.50.1...v1.50.2
v1.50.1 (2025-05-12)#
What's Changed
- fix: specify import exception type and add typing-extensions to deps (https://github.com/Ultraplot/ultraplot/pull/212)
Full Changelog: https://github.com/Ultraplot/ultraplot/compare/v1.50...v1.50.1
v1.50: Networks, lollipops and sharing (2025-05-11)#
UltraPlot v1.50
Version v1.50 is a major milestone for UltraPlot. As we become more familiar with the codebase, we’ve opened the door to new features—balancing innovation with continuous backend improvements and bug fixes.
🌍 GeoAxes Sharing
|
You can now share axes between subplots using GeoAxes, as long as they use the same rectilinear projection. This enables cleaner, more consistent layouts when working with geographical data. |
|
🕸️ Network Graphs
|
UltraPlot now supports network visualizations out of the box. With smart defaults and simple customization options, creating beautiful network plots is easier than ever. |
|
Network plotting code
import networkx as nx, ultraplot as uplt
n = 100
g = nx.random_geometric_graph(n, radius=0.2)
c = uplt.colormaps.get_cmap("viko")
c = c(np.linspace(0, 1, n))
node = dict(
node_size=np.random.rand(n) * 100,
node_color=c,
)
fig, ax = uplt.subplots()
ax.graph(g, layout="kamada_kawai", node_kw=node)
fig.show()
🍭 Lollipop Graphs
|
A sleek alternative to bar charts, lollipop graphs are now available directly through UltraPlot. They shine when visualizing datasets with many bars, reducing visual clutter while retaining clarity. |
|
Lollipop example code
import ultraplot as uplt, pandas as pd, numpy as np
data = np.random.rand(5, 5).cumsum(axis=0).cumsum(axis=1)[:, ::-1]
data = pd.DataFrame(
data,
columns=pd.Index(np.arange(1, 6), name="column"),
index=pd.Index(["a", "b", "c", "d", "e"], name="row idx"),
)
fig, ax = uplt.subplots(ncols=2, share=0)
ax[0].lollipop(
data,
stemcolor="green",
stemwidth=2,
marker="d",
edgecolor="k",
)
ax[1].lollipoph(data, linestyle="solid")
What's Changed
- separate logger for ultraplot and matplotlib (https://github.com/Ultraplot/ultraplot/pull/178)
- Capture warning (https://github.com/Ultraplot/ultraplot/pull/180)
- tmp turning of test (https://github.com/Ultraplot/ultraplot/pull/183)
- Skip missing tests if added in PR (https://github.com/Ultraplot/ultraplot/pull/175)
- Revert "Skip missing tests if added in PR" (https://github.com/Ultraplot/ultraplot/pull/184)
- rm conftest from codecov (https://github.com/Ultraplot/ultraplot/pull/187)
- skip tests properly (https://github.com/Ultraplot/ultraplot/pull/186)
- Fix colorbar loc (https://github.com/Ultraplot/ultraplot/pull/182)
- Fix bar alpha (https://github.com/Ultraplot/ultraplot/pull/192)
- make import uplt to be consistent with rest of repo (https://github.com/Ultraplot/ultraplot/pull/195)
- Ensure that shared labels are consistently updated. (https://github.com/Ultraplot/ultraplot/pull/177)
- sensible defaults and unittest (https://github.com/Ultraplot/ultraplot/pull/189)
- Deprecation fix mpl 3.10 and beyond (https://github.com/Ultraplot/ultraplot/pull/69)
- Add network plotting to UltraPlot (https://github.com/Ultraplot/ultraplot/pull/169)
- Hotfix test (https://github.com/Ultraplot/ultraplot/pull/196)
- Discrete colors for quiver (https://github.com/Ultraplot/ultraplot/pull/198)
- correct url for basemap objects (https://github.com/Ultraplot/ultraplot/pull/202)
- override logx/y/log with updated docstring (https://github.com/Ultraplot/ultraplot/pull/203)
- Add lollipop graph (https://github.com/Ultraplot/ultraplot/pull/194)
- Fix network linking in docs and api refs (https://github.com/Ultraplot/ultraplot/pull/205)
- Avoid getting edges and setting centers for some shaders (https://github.com/Ultraplot/ultraplot/pull/208)
- Fix some references in inset docs (https://github.com/Ultraplot/ultraplot/pull/209)
- rm dep warning (https://github.com/Ultraplot/ultraplot/pull/210)
- [Feature add] Share Axes in GeoPlot + bug fixes (https://github.com/Ultraplot/ultraplot/pull/159)
Full Changelog: https://github.com/Ultraplot/ultraplot/compare/v1.11...v1.5
v1.11: Various bug fixes (2025-04-25)#
What's Changed
- Update intersphinx links (https://github.com/Ultraplot/ultraplot/pull/128)
- Update geo doc (https://github.com/Ultraplot/ultraplot/pull/129)
- Hotfix update geo doc (https://github.com/Ultraplot/ultraplot/pull/130)
- New site, who dis? (https://github.com/Ultraplot/ultraplot/pull/132)
- Add about page (https://github.com/Ultraplot/ultraplot/pull/133)
- Make it mobile friendly (https://github.com/Ultraplot/ultraplot/pull/134)
- Add gallery to github page (https://github.com/Ultraplot/ultraplot/pull/140)
- Fix readme (https://github.com/Ultraplot/ultraplot/pull/142)
- Fix readme fixed sizes (https://github.com/Ultraplot/ultraplot/pull/143)
- added page for errors (https://github.com/Ultraplot/ultraplot/pull/141)
- Bump dawidd6/action-download-artifact from 2 to 6 in /.github/workflows (https://github.com/Ultraplot/ultraplot/pull/144)
- fix: checkout from correct fork (https://github.com/Ultraplot/ultraplot/pull/145)
- Set seed prior to test to ensure fidelity (https://github.com/Ultraplot/ultraplot/pull/148)
- Move warning inside pytest config (https://github.com/Ultraplot/ultraplot/pull/151)
- Fix scaler parsing (https://github.com/Ultraplot/ultraplot/pull/153)
- Update site logo (https://github.com/Ultraplot/ultraplot/pull/154)
- Fix minor grid showing on cbar (https://github.com/Ultraplot/ultraplot/pull/150)
- Add option to place abc indicator outside the axis bbox (https://github.com/Ultraplot/ultraplot/pull/139)
- Add unitests for ultraplot.internals.fonts (https://github.com/Ultraplot/ultraplot/pull/156)
- Minor refactor of unittests (https://github.com/Ultraplot/ultraplot/pull/157)
- Make anchor_mode default (https://github.com/Ultraplot/ultraplot/pull/161)
- Ipy rc kernel reset (https://github.com/Ultraplot/ultraplot/pull/164)
- allow subfigure formatting (https://github.com/Ultraplot/ultraplot/pull/167)
- make cbar labelloc possible for all direction (https://github.com/Ultraplot/ultraplot/pull/165)
- Add pyarrow to rm pandas error (https://github.com/Ultraplot/ultraplot/pull/171)
- Center figures in docs (https://github.com/Ultraplot/ultraplot/pull/170)
- mv toc to left (https://github.com/Ultraplot/ultraplot/pull/172)
- surpress warnings on action (https://github.com/Ultraplot/ultraplot/pull/174)
Full Changelog: https://github.com/Ultraplot/ultraplot/compare/v1.10.0...v1.11
v1.10.0: Ticks for Geoaxes (2025-03-20)#
This release marks a newly added feature: ticks on GeoAxes
This allows for users to set ticks for the x and or y axis. These can be controlled by
lonticklen, latticklen or ticklen for controlling the x, y or both axis at the same time. This works independently to the major and minor gridlines allow for optimal control over the look and feel of your plots.
What's Changed
- prod: add me to maintainers (https://github.com/Ultraplot/ultraplot/pull/117)
- prod: only use readthedocs for PR tests (https://github.com/Ultraplot/ultraplot/pull/118)
- feat: enable test coverage with codecov (https://github.com/Ultraplot/ultraplot/pull/121)
- dynamically build what's new (https://github.com/Ultraplot/ultraplot/pull/122)
- rm extra == line (https://github.com/Ultraplot/ultraplot/pull/123)
- bugfix for Axes.legend when certain keywords are set to str (https://github.com/Ultraplot/ultraplot/pull/124)
- reduce verbosity of extension (https://github.com/Ultraplot/ultraplot/pull/127)
- allow ticks for geoaxes (https://github.com/Ultraplot/ultraplot/pull/126)
New Contributors
- @syrte made their first contribution in https://github.com/Ultraplot/ultraplot/pull/124
Full Changelog: https://github.com/Ultraplot/ultraplot/compare/v1.0.9...v1.10.0
v1.0.9: IPython 9.0.0 compatibility and numerous backend fixes. (2025-03-05)#
What's Changed
- filter out property if not set (https://github.com/Ultraplot/ultraplot/pull/99)
- Add texgyre to docs (https://github.com/Ultraplot/ultraplot/pull/101)
- Texgyre fix (https://github.com/Ultraplot/ultraplot/pull/102)
- rm warnings when downloading data inside env (https://github.com/Ultraplot/ultraplot/pull/104)
- prod: only build ultraplot when ultraplot src changes (https://github.com/Ultraplot/ultraplot/pull/106)
- Gitignore baseline (https://github.com/Ultraplot/ultraplot/pull/109)
- Fix colorbar ticks (https://github.com/Ultraplot/ultraplot/pull/108)
- Attempt fix workflow docs build (https://github.com/Ultraplot/ultraplot/pull/114)
- rm ref semver (https://github.com/Ultraplot/ultraplot/pull/112)
- Only store failed tests mpl-pytest (https://github.com/Ultraplot/ultraplot/pull/113)
- fix: matrix test for no changes is running when it should not be (https://github.com/Ultraplot/ultraplot/pull/115)
Full Changelog: https://github.com/Ultraplot/ultraplot/compare/v1.0.8...v1.0.9
v1.0.8-2: Hotfix cycling properties (2025-02-27)#
Hot fix for cycle not recognizing color argument
What's Changed
- filter out property if not set (https://github.com/Ultraplot/ultraplot/pull/99)
Full Changelog: https://github.com/Ultraplot/ultraplot/compare/v1.0.8...v1.0.8-2
v1.0.8: Minor bug fixes (2025-02-23)#
Fixes an issue where ticks were not properly set when giving levels and ticks in pcolormesh and related functions in the colorbar. See more of the changes below.
What's Changed
- fix: remove race condition for pushes of tags (https://github.com/Ultraplot/ultraplot/pull/78)
- use seed for reproducibility (https://github.com/Ultraplot/ultraplot/pull/79)
- Fix demo function not extracting colormaps (https://github.com/Ultraplot/ultraplot/pull/83)
- allows cycle to be a tuple (https://github.com/Ultraplot/ultraplot/pull/87)
- Fixes heatmap not showing labels. (https://github.com/Ultraplot/ultraplot/pull/91)
- Doc link fix (https://github.com/Ultraplot/ultraplot/pull/92)
- explicitly override minor locator if given (https://github.com/Ultraplot/ultraplot/pull/96)
Full Changelog: https://github.com/Ultraplot/ultraplot/compare/v1.0.7...v1.0.8
v1.0.7: Dev update. (2025-02-15)#
What's Changed
- added path explicitly on publish (https://github.com/Ultraplot/ultraplot/pull/77)
Full Changelog: https://github.com/Ultraplot/ultraplot/compare/v1.0.6...v1.0.7
v1.0.6: Ensure norm fix (2025-02-15)#
What's Changed
- add case where cycler is already a cycle (https://github.com/Ultraplot/ultraplot/pull/65)
- fix: make sure PRs do not mess with releases (https://github.com/Ultraplot/ultraplot/pull/67)
- fix: ensure pypi readme works ok (https://github.com/Ultraplot/ultraplot/pull/70)
- feat: deduplicate pypi publish workflow (https://github.com/Ultraplot/ultraplot/pull/71)
- [pre-commit.ci] pre-commit autoupdate (https://github.com/Ultraplot/ultraplot/pull/73)
- use norm explicitly (https://github.com/Ultraplot/ultraplot/pull/76)
New Contributors
- @pre-commit-ci made their first contribution in https://github.com/Ultraplot/ultraplot/pull/73
Full Changelog: https://github.com/Ultraplot/ultraplot/compare/v1.0.5...v1.0.6
v1.0.5 (2025-02-02)#
What's Changed
- test: adjust test matrix to use one locale and add matrix dimension over MPL versions (https://github.com/Ultraplot/ultraplot/pull/51)
- Dep build (https://github.com/Ultraplot/ultraplot/pull/57)
- Second dep on build still being skipped (https://github.com/Ultraplot/ultraplot/pull/58)
- another attempt to fix publish (https://github.com/Ultraplot/ultraplot/pull/59)
- fix: remove custom classifier for MPL (https://github.com/Ultraplot/ultraplot/pull/62)
- fix: correct chain of logic for publish workflow (https://github.com/Ultraplot/ultraplot/pull/63)
- fix: get pypi publish working (https://github.com/Ultraplot/ultraplot/pull/64)
Full Changelog: https://github.com/Ultraplot/ultraplot/compare/v1.0.4...v1.0.5
v1.0.4: Fixing Margins (2025-01-31)#
A major change for this release is that the margins were not properly being set with the latest mpl. This reverts the margins back to the behavior where they are tighter as is expected from UltraPlot!
v1.0.3: Minor bug fixes (2025-01-27)#
We are still experiencing some growing pains with proplot -> ultraplot conversion, however it is looking good. Some bugs were fixed regarding compatibility with mpl3.10 and we are moving towards fidelity checks. Please update when you can to this latest release.
This release is to ensure that the latest version is on pypi and conda.
Full Changelog: https://github.com/Ultraplot/ultraplot/compare/v1.0.2...v1.0.3
v1.0: Big Compatibility Release! Matplotlib >= 3.8. (2025-01-11)#
- 78367da2 (HEAD -> main, tag: v1.0, uplt/main) Matplotlib 3.10 - Compatability
- 4e15fde2 Merge pull request #16 from cvanelteren/linter-workflow
- 54837966 (origin/linter-workflow, linter-workflow) make repo - black compatible
- 7aa82a2c added linter workflow
- 97f14082 point readme badge to correct workflow
- fb762c5b (origin/main) Merge pull request #13 from - cvanelteren/triangulation-fix
- afa14caf (origin/triangulation-fix) removed pandas reference
- 2d66e46b added data dict to unittest test and made - preprocessing compatible
- 465688e7 add decorator to other trifunctions
- ad83bfb0 ensure backwards compatibility
- 23f65bb9 added df to unittest
- d239bfc3 added unittest for triangulaions
- 5bb8ac14 use mpl triangulation parser
- 5dc8b44b move logic to internals and update input parsing - functions for tri
- f213d870 tripoint also added
- 9eeda2db small typo
- a1c8894b Merge branch 'main' into triangulation-fix
- 83973941 allow triangulation object in tricountour(f)
- 6b8223a8 Merge pull request #4 from cvanelteren/conda
- fa1f2fcc (origin/conda) removed conda recipe
- defb219e separate build and test
- 4cf2c940 # This is a combination of 2 commits. # This is the - 1st commit message:
- 9c75035c separate build and test
- 0089fe04 license revert
- adac1c9a Merge pull request #10 from Ultraplot/revert-6-main
- e31afe64 (uplt/revert-6-main) Revert "license update"
- 35204ef4 renamed yml to ensure consistency
- b243afe7 Merge pull request #8 from cvanelteren/main
- 0c4bc1f8 replaced pplt -> uplt
- 656a7464 Merge pull request #5 from cvanelteren/logo_square
- 89c59cf5 Merge pull request #7 from cvanelteren/main
- 8d01cf33 typo in readme shield
- 7e0ec000 Merge pull request #6 from cvanelteren/main
- 70157b33 license update
- e6d8eca9 (origin/logo_square, logo_square) capitalization to - UltraPlot in docs
- e99be782 square logos
- c2a96554 separated workflows
- 5609372c conda and pypi publish workflow
- d04ea9d9 small changes in workflow
- 5432bdbe add workflow for conda-forge