assets/2026-07-01-医数智联系列开篇-医院数字化工作者的工具箱_2026-07-01_18-28-11.jpg

[!ABSTRACT] 核心摘要

  • 本期主题:matplotlib 入门——用代码自动出图,告别手动画图
  • 核心结论:matplotlib = Python 版的「手动画图」,但能 100% 自动化、批量化、可重复
  • 4 类核心图表:柱状图(对比)、折线图(趋势)、饼图(占比)、热力图(矩阵)
  • 3 大医院场景:月度错误率对比、年度趋势、DRG 入组分布、科室-时间热力图
  • 典型效果:把「30 分钟手动画图」变成「1 秒自动出图」
  • 阅读时长:约 10 分钟

一、场景引入:手动画图的「时间黑洞」

[!example] 一个真实的周三上午
院长:「把今年各科室的错误率做个对比图。」

质管办小李打开 Excel:

  1. 复制数据 → 新 sheet → 5 分钟
  2. 选数据 → 插入柱状图 → 5 分钟
  3. 改标题、坐标轴、颜色 → 10 分钟(反复调)
  4. 加数据标签 → 5 分钟
  5. 改字号让院长看清楚 → 5 分钟

半小时过去了,终于出了图。

院长:「再加个同比呢?再画个趋势图?」

小李:「……下午给您。」

又要 1 小时

这是手动画图的典型困境——Excel 画图看起来简单,但每个细节都手动调

场景 Excel 画图 Python 画图
单张图 OK OK
同类图 10 张 每张手动 1 次代码,10 张自动
改数据重新画 重新选数据,所有格式重设 重跑脚本,自动更新
自定义样式 有限 无限(改任何细节)
嵌入 PDF 报告 手动截图 自动嵌入

[!quote] 狼叔的判断
“画图是质管人的’门面’,matplotlib 是’自动门面机’。” 这一期,带你掌握 4 类核心图表的代码模板,把 30 分钟变 1 秒。


二、工具原理:matplotlib 是什么?

2.1 matplotlib 是什么?

[!info] matplotlib 简介
matplotlib 是 Python 的绘图库,创始于 2003 年,作者 John D. Hunter。

江湖地位:

  • Python 绘图 #1 库(90% 场景)
  • pandas / seaborn 的底层依赖(底层就是 matplotlib)

2.2 4 类核心图表

1
2
3
4
1. 柱状图(bar)   → 对比(科室 A vs B vs C)
2. 折线图(plot) → 趋势(1 月到 12 月)
3. 饼图(pie) → 占比(各错误类型占比)
4. 热力图(heatmap) → 矩阵(科室 × 月份)

2.3 matplotlib vs Excel 对照

概念 Excel matplotlib
柱状图 插入 → 柱状图 plt.bar()
折线图 插入 → 折线图 plt.plot()
饼图 插入 → 饼图 plt.pie()
标题 「图表标题」输入框 plt.title()
X 轴 自动 plt.xlabel()
Y 轴 自动 plt.ylabel()
保存 「另存为图片」 plt.savefig()

三、医院实战:4 类核心图表代码模板

3.1 准备数据

先用 pandas 把数据准备好(承第 15 期)

1
2
3
4
5
6
7
8
9
10
11
12
import pandas as pd
import matplotlib.pyplot as plt

# 设置中文字体(关键!不然中文都是方块)
plt.rcParams['font.sans-serif'] = ['SimHei', 'Microsoft YaHei']
plt.rcParams['axes.unicode_minus'] = False # 负号正常显示

# 示例数据:各科室主诊错误率
df = pd.DataFrame({
'科室': ['骨科', '神经科', '消化科', '心内科', '呼吸科', '内分泌科'],
'错误率': [10.0, 7.69, 5.45, 4.32, 3.21, 2.89]
})

3.2 图表 ①:柱状图(对比)

[!example] 实战 ①:柱状图——科室错误率对比

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
fig, ax = plt.subplots(figsize=(10, 6))

# 画柱状图
bars = ax.bar(df['科室'], df['错误率'], color='steelblue', edgecolor='black')

# 加数据标签
for bar in bars:
height = bar.get_height()
ax.text(
bar.get_x() + bar.get_width() / 2,
height + 0.2,
f'{height:.1f}%',
ha='center', va='bottom',
fontsize=11
)

