创建百分位水平条形图

Beginner

This tutorial is from open-source community. Access the source code

简介

在本实验中,我们将学习如何使用 Python 的 Matplotlib 库创建水平条形图。我们将以体育老师想要向家长展示他们的孩子在一些体能测试中的表现与其他孩子相比如何为例。为了演示目的,我们将为小约翰尼·多伊编造一些数据来提取绘图代码。

虚拟机提示

虚拟机启动完成后,点击左上角切换到“笔记本”标签以访问 Jupyter Notebook 进行练习。

有时,你可能需要等待几秒钟让 Jupyter Notebook 完成加载。由于 Jupyter Notebook 的限制,操作验证无法自动化。

如果你在学习过程中遇到问题,请随时向 Labby 提问。课程结束后提供反馈,我们将立即为你解决问题。

导入库

我们首先导入必要的库。我们将使用 numpy 创建数据,并使用 matplotlib.pyplot 绘制图表。

import matplotlib.pyplot as plt
import numpy as np

定义数据

我们使用具名元组来定义数据。我们定义了一个 Student 元组,包含学生的姓名、年级和性别。我们还定义了一个 Score 元组,包含分数值、单位和百分位数。

from collections import namedtuple

Student = namedtuple('Student', ['name', 'grade', 'gender'])
Score = namedtuple('Score', ['value', 'unit', 'percentile'])

定义辅助函数

我们定义两个辅助函数。第一个函数 to_ordinal,将整数转换为序数词字符串(例如,2 -> '2nd')。第二个函数 format_score,为右 y 轴创建分数标签,格式为测试名称后跟测量单位(如果有),分两行显示。

def to_ordinal(num):
    suffixes = {str(i): v
                for i, v in enumerate(['th', 'st', 'nd', 'rd', 'th',
                                       'th', 'th', 'th', 'th', 'th'])}
    v = str(num)
    if v in {'11', '12', '13'}:
        return v + 'th'
    return v + suffixes[v[-1]]

def format_score(score):
    return f'{score.value}\n{score.unit}' if score.unit else str(score.value)

定义绘图函数

我们定义一个名为 plot_student_results 的函数,它接受一个 Student 元组、一个按测试分类的分数字典以及同组人数。这个函数会创建一个水平条形图,展示每个测试相对于学生所在年级和性别的同组学生的百分位排名。

def plot_student_results(student, scores_by_test, cohort_size):
    fig, ax1 = plt.subplots(figsize=(9, 7), layout='constrained')
    fig.canvas.manager.set_window_title('Fitness Chart')

    ax1.set_title(student.name)
    ax1.set_xlabel(
        'Percentile Ranking Across {grade} Grade {gender}s\n'
        'Cohort Size: {cohort_size}'.format(
            grade=to_ordinal(student.grade),
            gender=student.gender.title(),
            cohort_size=cohort_size))

    test_names = list(scores_by_test.keys())
    percentiles = [score.percentile for score in scores_by_test.values()]

    rects = ax1.barh(test_names, percentiles, align='center', height=0.5)

    large_percentiles = [to_ordinal(p) if p > 40 else '' for p in percentiles]
    small_percentiles = [to_ordinal(p) if p <= 40 else '' for p in percentiles]
    ax1.bar_label(rects, small_percentiles,
                  padding=5, color='black', fontweight='bold')
    ax1.bar_label(rects, large_percentiles,
                  padding=-32, color='white', fontweight='bold')

    ax1.set_xlim([0, 100])
    ax1.set_xticks([0, 10, 20, 30, 40, 50, 60, 70, 80, 90, 100])
    ax1.xaxis.grid(True, linestyle='--', which='major',
                   color='grey', alpha=.25)
    ax1.axvline(50, color='grey', alpha=0.25)

    ax2 = ax1.twinx()
    ax2.set_ylim(ax1.get_ylim())
    ax2.set_yticks(
        np.arange(len(scores_by_test)),
        labels=[format_score(score) for score in scores_by_test.values()])

    ax2.set_ylabel('Test Scores')

定义绘图数据

我们使用之前定义的具名元组来定义绘图数据。我们为约翰尼·多伊创建一个 Student 元组,并为每个测试创建一个 Score 元组的字典。

student = Student(name='Johnny Doe', grade=2, gender='Boy')
scores_by_test = {
    'Pacer Test': Score(7, 'laps', percentile=37),
    'Flexed Arm\n Hang': Score(48,'sec', percentile=95),
    'Mile Run': Score('12:52','min:sec', percentile=73),
    'Agility': Score(17,'sec', percentile=60),
    'Push Ups': Score(14, '', percentile=16),
}

绘制数据

我们以学生数据、按测试的分数以及同组人数作为参数调用 plot_student_results 函数,然后调用 plt.show() 来显示图表。

plot_student_results(student, scores_by_test, cohort_size=62)
plt.show()

总结

在这个实验中,我们学习了如何使用 Python 的 Matplotlib 库创建水平条形图。我们以体育老师想要向家长展示他们的孩子在一些体能测试中的表现相对于其他孩子的情况为例。我们使用具名元组定义了数据,并定义了辅助函数将整数转换为序数词字符串,以及为右 y 轴创建分数标签。我们定义了一个绘图函数,该函数创建一个水平条形图,展示每个测试相对于学生所在年级和性别的同组学生的百分位排名。然后,我们使用我们的数据调用绘图函数并显示了图表。