-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathtaylor_diagram.py
131 lines (100 loc) · 4.88 KB
/
taylor_diagram.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
# Copyright: Modified from below document which has been placed in the public domain. #Jill Zhang, Dec 2017
"""
Taylor diagram (Taylor, 2001) test implementation.
http://www-pcmdi.llnl.gov/about/staff/Taylor/CV/Taylor_diagram_primer.htm
"""
__version__ = "Time-stamp: <2012-02-17 20:59:35 ycopin>"
__author__ = "Yannick Copin <[email protected]>"
import numpy as np
from numpy import genfromtxt
import matplotlib
matplotlib.use('Agg')
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='_'):
"""Set up Taylor diagram axes, i.e. single quadrant polar
plot, using mpl_toolkits.axisartist.floating_axes. refstd is
the reference standard deviation to be compared to.
"""
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
gl2_num = np.linspace(0,1.5,7)
gl2 = GF.FixedLocator(gl2_num)
gl2_ticks = [(x, str(x)) for x in gl2_num]
gl2_ticks[-1] = [gl2_num[-1], '']
gl2_ticks[0] = [gl2_num[0], '0']
tf1 = GF.DictFormatter(dict(list(zip(tlocs, list(map(str,rlocs))))))
tf2 = GF.DictFormatter(dict(gl2_ticks))
# Standard deviation axis extent
self.smin = min(gl2_num)
#self.smax = 1.5*self.refstd
self.smax = max(gl2_num)
ghelper = FA.GridHelperCurveLinear(tr,
extremes=(0,np.pi/2, # 1st quadrant
self.smin,self.smax),
grid_locator1=gl1,
grid_locator2=gl2,
tick_formatter1=tf1,
tick_formatter2=tf2
)
# 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 (Normalized)")
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
# Contours along standard deviations
ax.grid(False)
# Shrink current axis by 10%
box = ax.get_position()
ax.set_position([box.x0, box.y0, box.width * 0.9, box.height * 0.9])
# Put a legend to the right of the current axis
#ax.legend(loc='center left', bbox_to_anchor=(1, 0.5))
self._ax = ax # Graphical axes
self.ax = ax.get_aux_axes(tr) # Polar coordinates
# Add reference point and stddev contour
#print "Reference std:", self.refstd
l, = self.ax.plot([0], self.refstd, 'k*',
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]
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_contours(self, levels=5, **kwargs):
"""Add constant centered RMS difference contours."""
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