This section documents features used for modifying Cartesian x and y axes, including axis scales, tick locations, tick label formatting, and several twin and dual axes commands.
Tick locationsMatplotlib tick locators select sensible tick locations based on the axis data limits. In UltraPlot, you can change the tick locator using the format()
keyword arguments xlocator, ylocator, xminorlocator, and yminorlocator (or their aliases, xticks, yticks, xminorticks, and yminorticks). This is powered by the Locator
constructor function.
You can use these keyword arguments to apply built-in matplotlib Locator
s by their “registered” names (e.g., xlocator='log'
), to draw ticks every N
data values with MultipleLocator
(e.g., xlocator=2
), or to tick the specific locations in a list using FixedLocator
(just like set_xticks()
and set_yticks()
). If you want to work with the locator classes directly, they are available in the top-level namespace (e.g., xlocator=uplt.MultipleLocator(...)
is allowed).
To generate lists of tick locations, we recommend using UltraPlot’s arange()
function – it’s basically an endpoint-inclusive version of arange()
, which is usually what you’ll want in this context.
import ultraplot as uplt import numpy as np state = np.random.RandomState(51423) uplt.rc.update( metawidth=1, fontsize=10, metacolor="dark blue", suptitlecolor="dark blue", titleloc="upper center", titlecolor="dark blue", titleborder=False, axesfacecolor=uplt.scale_luminance("powderblue", 1.15), ) fig = uplt.figure(share=False, refwidth=5, refaspect=(8, 1)) fig.format(suptitle="Tick locators demo") # Step size for tick locations ax = fig.subplot(711, title="MultipleLocator") ax.format(xlim=(0, 200), xminorlocator=10, xlocator=30) # Specific list of locations ax = fig.subplot(712, title="FixedLocator") ax.format(xlim=(0, 10), xminorlocator=0.1, xlocator=[0, 0.3, 0.8, 1.6, 4.4, 8, 8.8]) # Ticks at numpy.linspace(xmin, xmax, N) ax = fig.subplot(713, title="LinearLocator") ax.format(xlim=(0, 10), xlocator=("linear", 21)) # Logarithmic locator, used automatically for log scale plots ax = fig.subplot(714, title="LogLocator") ax.format(xlim=(1, 100), xlocator="log", xminorlocator="logminor") # Maximum number of ticks, but at "nice" locations ax = fig.subplot(715, title="MaxNLocator") ax.format(xlim=(1, 7), xlocator=("maxn", 11)) # Hide all ticks ax = fig.subplot(716, title="NullLocator") ax.format(xlim=(-10, 10), xlocator="null") # Tick locations that cleanly divide 60 minute/60 second intervals ax = fig.subplot(717, title="Degree-Minute-Second Locator (requires cartopy)") ax.format(xlim=(0, 2), xlocator="dms", xformatter="dms") uplt.rc.reset()
/home/docs/checkouts/readthedocs.org/user_builds/ultraplot/conda/latest/lib/python3.13/site-packages/ultraplot/axes/base.py:3197: UserWarning: Glyph 8242 (\N{PRIME}) missing from font(s) TeX Gyre Heros. self._tight_bbox = super().get_tightbbox(renderer, *args, **kwargs) /home/docs/checkouts/readthedocs.org/user_builds/ultraplot/conda/latest/lib/python3.13/site-packages/ultraplot/axes/base.py:3185: UserWarning: Glyph 8242 (\N{PRIME}) missing from font(s) TeX Gyre Heros. super().draw(renderer, *args, **kwargs)Tick formatting
Matplotlib tick formatters convert floating point numbers to nicely-formatted tick labels. In UltraPlot, you can change the tick formatter using the format()
keyword arguments xformatter and yformatter (or their aliases, xticklabels and yticklabels). This is powered by the Formatter
constructor function.
You can use these keyword arguments to apply built-in matplotlib Formatter
s by their “registered” names (e.g., xformatter='log'
), to apply a %
-style format directive with FormatStrFormatter
(e.g., xformatter='%.0f'
), or to apply custom tick labels with FixedFormatter
(just like set_xticklabels()
). You can also apply one of UltraPlot’s new tick formatters – for example, xformatter='deglat'
to label ticks as geographic latitude coordinates, xformatter='pi'
to label ticks as fractions of \(\pi\), or xformatter='sci'
to label ticks with scientific notation. If you want to work with the formatter classes directly, they are available in the top-level namespace (e.g., xformatter=uplt.SciFormatter(...)
is allowed).
UltraPlot also changes the default tick formatter to AutoFormatter
. This class trims trailing zeros by default, can optionally omit or wrap tick values within particular number ranges, and can add prefixes and suffixes to each label. See AutoFormatter
for details. To disable the trailing zero-trimming feature, set rc['formatter.zerotrim']
to False
.
import ultraplot as uplt uplt.rc.fontsize = 11 uplt.rc.metawidth = 1.5 uplt.rc.gridwidth = 1 # Create the figure fig, axs = uplt.subplots(ncols=2, nrows=2, refwidth=1.5, share=False) axs.format( ytickloc="both", yticklabelloc="both", titlepad="0.5em", suptitle="Default formatters demo", ) # Formatter comparison locator = [0, 0.25, 0.5, 0.75, 1] axs[0].format(xformatter="scalar", yformatter="scalar", title="Matplotlib formatter") axs[1].format(title="UltraPlot formatter") axs[:2].format(xlocator=locator, ylocator=locator) # Limiting the tick range axs[2].format( title="Omitting tick labels", ticklen=5, xlim=(0, 5), ylim=(0, 5), xtickrange=(0, 2), ytickrange=(0, 2), xlocator=1, ylocator=1, ) # Setting the wrap range axs[3].format( title="Wrapping the tick range", ticklen=5, xlim=(0, 7), ylim=(0, 6), xwraprange=(0, 5), ywraprange=(0, 3), xlocator=1, ylocator=1, ) uplt.rc.reset()
import ultraplot as uplt import numpy as np uplt.rc.update( metawidth=1.2, fontsize=10, axesfacecolor="gray0", figurefacecolor="gray2", metacolor="gray8", gridcolor="gray8", titlecolor="gray8", suptitlecolor="gray8", titleloc="upper center", titleborder=False, ) fig = uplt.figure(refwidth=5, refaspect=(8, 1), share=False) # Scientific notation ax = fig.subplot(911, title="SciFormatter") ax.format(xlim=(0, 1e20), xformatter="sci") # N significant figures for ticks at specific values ax = fig.subplot(912, title="SigFigFormatter") ax.format( xlim=(0, 20), xlocator=(0.0034, 3.233, 9.2, 15.2344, 7.2343, 19.58), xformatter=("sigfig", 2), # 2 significant digits ) # Fraction formatters ax = fig.subplot(913, title="FracFormatter") ax.format(xlim=(0, 3 * np.pi), xlocator=np.pi / 4, xformatter="pi") ax = fig.subplot(914, title="FracFormatter") ax.format(xlim=(0, 2 * np.e), xlocator=np.e / 2, xticklabels="e") # Geographic formatters ax = fig.subplot(915, title="Latitude Formatter") ax.format(xlim=(-90, 90), xlocator=30, xformatter="deglat") ax = fig.subplot(916, title="Longitude Formatter") ax.format(xlim=(0, 360), xlocator=60, xformatter="deglon") # User input labels ax = fig.subplot(917, title="FixedFormatter") ax.format( xlim=(0, 5), xlocator=np.arange(5), xticklabels=["a", "b", "c", "d", "e"], ) # Custom style labels ax = fig.subplot(918, title="FormatStrFormatter") ax.format(xlim=(0, 0.001), xlocator=0.0001, xformatter="%.E") ax = fig.subplot(919, title="StrMethodFormatter") ax.format(xlim=(0, 100), xtickminor=False, xlocator=20, xformatter="{x:.1f}") fig.format(ylocator="null", suptitle="Tick formatters demo") uplt.rc.reset()Datetime ticks
The above examples all assumed typical “numeric” axes. However format()
can also modify the tick locations and tick labels for “datetime” axes. To draw ticks on each occurence of some particular time unit, use a unit string (e.g., xlocator='month'
). To draw ticks every N
time units, use a (unit, N) tuple (e.g., xlocator=('day', 5)
). For % style formatting of datetime tick labels with strftime()
, you can use a string containing '%'
(e.g. xformatter='%Y-%m-%d'
). By default, x axis datetime axis labels are rotated 90 degrees, like in pandas. This can be disabled by passing xrotation=0
to format()
or by setting rc['formatter.timerotation']
to 0
. See Locator
and Formatter
for details.
import ultraplot as uplt import numpy as np uplt.rc.update( metawidth=1.2, fontsize=10, ticklenratio=0.7, figurefacecolor="w", axesfacecolor="pastel blue", titleloc="upper center", titleborder=False, ) fig, axs = uplt.subplots(nrows=5, refwidth=6, refaspect=(8, 1), share=False) # Default date locator # This is enabled if you plot datetime data or set datetime limits ax = axs[0] ax.format( xlim=(np.datetime64("2000-01-01"), np.datetime64("2001-01-02")), title="Auto date locator and formatter", ) # Concise date formatter introduced in matplotlib 3.1 ax = axs[1] ax.format( xlim=(np.datetime64("2000-01-01"), np.datetime64("2001-01-01")), xformatter="concise", title="Concise date formatter", ) # Minor ticks every year, major every 10 years ax = axs[2] ax.format( xlim=(np.datetime64("2000-01-01"), np.datetime64("2050-01-01")), xlocator=("year", 10), xformatter="'%y", title="Ticks every N units", ) # Minor ticks every 10 minutes, major every 2 minutes ax = axs[3] ax.format( xlim=(np.datetime64("2000-01-01T00:00:00"), np.datetime64("2000-01-01T12:00:00")), xlocator=("hour", range(0, 24, 2)), xminorlocator=("minute", range(0, 60, 10)), xformatter="T%H:%M:%S", title="Ticks at specific intervals", ) # Month and year labels, with default tick label rotation ax = axs[4] ax.format( xlim=(np.datetime64("2000-01-01"), np.datetime64("2008-01-01")), xlocator="year", xminorlocator="month", # minor ticks every month xformatter="%b %Y", title="Ticks with default rotation", ) axs[:4].format(xrotation=0) # no rotation for the first four examples fig.format(ylocator="null", suptitle="Datetime locators and formatters demo") uplt.rc.reset()Axis positions
The locations of axis spines, tick marks, tick labels, and axis labels can be controlled with ultraplot.axes.CartesianAxes.format()
keyword arguments like xspineloc (shorthand xloc), xtickloc, xticklabelloc, and xlabelloc. Valid locations include 'left'
, 'right'
, 'top'
, 'bottom'
, 'neither'
, 'none'
, or 'both'
. Spine locations can also be set to a valid set_position()
value, e.g. 'zero'
or ('axes', 1.5)
. The top or right spine is used when the coordinate is more than halfway across the axes. This is often convenient when passing e.g. loc to “alternate” axes commands. These keywords provide the functionality of matplotlib’s tick_left()
, tick_right()
, tick_top()
, and tick_bottom()
, and set_position()
, but with additional flexibility.
import ultraplot as uplt uplt.rc.update( metawidth=1.2, fontsize=10, gridcolor="coral", axesedgecolor="deep orange", figurefacecolor="white", ) fig = uplt.figure(share=False, refwidth=2, suptitle="Axis locations demo") # Spine location demonstration ax = fig.subplot(121, title="Various locations") ax.format(xloc="top", xlabel="original axis") ax.twiny(xloc="bottom", xcolor="black", xlabel="locked twin") ax.twiny(xloc=("axes", 1.25), xcolor="black", xlabel="offset twin") ax.twiny(xloc=("axes", -0.25), xcolor="black", xlabel="offset twin") ax.format(ytickloc="both", yticklabelloc="both") ax.format(ylabel="labels on both sides") # Other locations locations ax = fig.subplot(122, title="Zero-centered spines", titlepad="1em") ax.format(xlim=(-10, 10), ylim=(-3, 3), yticks=1) ax.format(xloc="zero", yloc="zero") uplt.rc.reset()Axis scales
“Axis scales” like 'linear'
and 'log'
control the x and y axis coordinate system. To change the axis scale, pass e.g. xscale='log'
or yscale='log'
to format()
. This is powered by the Scale
constructor function. UltraPlot makes several changes to the axis scale API:
The AutoFormatter
formatter is now used for all axis scales by default, including 'log'
and 'symlog'
. Matplotlib’s behavior can be restored by passing e.g. xformatter='log'
or yformatter='log'
to format()
.
To make its behavior consistent with Locator
and Formatter
, the Scale
constructor function returns instances of ScaleBase
, and set_xscale()
and set_yscale()
now accept these class instances in addition to “registered” names like 'log'
.
While matplotlib axis scales must be instantiated with an Axis
instance (for backwards compatibility reasons), UltraPlot axis scales can be instantiated without the axis instance (e.g., uplt.LogScale()
instead of uplt.LogScale(ax.xaxis)
).
The default subs for the 'symlog'
axis scale is now np.arange(1, 10)
, and the default linthresh is now 1
. Also the 'log'
and 'symlog'
axis scales now accept the keywords base, linthresh, linscale, and subs rather than keywords with trailing x
or y
.
UltraPlot also includes a few new axis scales. The 'cutoff'
scale CutoffScale
is useful when the statistical distribution of your data is very unusual. The 'sine'
scale SineLatitudeScale
scales the axis with a sine function (resulting in an area-weighted spherical latitude coordinate) and the 'mercator'
scale MercatorLatitudeScale
scales the axis with the Mercator projection latitude coordinate. The 'inverse'
scale InverseScale
can be useful when working with spectral data, especially with “dual” unit axes. If you want to work with the axis scale classes directly, they are available in the top-level namespace (e.g., xscale=uplt.CutoffScale(...)
is allowed).
import ultraplot as uplt import numpy as np N = 200 lw = 3 uplt.rc.update({"meta.width": 1, "label.weight": "bold", "tick.labelweight": "bold"}) fig = uplt.figure(refwidth=1.8, share=False) # Linear and log scales ax1 = fig.subplot(221) ax1.format(yscale="linear", ylabel="linear scale") ax2 = fig.subplot(222) ax2.format(ylim=(1e-3, 1e3), yscale="log", ylabel="log scale") for ax in (ax1, ax2): ax.plot(np.linspace(0, 1, N), np.linspace(0, 1000, N), lw=lw) # Symlog scale ax = fig.subplot(223) ax.format(yscale="symlog", ylabel="symlog scale") ax.plot(np.linspace(0, 1, N), np.linspace(-1000, 1000, N), lw=lw) # Logit scale ax = fig.subplot(224) ax.format(yscale="logit", ylabel="logit scale") ax.plot(np.linspace(0, 1, N), np.linspace(0.01, 0.99, N), lw=lw) fig.format(suptitle="Axis scales demo", ytickminor=True) uplt.rc.reset()
import ultraplot as uplt import numpy as np # Create figure x = np.linspace(0, 4 * np.pi, 100) dy = np.linspace(-1, 1, 5) ys = (np.sin(x), np.cos(x)) state = np.random.RandomState(51423) data = state.rand(len(dy) - 1, len(x) - 1) colors = ("coral", "sky blue") cmap = uplt.Colormap("grays", right=0.8) fig, axs = uplt.subplots(nrows=4, refaspect=(5, 1), figwidth=5.5, sharex=False) # Loop through various cutoff scale options titles = ("Zoom out of left", "Zoom into left", "Discrete jump", "Fast jump") args = ( (np.pi, 3), # speed up (3 * np.pi, 1 / 3), # slow down (np.pi, np.inf, 3 * np.pi), # discrete jump (np.pi, 5, 3 * np.pi), # fast jump ) locators = ( np.pi / 3, np.pi / 3, np.pi * np.append(np.linspace(0, 1, 4), np.linspace(3, 4, 4)), np.pi * np.append(np.linspace(0, 1, 4), np.linspace(3, 4, 4)), ) for ax, iargs, title, locator in zip(axs, args, titles, locators): ax.pcolormesh(x, dy, data, cmap=cmap) for y, color in zip(ys, colors): ax.plot(x, y, lw=4, color=color) ax.format( # xscale=("cutoff", *iargs), xlim=(0, 4 * np.pi), xlocator=locator, xformatter="pi", xtickminor=False, ygrid=False, ylabel="wave amplitude", title=title, suptitle="Cutoff axis scales demo", )
import ultraplot as uplt import numpy as np # Create figure n = 30 state = np.random.RandomState(51423) data = state.rand(n - 1, n - 1) colors = ("coral", "sky blue") cmap = uplt.Colormap("grays", right=0.8) gs = uplt.GridSpec(nrows=2, ncols=2) fig = uplt.figure(refwidth=2.3, share=False) fig.format(grid=False, suptitle="Other axis scales demo") # Geographic scales x = np.linspace(-180, 180, n) y = np.linspace(-85, 85, n) for i, scale in enumerate(("sine", "mercator")): ax = fig.subplot(gs[i, 0]) ax.plot(x, y, "-", color=colors[i], lw=4) ax.pcolormesh(x, y, data, cmap="grays", cmap_kw={"right": 0.8}) ax.format( yscale=scale, title=scale.title() + " scale", ylim=(-85, 85), ylocator=20, yformatter="deg", ) # Exponential scale n = 50 x = np.linspace(0, 1, n) y = 3 * np.linspace(0, 1, n) data = state.rand(len(y) - 1, len(x) - 1) ax = fig.subplot(gs[0, 1]) title = "Exponential $e^x$ scale" ax.pcolormesh(x, y, data, cmap="grays", cmap_kw={"right": 0.8}) ax.plot(x, y, lw=4, color=colors[0]) ax.format(ymin=0.05, yscale=("exp", np.e), title=title) # Power scale ax = fig.subplot(gs[1, 1]) title = "Power $x^{0.5}$ scale" ax.pcolormesh(x, y, data, cmap="grays", cmap_kw={"right": 0.8}) ax.plot(x, y, lw=4, color=colors[1]) ax.format(ymin=0.05, yscale=("power", 0.5), title=title)Alternate axes
The Axes
class includes twinx()
and twiny()
commands for drawing “twin” x and y axes in the same subplot. UltraPlot expands on these commands and adds the arguably more intuitive altx()
and alty()
options. Here altx()
is equivalent to twiny()
(makes an alternate x axes and an identical twin y axes) and alty()
is equivalent to twinx()
(makes an alternate y axes and an identical twin x axes). The UltraPlot versions can be quickly formatted by passing ultraplot.axes.CartesianAxes.format()
keyword arguments to the commands (e.g., ax.alty(ycolor='red')
or, since the y
prefix in this context is redundant, just ax.alty(color='red')
). They also enforce sensible default locations for the spines, ticks, and labels, and disable the twin axes background patch and gridlines by default.
Note
Unlike matplotlib, UltraPlot adds alternate axes as children of the original axes. This helps simplify the tight layout algorithm but means that the drawing order is controlled by the difference between the zorders of the alternate axes and the content inside the original axes rather than the zorder of the original axes itself (see this issue page for details).
import ultraplot as uplt import numpy as np state = np.random.RandomState(51423) c0 = "gray5" c1 = "red8" c2 = "blue8" N, M = 50, 10 # Alternate y axis data = state.rand(M) + (state.rand(N, M) - 0.48).cumsum(axis=0) altdata = 5 * (state.rand(N) - 0.45).cumsum(axis=0) fig = uplt.figure(share=False) ax = fig.subplot(121, title="Alternate y twin x") ax.line(data, color=c0, ls="--") ox = ax.alty(color=c2, label="alternate ylabel", linewidth=1) ox.line(altdata, color=c2) # Alternate x axis data = state.rand(M) + (state.rand(N, M) - 0.48).cumsum(axis=0) altdata = 5 * (state.rand(N) - 0.45).cumsum(axis=0) ax = fig.subplot(122, title="Alternate x twin y") ax.linex(data, color=c0, ls="--") ox = ax.altx(color=c1, label="alternate xlabel", linewidth=1) ox.linex(altdata, color=c1) fig.format(xlabel="xlabel", ylabel="ylabel", suptitle="Alternate axes demo")Dual unit axes
The dualx()
and dualy()
methods can be used to draw duplicate x and y axes meant to represent alternate units in the same coordinate range as the “parent” axis. This feature is powered by the FuncScale
class. dualx()
and dualy()
accept the same axis formatting keyword arguments as altx()
and alty()
. The alternate units are specified with either of the following three positional arguments:
A single linear forward function.
A 2-tuple of arbitrary forward and inverse functions.
An axis scale name or class instance.
In the third case, the axis scale transforms are used for the forward and inverse functions, and the default axis scale locators and formatters are used for the default dual axis locators and formatters. In the below examples, we generate dual axes with each of these three methods. Note that the “parent” axis scale is arbitrary – in the first example, we create a dualx()
axis for a symlog-scaled axis.
import ultraplot as uplt uplt.rc.update({"grid.alpha": 0.4, "meta.width": 1, "grid.linewidth": 1}) c1 = uplt.scale_luminance("cerulean", 0.5) c2 = uplt.scale_luminance("red", 0.5) fig = uplt.figure(refaspect=2.2, refwidth=3, share=False) axs = fig.subplots( [[1, 1, 2, 2], [0, 3, 3, 0]], suptitle="Duplicate axes with simple transformations", ylocator=[], yformatter=[], xcolor=c1, gridcolor=c1, ) # Meters and kilometers ax = axs[0] ax.format(xlim=(0, 5000), xlabel="meters") ax.dualx(lambda x: x * 1e-3, label="kilometers", grid=True, color=c2, gridcolor=c2) # Kelvin and Celsius ax = axs[1] ax.format(xlim=(200, 300), xlabel="temperature (K)") ax.dualx( lambda x: x - 273.15, label="temperature (\N{DEGREE SIGN}C)", grid=True, color=c2, gridcolor=c2, ) # With symlog parent ax = axs[2] ax.format(xlim=(-100, 100), xscale="symlog", xlabel="MegaJoules") ax.dualx( lambda x: x * 1e6, label="Joules", formatter="log", grid=True, color=c2, gridcolor=c2, ) uplt.rc.reset()
import ultraplot as uplt uplt.rc.update({"grid.alpha": 0.4, "meta.width": 1, "grid.linewidth": 1}) c1 = uplt.scale_luminance("cerulean", 0.5) c2 = uplt.scale_luminance("red", 0.5) fig = uplt.figure( share=False, refaspect=0.4, refwidth=1.8, suptitle="Duplicate axes with pressure and height", ) # Pressure as the linear scale, height on opposite axis (scale height 7km) ax = fig.subplot(121) ax.format( xformatter="null", ylabel="pressure (hPa)", ylim=(1000, 10), xlocator=[], ycolor=c1, gridcolor=c1, ) ax.dualy("height", label="height (km)", ticks=2.5, color=c2, gridcolor=c2, grid=True) # Height as the linear scale, pressure on opposite axis (scale height 7km) ax = fig.subplot(122) ax.format( xformatter="null", ylabel="height (km)", ylim=(0, 20), xlocator="null", grid=True, gridcolor=c2, ycolor=c2, ) ax.dualy( "pressure", label="pressure (hPa)", locator=100, color=c1, gridcolor=c1, grid=True ) uplt.rc.reset()
import ultraplot as uplt import numpy as np uplt.rc.margin = 0 c1 = uplt.scale_luminance("cerulean", 0.5) c2 = uplt.scale_luminance("red", 0.5) fig, ax = uplt.subplots(refaspect=(3, 1), figwidth=6) # Sample data cutoff = 1 / 5 x = np.linspace(0.01, 0.5, 1000) # in wavenumber days response = (np.tanh(-((x - cutoff) / 0.03)) + 1) / 2 # response func ax.axvline(cutoff, lw=2, ls="-", color=c2) ax.fill_between([cutoff - 0.03, cutoff + 0.03], 0, 1, color=c2, alpha=0.3) ax.plot(x, response, color=c1, lw=2) # Add inverse scale to top ax.format( title="Imaginary response function", suptitle="Duplicate axes with wavenumber and period", xlabel="wavenumber (days$^{-1}$)", ylabel="response", grid=False, ) ax = ax.dualx( "inverse", locator="log", locator_kw={"subs": (1, 2, 5)}, label="period (days)" ) uplt.rc.reset()
RetroSearch is an open source project built by @garambo | Open a GitHub Issue
Search and Browse the WWW like it's 1997 | Search results from DuckDuckGo
HTML:
3.2
| Encoding:
UTF-8
| Version:
0.7.4