Programming Practical Combat (3) - Python Drawing Polar Coordinate Radar Chart

Programming Practical Combat (3) - Python Drawing Polar Coordinate Radar Chart

Overview

Python's matplotlib drawing library is very powerful and can draw many kinds of pictures, and the radar charts we encounter in our daily lives are no exception.

A radar chart, also known as a network chart, spider chart, star chart, etc., is an irregular polygon. Radar charts can vividly display multi-dimensional indicators of the same thing, and have many application scenarios. For example, in this blog, they are used to show the differences in different abilities of players.

The radar plot drawing in the matplotlib library is based on polar coordinates , so all data and labels must be calculated based on angles.

This blog will explain in detail the ideas, processes and detailed analysis of each function method in the process of drawing radar charts. If there are any mistakes, please correct me~

Plotting code and parsing

Draw a multi-agent radar chart

The so-called multi-agent radar chart displays an attribute map composed of multiple polygons in one picture to form an intuitive comparison.

preprocessing

Download the corresponding package, then solve the problem of Chinese and symbol display, set the default font, and modify the drawing style;

Load the data set (the source of my data set here: a green football app), extract the keys and values ​​in the dictionary and save them in labels and scores respectively.

import numpy as np
import matplotlib.pyplot as plt

plt.rcParams['font.sans-serif'] = ['KaiTi']  # 指定默认字体
plt.rcParams['axes.unicode_minus'] = False  # 解决保存图像是负号'-'显示为方块的问题
plt.style.use('ggplot')  # 设置ggplot主题样式

# 原始数据集并获取数据集长度
results = [{
    
    "速度": 68, "射门": 91, "传球": 83, "盘带": 83, "防守": 47, "力量": 82},
           {
    
    "速度": 88, "射门": 87, "传球": 82, "盘带": 86, "防守": 42, "力量": 69},
           {
    
    "速度": 58, "射门": 55, "传球": 70, "盘带": 67, "防守": 86, "力量": 77}]
data_length = len(results[0])

 # 将极坐标根据数据长度进行等分,形成角度列表
angles = np.linspace(0, 2 * np.pi, data_length, endpoint=False)

# 分离属性字段和数据
labels = [key for key in results[0].keys()]
score = [[v for v in result.values()] for result in results]

If you are interested, you can refer to this blog: Detailed explanation of plt.style.use setting background style parameters I chose a ggplot theme that I think looks better;

closed radar chart

Since the picture we drew is a closed polygon, and each item of the score, labels and angles data we extracted is not connected end to end, we need to copy the first item of each list and then add to the end of the list.

angles = np.concatenate((angles, [angles[0]]))
labels = np.concatenate((labels, [labels[0]]))
score_Harry = np.concatenate((score[0], [score[0][0]]))
score_Son = np.concatenate((score[1], [score[1][0]]))
score_Tobi = np.concatenate((score[2], [score[2][0]]))

Here, because I have the data of three players, there are three pieces of score data.

Mention the np.concatenate function. This is a function in the numpy library that merges multiple arrays. The parameter format is: concatenate((arr1, arr2, arr3), axis=0). The first bracket is for merging. You can fill in any number of arrays. The second axis parameter is related to the merging method. We won’t go into details here if we don’t need it.

draw image

Drawing an image is basically the same as drawing a bar chart. There are a few points to note:

  • In the plot function, when drawing a bar chart, the first and second parameters are filled in according to "abscissa - ordinate". Since our radar chart is based on polar coordinates, matplotlib also intelligently provides "angle - radius" How to fill in the polar coordinate parameters;
  • Regarding the color parameters, you can refer to this: plt.plot color encyclopedia . The color in the plot is just the border color. The color in the fill function is the filling color , which can be freely combined to pursue beauty;
  • The alpha parameter in the fill function refers to the transparency of the coverage area. The smaller it is, the more transparent it is. The interval is 0-1;
  • The loc in the plt.legend legend function describes the position of the legend. For details, please refer to: Detailed explanation of legend position adjustment.

code show as below:

# 设置图形的大小
fig = plt.figure(figsize=(8, 6), dpi=100)

# 新建一个子图
ax = plt.subplot(111, polar=True)

# 绘制雷达图并填充颜色
ax.plot(angles, score_Harry, color='orange')
ax.fill(angles, score_Harry, 'y', alpha=0.4)
ax.plot(angles, score_Son, color='b')
ax.fill(angles, score_Son, 'cyan', alpha=0.4)
ax.plot(angles, score_Tobi, color='r')
ax.fill(angles, score_Tobi, 'salmon', alpha=0.4)

# 设置雷达图中每一项的标签显示
ax.set_thetagrids(angles * 180 / np.pi, labels, fontsize=15)

ax.set_theta_zero_location('E')  # 设置0度坐标轴起始位置,东西南北

ax.set_rlim(0, 100)  # 设置雷达图的坐标刻度范围

ax.set_rlabel_position(270)  # 设置雷达图的坐标值显示角度,相对于起始角度的偏移量
ax.set_title("热刺球员能力对比图")
plt.legend(["哈里·凯恩", "孙兴愍", "托比"], loc='lower left')

plt.show()

Complete code:

import numpy as np
import matplotlib.pyplot as plt

