Skip to content

Instantly share code, notes, and snippets.

Embed
What would you like to do?
Taylor diagram for python/matplotlib [ 10.5281/zenodo.5548061 ]
#!/usr/bin/env python
# Copyright: This document has been placed in the public domain.
"""
Taylor diagram (Taylor, 2001) implementation.
Note: If you have found these software useful for your research, I would
appreciate an acknowledgment.
"""
__version__ = "Time-stamp: <2018-12-06 11:43:41 ycopin>"
__author__ = "Yannick Copin <yannick.copin@laposte.net>"
import numpy as NP
import matplotlib.pyplot as PLT
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), extend=False):
"""
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*
* extend: extend diagram to negative correlations
"""
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.array([0, 0.2, 0.4, 0.6, 0.7, 0.8, 0.9, 0.95, 0.99, 1])
if extend:
# Diagram extended to negative correlations
self.tmax = NP.pi
rlocs = NP.concatenate((-rlocs[:0:-1], rlocs))
else:
# Diagram limited to positive correlations
self.tmax = NP.pi/2
tlocs = NP.arccos(rlocs) # Conversion to polar angles
gl1 = GF.FixedLocator(tlocs) # Positions
tf1 = GF.DictFormatter(dict(zip(tlocs, 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, self.tmax, 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(
"bottom" if extend else "left")
if self.smin:
ax.axis["bottom"].toggle(ticklabels=False, label=False)
else:
ax.axis["bottom"].set_visible(False) # Unused
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, 'k*',
ls='', ms=10, label=label)
t = NP.linspace(0, self.tmax)
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]
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
def add_grid(self, *args, **kwargs):
"""Add a grid."""
self._ax.grid(*args, **kwargs)
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, self.tmax))
# 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
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",
srange=(0.5, 1.5))
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, fmt='%.2f')
# 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 dia
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', extend=True)
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='%.0f')
dia.add_grid() # Add grid
dia._ax.axis[:].major_ticks.set_tick_out(True) # Put ticks outward
# 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 dia
if __name__ == '__main__':
dia = test1()
dia = test2()
PLT.show()
#!/usr/bin/env python
__version__ = "Time-stamp: <2018-12-06 11:55:22 ycopin>"
__author__ = "Yannick Copin <yannick.copin@laposte.net>"
"""
Example of use of TaylorDiagram. Illustration dataset courtesy of Michael
Rawlins.
Rawlins, M. A., R. S. Bradley, H. F. Diaz, 2012. Assessment of regional climate
model simulation estimates over the Northeast United States, Journal of
Geophysical Research (2012JGRD..11723112R).
"""
from taylorDiagram import TaylorDiagram
import numpy as NP
import matplotlib.pyplot as PLT
# Reference std
stdrefs = dict(winter=48.491,
spring=44.927,
summer=37.664,
autumn=41.589)
# Sample std,rho: Be sure to check order and that correct numbers are placed!
samples = dict(winter=[[17.831, 0.360, "CCSM CRCM"],
[27.062, 0.360, "CCSM MM5"],
[33.125, 0.585, "CCSM WRFG"],
[25.939, 0.385, "CGCM3 CRCM"],
[29.593, 0.509, "CGCM3 RCM3"],
[35.807, 0.609, "CGCM3 WRFG"],
[38.449, 0.342, "GFDL ECP2"],
[29.593, 0.509, "GFDL RCM3"],
[71.215, 0.473, "HADCM3 HRM3"]],
spring=[[32.174, -0.262, "CCSM CRCM"],
[24.042, -0.055, "CCSM MM5"],
[29.647, -0.040, "CCSM WRFG"],
[22.820, 0.222, "CGCM3 CRCM"],
[20.505, 0.445, "CGCM3 RCM3"],
[26.917, 0.332, "CGCM3 WRFG"],
[25.776, 0.366, "GFDL ECP2"],
[18.018, 0.452, "GFDL RCM3"],
[79.875, 0.447, "HADCM3 HRM3"]],
summer=[[35.863, 0.096, "CCSM CRCM"],
[43.771, 0.367, "CCSM MM5"],
[35.890, 0.267, "CCSM WRFG"],
[49.658, 0.134, "CGCM3 CRCM"],
[28.972, 0.027, "CGCM3 RCM3"],
[60.396, 0.191, "CGCM3 WRFG"],
[46.529, 0.258, "GFDL ECP2"],
[35.230, -0.014, "GFDL RCM3"],
[87.562, 0.503, "HADCM3 HRM3"]],
autumn=[[27.374, 0.150, "CCSM CRCM"],
[20.270, 0.451, "CCSM MM5"],
[21.070, 0.505, "CCSM WRFG"],
[25.666, 0.517, "CGCM3 CRCM"],
[35.073, 0.205, "CGCM3 RCM3"],
[25.666, 0.517, "CGCM3 WRFG"],
[23.409, 0.353, "GFDL ECP2"],
[29.367, 0.235, "GFDL RCM3"],
[70.065, 0.444, "HADCM3 HRM3"]])
# Colormap (see http://www.scipy.org/Cookbook/Matplotlib/Show_colormaps)
colors = PLT.matplotlib.cm.Set1(NP.linspace(0,1,len(samples['winter'])))
# Here set placement of the points marking 95th and 99th significance
# levels. For more than 102 samples (degrees freedom > 100), critical
# correlation levels are 0.195 and 0.254 for 95th and 99th
# significance levels respectively. Set these by eyeball using the
# standard deviation x and y axis.
#x95 = [0.01, 0.68] # For Tair, this is for 95th level (r = 0.195)
#y95 = [0.0, 3.45]
#x99 = [0.01, 0.95] # For Tair, this is for 99th level (r = 0.254)
#y99 = [0.0, 3.45]
x95 = [0.05, 13.9] # For Prcp, this is for 95th level (r = 0.195)
y95 = [0.0, 71.0]
x99 = [0.05, 19.0] # For Prcp, this is for 99th level (r = 0.254)
y99 = [0.0, 70.0]
rects = dict(winter=221,
spring=222,
summer=223,
autumn=224)
fig = PLT.figure(figsize=(11,8))
fig.suptitle("Precipitations", size='x-large')
for season in ['winter','spring','summer','autumn']:
dia = TaylorDiagram(stdrefs[season], fig=fig, rect=rects[season],
label='Reference')
dia.ax.plot(x95,y95,color='k')
dia.ax.plot(x99,y99,color='k')
# Add samples to Taylor diagram
for i,(stddev,corrcoef,name) in enumerate(samples[season]):
dia.add_sample(stddev, corrcoef,
marker='$%d$' % (i+1), ms=10, ls='',
#mfc='k', mec='k', # B&W
mfc=colors[i], mec=colors[i], # Colors
label=name)
# Add RMS contours, and label them
contours = dia.add_contours(levels=5, colors='0.5') # 5 levels
dia.ax.clabel(contours, inline=1, fontsize=10, fmt='%.1f')
# Tricky: ax is the polar ax (used for plots), _ax is the
# container (used for layout)
dia._ax.set_title(season.capitalize())
# Add a figure legend and title. For loc option, place x,y tuple inside [ ].
# Can also use special options here:
# http://matplotlib.sourceforge.net/users/legend_guide.html
fig.legend(dia.samplePoints,
[ p.get_label() for p in dia.samplePoints ],
numpoints=1, prop=dict(size='small'), loc='center')
fig.tight_layout()
PLT.savefig('test_taylor_4panel.png')
PLT.show()
@MJJoyce
Copy link

