1
+ """
2
+ Example of creating a radar chart (a.k.a. a spider or star chart) [1]_.
3
+
4
+ .. [1] http://en.wikipedia.org/wiki/Radar_chart
5
+ """
1
6
import numpy as np
2
7
3
8
import matplotlib .pyplot as plt
8
13
9
14
10
15
def radar_factory (num_vars , frame = 'circle' ):
11
- """Create a radar chart with `num_vars` axes."""
16
+ """Create a radar chart with `num_vars` axes.
17
+
18
+ This function creates a RadarAxes projection and registers it.
19
+
20
+ Parameters
21
+ ----------
22
+ num_vars : int
23
+ Number of variables for radar chart.
24
+ frame : {'circle' | 'polygon'}
25
+ Shape of frame surrounding axes.
26
+
27
+ """
12
28
# calculate evenly-spaced axis angles
13
29
theta = 2 * np .pi * np .linspace (0 , 1 - 1. / num_vars , num_vars )
14
30
# rotate theta such that the first axis is at the top
15
31
theta += np .pi / 2
16
32
17
- def poly_verts (x0 , y0 , r ):
18
- # TODO: use transforms to convert (x, y) to (r, theta)
19
- verts = [(r * np .cos (t ) + x0 , r * np .sin (t ) + y0 ) for t in theta ]
20
- return verts
21
-
22
- def draw_poly_frame (self , x0 , y0 , r ):
23
- verts = poly_verts (x0 , y0 , r )
33
+ def draw_poly_patch (self ):
34
+ verts = unit_poly_verts (theta )
24
35
return plt .Polygon (verts , closed = True , edgecolor = 'k' )
25
36
26
- def draw_circle_frame (self , x0 , y0 , r ):
27
- return plt .Circle ((x0 , y0 ), r )
37
+ def draw_circle_patch (self ):
38
+ # unit circle centered on (0.5, 0.5)
39
+ return plt .Circle ((0.5 , 0.5 ), 0.5 )
28
40
29
- frame_dict = {'polygon' : draw_poly_frame , 'circle' : draw_circle_frame }
30
- if frame not in frame_dict :
41
+ patch_dict = {'polygon' : draw_poly_patch , 'circle' : draw_circle_patch }
42
+ if frame not in patch_dict :
31
43
raise ValueError , 'unknown value for `frame`: %s' % frame
32
44
33
45
class RadarAxes (PolarAxes ):
34
- """Class for creating a radar chart (a.k.a. a spider or star chart)
35
46
36
- http://en.wikipedia.org/wiki/Radar_chart
37
- """
38
47
name = 'radar'
39
48
# use 1 line segment to connect specified points
40
49
RESOLUTION = 1
41
50
# define draw_frame method
42
- draw_frame = frame_dict [frame ]
51
+ draw_patch = patch_dict [frame ]
43
52
44
53
def fill (self , * args , ** kwargs ):
45
54
"""Override fill so that line is closed by default"""
@@ -64,9 +73,7 @@ def set_varlabels(self, labels):
64
73
self .set_thetagrids (theta * 180 / np .pi , labels )
65
74
66
75
def _gen_axes_patch (self ):
67
- x0 , y0 = (0.5 , 0.5 )
68
- r = 0.5
69
- return self .draw_frame (x0 , y0 , r )
76
+ return self .draw_patch ()
70
77
71
78
def _gen_axes_spines (self ):
72
79
if frame == 'circle' :
@@ -76,10 +83,7 @@ def _gen_axes_spines(self):
76
83
77
84
# spine_type must be 'left', 'right', 'top', 'bottom', or `circle`.
78
85
spine_type = 'circle'
79
- r = 0.5
80
- x0 , y0 = (0.5 , 0.5 )
81
- #verts = [(t, r) for t in theta]
82
- verts = poly_verts (x0 , y0 , r )
86
+ verts = unit_poly_verts (theta )
83
87
# close off polygon by repeating first vertex
84
88
verts .append (verts [0 ])
85
89
path = Path (verts )
@@ -92,7 +96,17 @@ def _gen_axes_spines(self):
92
96
return theta
93
97
94
98
95
- if __name__ == '__main__' :
99
+ def unit_poly_verts (theta ):
100
+ """Return vertices of polygon for subplot axes.
101
+
102
+ This polygon is circumscribed by a unit circle centered at (0.5, 0.5)
103
+ """
104
+ x0 , y0 , r = [0.5 ] * 3
105
+ verts = [(r * np .cos (t ) + x0 , r * np .sin (t ) + y0 ) for t in theta ]
106
+ return verts
107
+
108
+
109
+ def example_data ():
96
110
#The following data is from the Denver Aerosol Sources and Health study.
97
111
#See doi:10.1016/j.atmosenv.2008.12.017
98
112
#
@@ -111,51 +125,52 @@ def _gen_axes_spines(self):
111
125
# 2)Inclusion of gas-phase specie carbon monoxide (CO)
112
126
# 3)Inclusion of gas-phase specie ozone (O3).
113
127
# 4)Inclusion of both gas-phase speciesis present...
128
+ data = {
129
+ 'column names' :
130
+ ['Sulfate' , 'Nitrate' , 'EC' , 'OC1' , 'OC2' , 'OC3' , 'OP' , 'CO' , 'O3' ],
131
+ 'Basecase' :
132
+ [[0.88 , 0.01 , 0.03 , 0.03 , 0.00 , 0.06 , 0.01 , 0.00 , 0.00 ],
133
+ [0.07 , 0.95 , 0.04 , 0.05 , 0.00 , 0.02 , 0.01 , 0.00 , 0.00 ],
134
+ [0.01 , 0.02 , 0.85 , 0.19 , 0.05 , 0.10 , 0.00 , 0.00 , 0.00 ],
135
+ [0.02 , 0.01 , 0.07 , 0.01 , 0.21 , 0.12 , 0.98 , 0.00 , 0.00 ],
136
+ [0.01 , 0.01 , 0.02 , 0.71 , 0.74 , 0.70 , 0.00 , 0.00 , 0.00 ]],
137
+ 'With CO' :
138
+ [[0.88 , 0.02 , 0.02 , 0.02 , 0.00 , 0.05 , 0.00 , 0.05 , 0.00 ],
139
+ [0.08 , 0.94 , 0.04 , 0.02 , 0.00 , 0.01 , 0.12 , 0.04 , 0.00 ],
140
+ [0.01 , 0.01 , 0.79 , 0.10 , 0.00 , 0.05 , 0.00 , 0.31 , 0.00 ],
141
+ [0.00 , 0.02 , 0.03 , 0.38 , 0.31 , 0.31 , 0.00 , 0.59 , 0.00 ],
142
+ [0.02 , 0.02 , 0.11 , 0.47 , 0.69 , 0.58 , 0.88 , 0.00 , 0.00 ]],
143
+ 'With O3' :
144
+ [[0.89 , 0.01 , 0.07 , 0.00 , 0.00 , 0.05 , 0.00 , 0.00 , 0.03 ],
145
+ [0.07 , 0.95 , 0.05 , 0.04 , 0.00 , 0.02 , 0.12 , 0.00 , 0.00 ],
146
+ [0.01 , 0.02 , 0.86 , 0.27 , 0.16 , 0.19 , 0.00 , 0.00 , 0.00 ],
147
+ [0.01 , 0.03 , 0.00 , 0.32 , 0.29 , 0.27 , 0.00 , 0.00 , 0.95 ],
148
+ [0.02 , 0.00 , 0.03 , 0.37 , 0.56 , 0.47 , 0.87 , 0.00 , 0.00 ]],
149
+ 'CO & O3' :
150
+ [[0.87 , 0.01 , 0.08 , 0.00 , 0.00 , 0.04 , 0.00 , 0.00 , 0.01 ],
151
+ [0.09 , 0.95 , 0.02 , 0.03 , 0.00 , 0.01 , 0.13 , 0.06 , 0.00 ],
152
+ [0.01 , 0.02 , 0.71 , 0.24 , 0.13 , 0.16 , 0.00 , 0.50 , 0.00 ],
153
+ [0.01 , 0.03 , 0.00 , 0.28 , 0.24 , 0.23 , 0.00 , 0.44 , 0.88 ],
154
+ [0.02 , 0.00 , 0.18 , 0.45 , 0.64 , 0.55 , 0.86 , 0.00 , 0.16 ]]
155
+ }
156
+ return data
157
+
158
+
159
+ if __name__ == '__main__' :
114
160
N = 9
115
161
theta = radar_factory (N , frame = 'polygon' )
116
- spoke_labels = ['Sulfate' , 'Nitrate' , 'EC' , 'OC1' , 'OC2' , 'OC3' , 'OP' , 'CO' ,
117
- 'O3' ]
118
- f1_base = [0.88 , 0.01 , 0.03 , 0.03 , 0.00 , 0.06 , 0.01 , 0.00 , 0.00 ]
119
- f1_CO = [0.88 , 0.02 , 0.02 , 0.02 , 0.00 , 0.05 , 0.00 , 0.05 , 0.00 ]
120
- f1_O3 = [0.89 , 0.01 , 0.07 , 0.00 , 0.00 , 0.05 , 0.00 , 0.00 , 0.03 ]
121
- f1_both = [0.87 , 0.01 , 0.08 , 0.00 , 0.00 , 0.04 , 0.00 , 0.00 , 0.01 ]
122
-
123
- f2_base = [0.07 , 0.95 , 0.04 , 0.05 , 0.00 , 0.02 , 0.01 , 0.00 , 0.00 ]
124
- f2_CO = [0.08 , 0.94 , 0.04 , 0.02 , 0.00 , 0.01 , 0.12 , 0.04 , 0.00 ]
125
- f2_O3 = [0.07 , 0.95 , 0.05 , 0.04 , 0.00 , 0.02 , 0.12 , 0.00 , 0.00 ]
126
- f2_both = [0.09 , 0.95 , 0.02 , 0.03 , 0.00 , 0.01 , 0.13 , 0.06 , 0.00 ]
127
-
128
- f3_base = [0.01 , 0.02 , 0.85 , 0.19 , 0.05 , 0.10 , 0.00 , 0.00 , 0.00 ]
129
- f3_CO = [0.01 , 0.01 , 0.79 , 0.10 , 0.00 , 0.05 , 0.00 , 0.31 , 0.00 ]
130
- f3_O3 = [0.01 , 0.02 , 0.86 , 0.27 , 0.16 , 0.19 , 0.00 , 0.00 , 0.00 ]
131
- f3_both = [0.01 , 0.02 , 0.71 , 0.24 , 0.13 , 0.16 , 0.00 , 0.50 , 0.00 ]
132
-
133
- f4_base = [0.02 , 0.01 , 0.07 , 0.01 , 0.21 , 0.12 , 0.98 , 0.00 , 0.00 ]
134
- f4_CO = [0.00 , 0.02 , 0.03 , 0.38 , 0.31 , 0.31 , 0.00 , 0.59 , 0.00 ]
135
- f4_O3 = [0.01 , 0.03 , 0.00 , 0.32 , 0.29 , 0.27 , 0.00 , 0.00 , 0.95 ]
136
- f4_both = [0.01 , 0.03 , 0.00 , 0.28 , 0.24 , 0.23 , 0.00 , 0.44 , 0.88 ]
137
-
138
- f5_base = [0.01 , 0.01 , 0.02 , 0.71 , 0.74 , 0.70 , 0.00 , 0.00 , 0.00 ]
139
- f5_CO = [0.02 , 0.02 , 0.11 , 0.47 , 0.69 , 0.58 , 0.88 , 0.00 , 0.00 ]
140
- f5_O3 = [0.02 , 0.00 , 0.03 , 0.37 , 0.56 , 0.47 , 0.87 , 0.00 , 0.00 ]
141
- f5_both = [0.02 , 0.00 , 0.18 , 0.45 , 0.64 , 0.55 , 0.86 , 0.00 , 0.16 ]
142
-
143
- fig = plt .figure (figsize = (9 ,9 ))
144
- # adjust spacing around the subplots
162
+
163
+ data = example_data ()
164
+ spoke_labels = data .pop ('column names' )
165
+
166
+ fig = plt .figure (figsize = (9 , 9 ))
145
167
fig .subplots_adjust (wspace = 0.25 , hspace = 0.20 , top = 0.85 , bottom = 0.05 )
146
- title_list = ['Basecase' , 'With CO' , 'With O3' , 'CO & O3' ]
147
- data = {'Basecase' : [f1_base , f2_base , f3_base , f4_base , f5_base ],
148
- 'With CO' : [f1_CO , f2_CO , f3_CO , f4_CO , f5_CO ],
149
- 'With O3' : [f1_O3 , f2_O3 , f3_O3 , f4_O3 , f5_O3 ],
150
- 'CO & O3' : [f1_both , f2_both , f3_both , f4_both , f5_both ]}
151
- colors = ['b' , 'r' , 'g' , 'm' , 'y' ]
152
- # chemicals range from 0 to 1
153
- radial_grid = [0.2 , 0.4 , 0.6 , 0.8 ]
154
168
155
- # If you don't care about the order, you can loop over data_dict.items()
156
- for n , title in enumerate (title_list ):
169
+ colors = ['b' , 'r' , 'g' , 'm' , 'y' ]
170
+ # Plot the four cases from the example data on separate axes
171
+ for n , title in enumerate (data .keys ()):
157
172
ax = fig .add_subplot (2 , 2 , n + 1 , projection = 'radar' )
158
- plt .rgrids (radial_grid )
173
+ plt .rgrids ([ 0.2 , 0.4 , 0.6 , 0.8 ] )
159
174
ax .set_title (title , weight = 'bold' , size = 'medium' , position = (0.5 , 1.1 ),
160
175
horizontalalignment = 'center' , verticalalignment = 'center' )
161
176
for d , color in zip (data [title ], colors ):
0 commit comments