雷达图(又名蜘蛛星图)

此示例创建雷达图表,也称为蜘蛛星图。

虽然此示例允许“圆”或“多边形”的框架,但多边形框架没有合适的网格线(线条是圆形而不是多边形)。 通过将matplotlib.axis中的GRIDLINE_INTERPOLATION_STEPS设置为所需的顶点数,可以获得多边形网格,但多边形的方向不与径向轴对齐。

http://en.wikipedia.org/wiki/Radar_chart

  1. import numpy as np
  2. import matplotlib.pyplot as plt
  3. from matplotlib.path import Path
  4. from matplotlib.spines import Spine
  5. from matplotlib.projections.polar import PolarAxes
  6. from matplotlib.projections import register_projection
  7. def radar_factory(num_vars, frame='circle'):
  8. """Create a radar chart with `num_vars` axes.
  9. This function creates a RadarAxes projection and registers it.
  10. Parameters
  11. ----------
  12. num_vars : int
  13. Number of variables for radar chart.
  14. frame : {'circle' | 'polygon'}
  15. Shape of frame surrounding axes.
  16. """
  17. # calculate evenly-spaced axis angles
  18. theta = np.linspace(0, 2*np.pi, num_vars, endpoint=False)
  19. def draw_poly_patch(self):
  20. # rotate theta such that the first axis is at the top
  21. verts = unit_poly_verts(theta + np.pi / 2)
  22. return plt.Polygon(verts, closed=True, edgecolor='k')
  23. def draw_circle_patch(self):
  24. # unit circle centered on (0.5, 0.5)
  25. return plt.Circle((0.5, 0.5), 0.5)
  26. patch_dict = {'polygon': draw_poly_patch, 'circle': draw_circle_patch}
  27. if frame not in patch_dict:
  28. raise ValueError('unknown value for `frame`: %s' % frame)
  29. class RadarAxes(PolarAxes):
  30. name = 'radar'
  31. # use 1 line segment to connect specified points
  32. RESOLUTION = 1
  33. # define draw_frame method
  34. draw_patch = patch_dict[frame]
  35. def __init__(self, *args, **kwargs):
  36. super().__init__(*args, **kwargs)
  37. # rotate plot such that the first axis is at the top
  38. self.set_theta_zero_location('N')
  39. def fill(self, *args, closed=True, **kwargs):
  40. """Override fill so that line is closed by default"""
  41. return super().fill(closed=closed, *args, **kwargs)
  42. def plot(self, *args, **kwargs):
  43. """Override plot so that line is closed by default"""
  44. lines = super().plot(*args, **kwargs)
  45. for line in lines:
  46. self._close_line(line)
  47. def _close_line(self, line):
  48. x, y = line.get_data()
  49. # FIXME: markers at x[0], y[0] get doubled-up
  50. if x[0] != x[-1]:
  51. x = np.concatenate((x, [x[0]]))
  52. y = np.concatenate((y, [y[0]]))
  53. line.set_data(x, y)
  54. def set_varlabels(self, labels):
  55. self.set_thetagrids(np.degrees(theta), labels)
  56. def _gen_axes_patch(self):
  57. return self.draw_patch()
  58. def _gen_axes_spines(self):
  59. if frame == 'circle':
  60. return super()._gen_axes_spines()
  61. # The following is a hack to get the spines (i.e. the axes frame)
  62. # to draw correctly for a polygon frame.
  63. # spine_type must be 'left', 'right', 'top', 'bottom', or `circle`.
  64. spine_type = 'circle'
  65. verts = unit_poly_verts(theta + np.pi / 2)
  66. # close off polygon by repeating first vertex
  67. verts.append(verts[0])
  68. path = Path(verts)
  69. spine = Spine(self, spine_type, path)
  70. spine.set_transform(self.transAxes)
  71. return {'polar': spine}
  72. register_projection(RadarAxes)
  73. return theta
  74. def unit_poly_verts(theta):
  75. """Return vertices of polygon for subplot axes.
  76. This polygon is circumscribed by a unit circle centered at (0.5, 0.5)
  77. """
  78. x0, y0, r = [0.5] * 3
  79. verts = [(r*np.cos(t) + x0, r*np.sin(t) + y0) for t in theta]
  80. return verts
  81. def example_data():
  82. # The following data is from the Denver Aerosol Sources and Health study.
  83. # See doi:10.1016/j.atmosenv.2008.12.017
  84. #
  85. # The data are pollution source profile estimates for five modeled
  86. # pollution sources (e.g., cars, wood-burning, etc) that emit 7-9 chemical
  87. # species. The radar charts are experimented with here to see if we can
  88. # nicely visualize how the modeled source profiles change across four
  89. # scenarios:
  90. # 1) No gas-phase species present, just seven particulate counts on
  91. # Sulfate
  92. # Nitrate
  93. # Elemental Carbon (EC)
  94. # Organic Carbon fraction 1 (OC)
  95. # Organic Carbon fraction 2 (OC2)
  96. # Organic Carbon fraction 3 (OC3)
  97. # Pyrolized Organic Carbon (OP)
  98. # 2)Inclusion of gas-phase specie carbon monoxide (CO)
  99. # 3)Inclusion of gas-phase specie ozone (O3).
  100. # 4)Inclusion of both gas-phase species is present...
  101. data = [
  102. ['Sulfate', 'Nitrate', 'EC', 'OC1', 'OC2', 'OC3', 'OP', 'CO', 'O3'],
  103. ('Basecase', [
  104. [0.88, 0.01, 0.03, 0.03, 0.00, 0.06, 0.01, 0.00, 0.00],
  105. [0.07, 0.95, 0.04, 0.05, 0.00, 0.02, 0.01, 0.00, 0.00],
  106. [0.01, 0.02, 0.85, 0.19, 0.05, 0.10, 0.00, 0.00, 0.00],
  107. [0.02, 0.01, 0.07, 0.01, 0.21, 0.12, 0.98, 0.00, 0.00],
  108. [0.01, 0.01, 0.02, 0.71, 0.74, 0.70, 0.00, 0.00, 0.00]]),
  109. ('With CO', [
  110. [0.88, 0.02, 0.02, 0.02, 0.00, 0.05, 0.00, 0.05, 0.00],
  111. [0.08, 0.94, 0.04, 0.02, 0.00, 0.01, 0.12, 0.04, 0.00],
  112. [0.01, 0.01, 0.79, 0.10, 0.00, 0.05, 0.00, 0.31, 0.00],
  113. [0.00, 0.02, 0.03, 0.38, 0.31, 0.31, 0.00, 0.59, 0.00],
  114. [0.02, 0.02, 0.11, 0.47, 0.69, 0.58, 0.88, 0.00, 0.00]]),
  115. ('With O3', [
  116. [0.89, 0.01, 0.07, 0.00, 0.00, 0.05, 0.00, 0.00, 0.03],
  117. [0.07, 0.95, 0.05, 0.04, 0.00, 0.02, 0.12, 0.00, 0.00],
  118. [0.01, 0.02, 0.86, 0.27, 0.16, 0.19, 0.00, 0.00, 0.00],
  119. [0.01, 0.03, 0.00, 0.32, 0.29, 0.27, 0.00, 0.00, 0.95],
  120. [0.02, 0.00, 0.03, 0.37, 0.56, 0.47, 0.87, 0.00, 0.00]]),
  121. ('CO & O3', [
  122. [0.87, 0.01, 0.08, 0.00, 0.00, 0.04, 0.00, 0.00, 0.01],
  123. [0.09, 0.95, 0.02, 0.03, 0.00, 0.01, 0.13, 0.06, 0.00],
  124. [0.01, 0.02, 0.71, 0.24, 0.13, 0.16, 0.00, 0.50, 0.00],
  125. [0.01, 0.03, 0.00, 0.28, 0.24, 0.23, 0.00, 0.44, 0.88],
  126. [0.02, 0.00, 0.18, 0.45, 0.64, 0.55, 0.86, 0.00, 0.16]])
  127. ]
  128. return data
  129. if __name__ == '__main__':
  130. N = 9
  131. theta = radar_factory(N, frame='polygon')
  132. data = example_data()
  133. spoke_labels = data.pop(0)
  134. fig, axes = plt.subplots(figsize=(9, 9), nrows=2, ncols=2,
  135. subplot_kw=dict(projection='radar'))
  136. fig.subplots_adjust(wspace=0.25, hspace=0.20, top=0.85, bottom=0.05)
  137. colors = ['b', 'r', 'g', 'm', 'y']
  138. # Plot the four cases from the example data on separate axes
  139. for ax, (title, case_data) in zip(axes.flatten(), data):
  140. ax.set_rgrids([0.2, 0.4, 0.6, 0.8])
  141. ax.set_title(title, weight='bold', size='medium', position=(0.5, 1.1),
  142. horizontalalignment='center', verticalalignment='center')
  143. for d, color in zip(case_data, colors):
  144. ax.plot(theta, d, color=color)
  145. ax.fill(theta, d, facecolor=color, alpha=0.25)
  146. ax.set_varlabels(spoke_labels)
  147. # add legend relative to top-left plot
  148. ax = axes[0, 0]
  149. labels = ('Factor 1', 'Factor 2', 'Factor 3', 'Factor 4', 'Factor 5')
  150. legend = ax.legend(labels, loc=(0.9, .95),
  151. labelspacing=0.1, fontsize='small')
  152. fig.text(0.5, 0.965, '5-Factor Solution Profiles Across Four Scenarios',
  153. horizontalalignment='center', color='black', weight='bold',
  154. size='large')
  155. plt.show()

雷达图示例

参考

此示例中显示了以下函数,方法,类和模块的使用:

  1. import matplotlib
  2. matplotlib.path
  3. matplotlib.path.Path
  4. matplotlib.spines
  5. matplotlib.spines.Spine
  6. matplotlib.projections
  7. matplotlib.projections.polar
  8. matplotlib.projections.polar.PolarAxes
  9. matplotlib.projections.register_projection

下载这个示例