MJJoyce commented Feb 19, 2014

Would you be willing to explicitly attach a license to this?

@MJJoyce
Copy link

MJJoyce commented Mar 7, 2014

Hi @ycopin, sorry to bother you again about this. I would like to integrate your TaylorDiagram class into an open source project that I work on. However, without an explicit license I'm not able to know if I'm allowed to do so or not. Would you be willing to attach an explicit license to this work?

@ycopin
Copy link
Author

ycopin commented Apr 16, 2014

Mmmm sorry I did not see your comment before. What kind of licence should I add? I'm pretty ignorant of all this business: just let me know what I should add, and I'll try.

@ycopin
Copy link
Author

ycopin commented Apr 16, 2014

OK I just added "# Copyright: This document has been placed in the public domain."

@MJJoyce
Copy link

MJJoyce commented Jul 15, 2014

Sorry I missed your comments for so long.

Regarding licensing, if you want to release something so people can use it without many/any limitations generally sticking a permissive license like the MIT or Apache v2 license on it is perfect. The Github documentation on this might be of some use as well.

https://help.github.com/articles/open-source-licensing

However, you placing it into the public domain is perfect. Thanks very much for that! I'm very glad that I can go back to using your code, it's been very useful.

Cheers!

@MJJoyce
Copy link

MJJoyce commented Jul 15, 2014

