Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
72 changes: 72 additions & 0 deletions docs/examples/plot_types/12_taylor_diagram.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
"""
Taylor Diagram
==============

Taylor diagrams compare model skill with correlation coefficient, standard
deviation, and centered RMS difference in a single polar-style plot.

Why UltraPlot here?
-------------------
UltraPlot exposes Taylor diagrams as a projection, so you can create them with
``proj="taylor"`` and then use regular axes methods plus convenience methods
for plotting points from correlation and standard-deviation coordinates.

Key functions: :py:meth:`ultraplot.axes.TaylorAxes.plot_corr`,
:py:meth:`ultraplot.axes.TaylorAxes.scatter_corr`.

See also
--------
* :doc:`Geographic and polar axes </projections>`
"""

import numpy as np

import ultraplot as uplt

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()
65 changes: 65 additions & 0 deletions docs/projections.py
Original file line number Diff line number Diff line change
Expand Up @@ -108,6 +108,71 @@
rlocator=2,
)

# %% [raw] raw_mimetype="text/restructuredtext"
# .. _ug_taylor:
#
# Taylor diagrams
# ---------------
#
# To create Taylor diagrams, pass ``proj='taylor'`` to an axes-creation command.
# Taylor axes are represented with the :class:`~ultraplot.axes.TaylorAxes`
# subclass and are useful for comparing model output against a reference series:
# angular gridlines represent correlation coefficients and radial gridlines
# represent standard deviation. The :meth:`~ultraplot.axes.TaylorAxes.plot_corr`
# and :meth:`~ultraplot.axes.TaylorAxes.scatter_corr` helpers accept correlation
# and standard-deviation coordinates directly, while regular polar plotting
# commands can still be used for annotations like centered RMS-difference
# contours.

# %%
import numpy as np

import ultraplot as uplt

labels = ("Model A", "Model B", "Model C", "Model D")
corr = np.array([0.72, 0.84, 0.91, 0.96])
std = np.array([0.86, 1.16, 1.04, 0.94])
colors = ("blue7", "orange7", "green7", "violet7")

fig, ax = uplt.subplots(proj="taylor", refwidth=4.2)
ax.format(
title="Taylor diagram",
xlabel="Standard deviation",
ylabel="",
rlim=(0, 1.5),
rlines=0.25,
corrlines=(1, 0.95, 0.9, 0.8, 0.6, 0.4, 0.2, 0),
)

theta = np.linspace(0, np.pi / 2, 121)
radius = np.linspace(0, 1.5, 121)
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=11, color="red7", label="Reference")
for label, c, s, color in zip(labels, corr, std, colors):
ax.scatter_corr(
c,
s,
s=70,
color=color,
edgecolor="white",
lw=0.8,
zorder=4,
label=label,
)
ax.legend(loc="b", ncols=3, frame=False)

# %% [raw] raw_mimetype="text/restructuredtext"
# .. _ug_geo:
#
Expand Down
1 change: 1 addition & 0 deletions ultraplot/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@
from .axes import GeoAxes as GeoAxes
from .axes import PlotAxes as PlotAxes
from .axes import PolarAxes as PolarAxes
from .axes import TaylorAxes as TaylorAxes
from .axes import ThreeAxes as ThreeAxes
from .colors import ColormapDatabase as ColormapDatabase
from .colors import ColorDatabase as ColorDatabase
Expand Down
11 changes: 10 additions & 1 deletion ultraplot/axes/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
from .plot import PlotAxes # noqa: F401
from .polar import PolarAxes
from .shared import _SharedAxes # noqa: F401
from .taylor import TaylorAxes
from .three import ThreeAxes # noqa: F401

# Prevent importing module names and set order of appearance for objects
Expand All @@ -25,6 +26,7 @@
"PlotAxes",
"CartesianAxes",
"PolarAxes",
"TaylorAxes",
"GeoAxes",
"ThreeAxes",
"ExternalAxesContainer",
Expand All @@ -34,7 +36,14 @@
# NOTE: We integrate with cartopy and basemap rather than using matplotlib's
# native projection system. Therefore axes names are not part of public API.
_cls_dict = {} # track valid names
for _cls in (CartesianAxes, PolarAxes, _CartopyAxes, _BasemapAxes, ThreeAxes):
for _cls in (
CartesianAxes,
PolarAxes,
TaylorAxes,
_CartopyAxes,
_BasemapAxes,
ThreeAxes,
):
for _name in (_cls._name, *_cls._name_aliases):
with context._state_context(_cls, name="ultraplot_" + _name):
mproj.register_projection(_cls)
Expand Down
Loading