UltraPlot 2.2.0: Precision Placement — colorbars that span, norms that flex, labels that stay
LatestUltraPlot 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: v2.1.9...v2.2.0
What's Changed
- Hotfix/publish zenodo fix by @cvanelteren in #686
- Refactor init figure by @cvanelteren in #687
- Feature/span cbar slot based by @cvanelteren in #688
- Fix patheffects affecting recall of titleborder by @cvanelteren in #691
- Fix outer legend hiding y-tick labels with sharey='labs' (#694) by @cvanelteren in #696
- Fix/whats new page generation by @cvanelteren in #697
- Fix/norm inputs by @cvanelteren in #693
Full Changelog: v2.1.9...v2.2.0