# 解决中文显示问题
plt.rcParams['font.sans-serif'] = ['KaiTi']  # 指定默认字体
plt.rcParams['axes.unicode_minus'] = False  # 解决保存图像是负号'-'显示为方块的问题
plt.style.use('ggplot')  # 设置ggplot样式

# 原始数据集并获取数据集长度
results = [{
    
    "速度": 68, "射门": 91, "传球": 83, "盘带": 83, "防守": 47, "力量": 82},
           {
    
    "速度": 88, "射门": 87, "传球": 82, "盘带": 86, "防守": 42, "力量": 69},
           {
    
    "速度": 58, "射门": 55, "传球": 70, "盘带": 67, "防守": 86, "力量": 77}]
data_length = len(results[0])

angles = np.linspace(0, 2 * np.pi, data_length, endpoint=False)  # 将极坐标根据数据长度进行等分

# 分离属性字段和数据
labels = [key for key in results[0].keys()]
score = [[v for v in result.values()] for result in results]

# 使雷达图数据封闭
angles = np.concatenate((angles, [angles[0]]))
labels = np.concatenate((labels, [labels[0]]))
score_Harry = np.concatenate((score[0], [score[0][0]]))
score_Son = np.concatenate((score[1], [score[1][0]]))
score_Tobi = np.concatenate((score[2], [score[2][0]]))

# 设置图形的大小
fig = plt.figure(figsize=(8, 6), dpi=100)

# 新建一个子图
ax = plt.subplot(111, polar=True)

# 绘制雷达图并填充颜色
ax.plot(angles, score_Harry, color='orange')
ax.fill(angles, score_Harry, 'y', alpha=0.4)
ax.plot(angles, score_Son, color='b')
ax.fill(angles, score_Son, 'cyan', alpha=0.4)
ax.plot(angles, score_Tobi, color='r')
ax.fill(angles, score_Tobi, 'salmon', alpha=0.4)

# 设置雷达图中每一项的标签显示
ax.set_thetagrids(angles * 180 / np.pi, labels, fontsize=15)

ax.set_theta_zero_location('E')  # 设置0度坐标轴起始位置,东西南北

ax.set_rlim(0, 100)  # 设置雷达图的坐标刻度范围

ax.set_rlabel_position(270)  # 设置雷达图的坐标值显示角度,相对于起始角度的偏移量
ax.set_title("热刺球员能力对比图")
plt.legend(["哈里·凯恩", "孙兴愍", "托比"], loc='lower left')

plt.show()

The final radar chart:

Insert image description here

Still pretty(?)

Draw multiple single-agent radar charts

The data processing part is the same as above, let's start directly with drawing.

Create a subgraph

First, after we use plt.figure to draw the basic canvas, we need to create three subfigures (because there are three players):

ax1 = plt.subplot(131, polar=True)
ax2 = plt.subplot(132, polar=True)
ax3 = plt.subplot(133, polar=True)

We regard the entire canvas as a matrix with m rows and n columns;

The first parameter is a three-digit number. The first digit indicates the row position of the subimage in the entire canvas. 1 indicates that the canvas is divided into 1 row. The second digit indicates the column. 3 indicates that the canvas is divided into 3 columns. The third one represents the index value, which refers to the specific position, arranged from top to bottom and from left to right. For example, in a 2*2 canvas, an index value of 3 means the first one in the second row;

Loop through and draw each subgraph

First, save the sub-pictures, data, labels, and colors used for drawing in lists to facilitate traversal;

Iterate over each subgraph:

  • First draw the angular coordinate axis and the frame line. '-.' means that the frame line style is composed of a small line segment plus a point. lw is the abbreviation of linewidth, indicating the thickness of the line;
  • First draw the contours along the radius direction, and then draw the angle axis;
  • Draw and fill with color, same as above;
  • Mark the data subscript, ha and va are the positions to adjust the horizontal and vertical directions
  • Finally, adjust parameters such as labels, coordinate value range, etc.
ax, data, name, color = [ax1, ax2, ax3], [score_Harry, score_Son, score_Tobi], ["哈里·凯恩", "孙兴愍", "托比"], ["orange", "cyan", "green"]

for i in range(3):
    # 绘制角度轴和框线
    for j in np.arange(0, 100+20, 20):
        ax[i].plot(angles, 7*[j], '-.', lw=0.5, color='black')  #  沿半径方向的等值线
    for j in range(5):
        ax[i].plot([angles[j], angles[j]], [0, 100], '-.', lw=0.5, color='black')  #  绘制角度轴

    # 绘制图像并填充颜色
    ax[i].plot(angles, data[i], color=color[i])
    ax[i].fill(angles, data[i], color=color[i], alpha=0.4)

    # 数据下标
    for a, b in zip(angles, data[i]):
        ax[i].text(a, b+5, '%.00f' % b, ha='center', va='center', fontsize=10, color='black')

    # 参数设置
    ax[i].set_thetagrids(angles*180/np.pi, labels)
    ax[i].set_theta_zero_location('N')
    ax[i].set_rlim(0, 100)
    ax[i].set_rlabel_position(0)
    ax[i].set_title(name[i])

The final result is obtained (personal aesthetics are limited, don’t spray if you don’t like it):

Insert image description here

Generally speaking, powerful matplotlib + flexible use of code + beautification = a good-looking radar chart~

Guess you like

Origin blog.csdn.net/qq_45882682/article/details/122796602
Recommended