# 标题和坐标轴
ax.set_title('各科室主诊错误率对比(2026 年 6 月)', fontsize=14, fontweight='bold')
ax.set_xlabel('科室', fontsize=12)
ax.set_ylabel('错误率(%)', fontsize=12)
ax.set_ylim(0, max(df['错误率']) * 1.2)
ax.grid(axis='y', linestyle='--', alpha=0.7)

plt.tight_layout()
plt.savefig('科室错误率对比.png', dpi=300) # 300 DPI 高清
plt.show()

3.3 图表 ②:折线图(趋势)

[!example] 实战 ②:折线图——月度错误率趋势

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
# 示例数据:12 个月错误率
月份 = ['1月', '2月', '3月', '4月', '5月', '6月', '7月', '8月', '9月', '10月', '11月', '12月']
本月错误率 = [3.2, 2.8, 3.5, 3.1, 2.9, 2.7, 3.0, 2.6, 2.4, 2.3, 2.1, 2.0]
去年同期错误率 = [4.5, 4.2, 4.0, 3.8, 3.7, 3.5, 3.4, 3.2, 3.0, 2.9, 2.7, 2.5]

fig, ax = plt.subplots(figsize=(12, 6))

# 画两条线
ax.plot(月份, 本月错误率, marker='o', linewidth=2, label='2026 年', color='steelblue')
ax.plot(月份, 去年同期错误率, marker='s', linewidth=2, label='2025 年', color='coral', linestyle='--')

# 标注关键点
ax.annotate(
'最低点',
xy=('11月', 2.1),
xytext=('11月', 3.0),
arrowprops=dict(arrowstyle='->', color='red'),
fontsize=11, color='red'
)

ax.set_title('主诊错误率月度趋势(同比)', fontsize=14, fontweight='bold')
ax.set_xlabel('月份', fontsize=12)
ax.set_ylabel('错误率(%)', fontsize=12)
ax.legend(loc='upper right', fontsize=11)
ax.grid(True, linestyle='--', alpha=0.5)

plt.tight_layout()
plt.savefig('错误率趋势.png', dpi=300)
plt.show()

3.4 图表 ③:饼图(占比)

[!example] 实战 ③:饼图——错误类型占比

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
错误类型 = ['主诊未选主要诊断', '主诊ICD与性别不符', '手术ICD与操作不符', '其他']
错误数 = [45, 18, 8, 4]
颜色 = ['#ff6b6b', '#4ecdc4', '#45b7d1', '#ffd93d']
突出显示 = [0.1, 0, 0, 0] # 第一块突出

fig, ax = plt.subplots(figsize=(8, 8))

wedges, texts, autotexts = ax.pie(
错误数,
labels=错误类型,
autopct='%1.1f%%', # 显示百分比
colors=颜色,
explode=突出显示, # 突出某块
startangle=90, # 起始角度
shadow=True, # 阴影
textprops={'fontsize': 12}
)

# 美化百分比文字
for autotext in autotexts:
autotext.set_color('white')
autotext.set_fontweight('bold')

ax.set_title('主诊错误类型占比(2026 年 6 月)', fontsize=14, fontweight='bold')

plt.tight_layout()
plt.savefig('错误类型占比.png', dpi=300)
plt.show()

3.5 图表 ④:热力图(矩阵)

[!example] 实战 ④:热力图——科室 × 月份 错误率矩阵

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
import numpy as np
import seaborn as sns

# 矩阵数据:7 个科室 × 12 个月
科室列表 = ['骨科', '神经科', '消化科', '心内科', '呼吸科', '内分泌科', '肾内科']
月份列表 = [f'{i}月' for i in range(1, 13)]

# 模拟错误率矩阵(实际从 SQL 读)
np.random.seed(42)
错误率矩阵 = np.random.uniform(1, 10, size=(len(科室列表), len(月份列表)))

fig, ax = plt.subplots(figsize=(14, 6))

# 画热力图
sns.heatmap(
错误率矩阵,
annot=True, # 显示数值
fmt='.1f', # 1 位小数
cmap='YlOrRd', # 黄-橙-红 渐变
xticklabels=月份列表,
yticklabels=科室列表,
cbar_kws={'label': '错误率(%)'},
ax=ax
)

ax.set_title('各科室月度错误率热力图(2026 年)', fontsize=14, fontweight='bold')
ax.set_xlabel('月份', fontsize=12)
ax.set_ylabel('科室', fontsize=12)

plt.tight_layout()
plt.savefig('错误率热力图.png', dpi=300)
plt.show()

3.6 子图组合(一次性出 4 张图)

[!example] 实战 ⑤:4 子图组合报告

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
fig, axes = plt.subplots(2, 2, figsize=(14, 10))

# 子图 1:柱状图
axes[0, 0].bar(df['科室'], df['错误率'], color='steelblue')
axes[0, 0].set_title('科室错误率对比', fontweight='bold')
axes[0, 0].tick_params(axis='x', rotation=45)

# 子图 2:折线图
axes[0, 1].plot(月份, 本月错误率, marker='o', color='steelblue', label='2026')
axes[0, 1].plot(月份, 去年同期错误率, marker='s', color='coral', label='2025')
axes[0, 1].set_title('错误率趋势', fontweight='bold')
axes[0, 1].legend()

# 子图 3:饼图
axes[1, 0].pie(错误数, labels=错误类型, autopct='%1.1f%%', colors=颜色)
axes[1, 0].set_title('错误类型占比', fontweight='bold')

# 子图 4:热力图
sns.heatmap(错误率矩阵, cmap='YlOrRd', ax=axes[1, 1], cbar=False)
axes[1, 1].set_title('错误率热力图', fontweight='bold')

plt.suptitle('本月质控总览报告(2026 年 6 月)', fontsize=16, fontweight='bold', y=1.00)
plt.tight_layout()
plt.savefig('本月质控总览.png', dpi=300)
plt.show()

四、避坑指南:5 个让 matplotlib「出图失败」的常见错误

[!warning] 雷区 ①:中文显示为方块
症状:图表里的中文全是「□□□」。
原因:matplotlib 默认字体不支持中文。
解决:设置中文字体:

1
2
plt.rcParams['font.sans-serif'] = ['SimHei', 'Microsoft YaHei']
plt.rcParams['axes.unicode_minus'] = False

[!warning] 雷区 ②:保存图片空白
症状:plt.savefig('图.png') 后打开是空白。
原因:plt.show()savefig 之后调用,把内存清空。
解决:savefig,再 show:

1
2
plt.savefig('图.png', dpi=300)  # 先保存
plt.show() # 再显示

[!warning] 雷区 ③:图片模糊
症状:插入 PPT 看不清。
原因:默认 DPI = 100,放大模糊。
解决:DPI 改 300:plt.savefig('图.png', dpi=300)

[!warning] 雷区 ④:X 轴标签重叠
症状:科室名挤在一起看不清。
解决:旋转 45 度:

1
plt.xticks(rotation=45, ha='right')

[!warning] 雷区 ⑤:颜色太丑
症状:默认颜色像 Excel 2003。
解决:用专业配色:

1
2
3
4
# matplotlib 自带
plt.style.use('seaborn-v0_8-whitegrid')
# 或用十六进制自定义
ax.bar(..., color='#4ecdc4')

五、把 matplotlib 嵌入自动化流程

[!TIP] 完整工作流

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
# 1. 从 SQL 读数据
import pymysql
conn = pymysql.connect(host='...', user='...', password='...', db='his_db')
df = pd.read_sql('SELECT 科室, COUNT(*) ... FROM 首页主表 GROUP BY 科室', conn)

# 2. 用 pandas 处理
df['错误率'] = df['错误数'] / df['总数'] * 100

# 3. 用 matplotlib 出图
fig, ax = plt.subplots(figsize=(10, 6))
ax.bar(df['科室'], df['错误率'])
ax.set_title('本月错误率')
plt.savefig('本月错误率.png', dpi=300)

# 4. 自动发邮件
import yagmail
yagmail.SMTP('xxx@xxx.com').send(
to=['院长@xxx.com'],
subject='本月质控报告',
contents=['本月报告见附件', '本月错误率.png']
)

六、下期预告

[!quote] 第 17 期
Python + 首页数据自动校验 —— 实战案例

这一期,带你写一个真实的首页校验脚本,自动检查主诊、ICD、手术编码,生成错误清单 Excel。


[!TIP] 互动
如果你觉得这篇「医数智联·第 16 期」对你有用,请点赞、在看、转发给科里的同事。

我是白衣狼,一个用数字化工具把医院管理做「轻」的实战派。咱们下期见。