Thanks again @ycopin. I've made a note on our project that we're using your public domain code. We really appreciate you following up on this and for going the extra step to release your code to the public.

https://github.com/apache/climate

@soz1
Copy link

soz1 commented Jul 12, 2016

Dear Yannick,

I am a new user of Python. If I have x1, x2,x3 etc. how to integrated ? instead of Model1 etc I will call each data set but , how to solve other arithmetic functions , since your equ is defined as a function of one variable.

thanks in advance for your reply!
Cheers!

@PeterRochford
Copy link

PeterRochford commented Jan 14, 2017

Note that two Python packages have recently been made available within the past year for producing Taylor diagrams: SkillMetrics and verif. They can be found on PyPI and can be easily installed using pip. A Matlab version of the SkillMetrics package is also available as the Skill Metrics Toolbox. Hope this proves helpful to anyone looking to produce Taylor diagrams.

@ilgatto88
Copy link

ilgatto88 commented Jan 26, 2017

Hi,
how can I adjust the position of the TaylorDiagram on the plot?
dia.set_position([left, bottom, width, height]) is unfortunately not working.
This is how my plot looks like, I'm using rect=222 for the TaylorDiagram.
1

Thanks in advance! :)

@ycopin
Copy link
Author

ycopin commented Jan 26, 2017

@ilgatto88 As mentioned in the code (lines 78-79), this is because dia.ax points to the polar plot (the one to be used to add points/lines/etc.), while the true graphical axes (the one which can be moved around) is referred to by dia._ax:

dia._ax.set_position([left, bottom, width, height])

should work! This is not a bug, but an undocumented feature ;-)

@HowKeyIap
Copy link

HowKeyIap commented Mar 3, 2017

Hi, thanks for sharing your code. i want to know how to change the font properties of the left and right axis. I have used "dia._ax.axis[:].label.set_font_properties(fontName)" and "dia._ax.axis[:].major_ticklabels.set_font_properties(fontName)
" , but it doesn't work well for left and right axis. Thanks very much.

@lee1043
Copy link

lee1043 commented Aug 21, 2019

@ycopin thanks for sharing your code!

@sullyandro
Copy link

sullyandro commented Apr 9, 2020

Hi Yannick Copin, thanks for share.
I tested test_taylor_4panel.py, and it works very well.
But, there is an issue at the lines 95 and 96, where you may need to change to "dia._ax.plot(...)".
Thanks again.

@jess253
Copy link

jess253 commented Sep 10, 2021

Hi, thanks very much for sharing this useful code. I am new to Python and creating Taylor diagrams so I am sorry if this is an obvious question.
I see in test_taylor_4panel.py the "samples" consists of std and rho. Is rho referring to Spearman's rank correlation coefficient and can the script be used with Pearson's correlation coefficient instead?

@ycopin
Copy link
Author

ycopin commented Sep 10, 2021

I see in test_taylor_4panel.py the "samples" consists of std and rho. Is rho referring to Spearman's rank correlation coefficient and can the script be used with Pearson's correlation coefficient instead?

You input whatever correlation coeff you want!

@mickaellalande
Copy link

mickaellalande commented Oct 4, 2021

Hi @ycopin, I used your code in a recent paper and the editors are actually asking for a DOI (not just the link). Do you have one for this gist? I'll see so far if there are ok with just the link, but it would be nice to have a DOI for each version of your code in the future. You can use Zenodo for example for this! Thanks in advance for your answer and a big thanks for this piece of code that is very valuable!

@ycopin
Copy link
Author

ycopin commented Oct 4, 2021

Hi @ycopin, I used your code in a recent paper and the editors are actually asking for a DOI (not just the link). Do you have one for this gist? I'll see so far if there are ok with just the link, but it would be nice to have a DOI for each version of your code in the future. You can use Zenodo for example for this! Thanks in advance for your answer and a big thanks for this piece of code that is very valuable!

The latest version (2018-12-06) has been tagged in zenodo: 10.5281/zenodo.5548061

@anwarulhaq07
Copy link

anwarulhaq07 commented Jan 31, 2022

Hello Dear!
I am using the test_taylor_4panel.py for plotting the Global climate models. But I facing some issues

taylor

@ycopin
Copy link
Author

ycopin commented Jan 31, 2022

It works fine under 3.8. I cannot tell much from the restricted error msg...

@anwarulhaq07
Copy link

anwarulhaq07 commented Feb 1, 2022

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment