"""
taylor
==========
"""
import numpy as np
import matplotlib.pyplot as plt
[docs]class TaylorDiagram(object):
"""Taylor diagram.
Plot model standard deviation and correlation to reference (data)
sample in a single-quadrant polar plot, with r=stddev and
theta=arccos(correlation).
"""
def __init__(self, refstd, fig=None, rect=111, label='_', srange=(0,1.5)):
"""Set up Taylor diagram axes, i.e. single quadrant polar
plot, using `mpl_toolkits.axisartist.floating_axes`.
Parameters:
* refstd: reference standard deviation to be compared to
* fig: input Figure or None
* rect: subplot definition
* label: reference label
* srange: stddev axis extension, in units of *refstd*
"""
from matplotlib.projections import PolarAxes
import mpl_toolkits.axisartist.floating_axes as fa
import mpl_toolkits.axisartist.grid_finder as gf
self.refstd = refstd # Reference standard deviation
tr = PolarAxes.PolarTransform()
# Correlation labels
rlocs = np.concatenate((np.arange(10)/10.,[0.95,0.99]))
tlocs = np.arccos(rlocs) # Conversion to polar angles
gl1 = gf.FixedLocator(tlocs) # Positions
tf1 = gf.DictFormatter(dict(list(zip(tlocs, list(map(str,rlocs))))))
# Standard deviation axis extent (in units of reference stddev)
self.smin = srange[0]*self.refstd
self.smax = srange[1]*self.refstd
ghelper = fa.GridHelperCurveLinear(tr,
extremes=(0, np.pi/2, # 1st quadrant
self.smin, self.smax),
grid_locator1=gl1,
tick_formatter1=tf1)
if fig is None:
fig = plt.figure()
ax = fa.FloatingSubplot(fig, rect, grid_helper=ghelper)
fig.add_subplot(ax)
# Adjust axes
ax.axis["top"].set_axis_direction("bottom") # "Angle axis"
ax.axis["top"].toggle(ticklabels=True, label=True)
ax.axis["top"].major_ticklabels.set_axis_direction("top")
ax.axis["top"].label.set_axis_direction("top")
ax.axis["top"].label.set_text("Correlation")
ax.axis["left"].set_axis_direction("bottom") # "X axis"
ax.axis["left"].label.set_text("Standard deviation")
ax.axis["right"].set_axis_direction("top") # "Y axis"
ax.axis["right"].toggle(ticklabels=True)
ax.axis["right"].major_ticklabels.set_axis_direction("left")
ax.axis["bottom"].set_visible(False) # Useless
self._ax = ax # Graphical axes
self.ax = ax.get_aux_axes(tr) # Polar coordinates
# Add reference point and stddev contour
l, = self.ax.plot([0], self.refstd, 'b*', mec='b',
ls='', ms=10, label=label)
t = np.linspace(0, np.pi/2)
r = np.zeros_like(t) + self.refstd
self.ax.plot(t,r, 'k--', label='_')
# Collect sample points for latter use (e.g. legend)
self.samplePoints = [l]
[docs] def add_sample(self, stddev, corrcoef, *args, **kwargs):
"""Add sample (*stddev*,*corrcoeff*) to the Taylor
diagram. *args* and *kwargs* are directly propagated to the
`Figure.plot` command."""
l, = self.ax.plot(np.arccos(corrcoef), stddev,
*args, **kwargs) # (theta,radius)
self.samplePoints.append(l)
return l
[docs] def add_grid(self, *args, **kwargs):
"""Add a grid."""
self._ax.grid(*args, **kwargs)
[docs] def add_contours(self, levels=5, **kwargs):
"""Add constant centered RMS difference contours, defined by
*levels*."""
rs,ts = np.meshgrid(np.linspace(self.smin,self.smax),
np.linspace(0,np.pi/2))
# Compute centered RMS difference
rms = np.sqrt(self.refstd**2 + rs**2 - 2*self.refstd*rs*np.cos(ts))
contours = self.ax.contour(ts, rs, rms, levels, **kwargs)
return contours
[docs]def test1():
"""Display a Taylor diagram in a separate axis."""
# Reference dataset
x = np.linspace(0,4*np.pi,100)
data = np.sin(x)
refstd = data.std(ddof=1) # Reference standard deviation
# Generate models
m1 = data + 0.2*np.random.randn(len(x)) # Model 1
m2 = 0.8*data + .1*np.random.randn(len(x)) # Model 2
m3 = np.sin(x-np.pi/10) # Model 3
# Compute stddev and correlation coefficient of models
samples = np.array([ [m.std(ddof=1), np.corrcoef(data, m)[0,1]]
for m in (m1,m2,m3)])
fig = plt.figure(figsize=(10,4))
ax1 = fig.add_subplot(1,2,1, xlabel='X', ylabel='Y')
# Taylor diagram
dia = TaylorDiagram(refstd, fig=fig, rect=122, label="Reference")
colors = plt.matplotlib.cm.jet(np.linspace(0,1,len(samples)))
ax1.plot(x,data,'ko', label='Data')
for i,m in enumerate([m1,m2,m3]):
ax1.plot(x,m, c=colors[i], label='Model %d' % (i+1))
ax1.legend(numpoints=1, prop=dict(size='small'), loc='best')
# Add the models to Taylor diagram
for i,(stddev,corrcoef) in enumerate(samples):
dia.add_sample(stddev, corrcoef,
marker='$%d$' % (i+1), ms=10, ls='',
mfc=colors[i], mec=colors[i],
label="Model %d" % (i+1))
# Add grid
dia.add_grid()
# Add RMS contours, and label them
contours = dia.add_contours(colors='0.5')
plt.clabel(contours, inline=1, fontsize=10)
# Add a figure legend
fig.legend(dia.samplePoints,
[ p.get_label() for p in dia.samplePoints ],
numpoints=1, prop=dict(size='small'), loc='upper right')
return fig
[docs]def test2():
"""Climatology-oriented example (after iteration w/ Michael
A. Rawlins)."""
# Reference std
stdref = 48.491
# Samples std,rho,name
samples = [[25.939, 0.385, "Model A"],
[29.593, 0.509, "Model B"],
[33.125, 0.585, "Model C"],
[29.593, 0.509, "Model D"],
[71.215, 0.473, "Model E"],
[27.062, 0.360, "Model F"],
[38.449, 0.342, "Model G"],
[35.807, 0.609, "Model H"],
[17.831, 0.360, "Model I"]]
fig = plt.figure()
dia = TaylorDiagram(stdref, fig=fig, label='Reference')
dia.samplePoints[0].set_color('r') # Mark reference point as a red star
# Add models to Taylor diagram
for i,(stddev,corrcoef,name) in enumerate(samples):
dia.add_sample(stddev, corrcoef,
marker='$%d$' % (i+1), ms=10, ls='',
mfc='k', mec='k',
label=name)
# Add RMS contours, and label them
contours = dia.add_contours(levels=5, colors='0.5') # 5 levels in grey
plt.clabel(contours, inline=1, fontsize=10, fmt='%.1f')
# Add a figure legend and title
fig.legend(dia.samplePoints,
[ p.get_label() for p in dia.samplePoints ],
numpoints=1, prop=dict(size='small'), loc='upper right')
fig.suptitle("Taylor diagram", size='x-large') # Figure title
return fig
if __name__=='__main__':
test1()
test2()
plt.show()