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

医数智联·第 15 期:pandas 入门 —— 首页数据的「Excel 终结者」,DataFrame 10 大操作

[!ABSTRACT] 核心摘要

  • 本期主题:pandas 入门——把首页数据从「Excel 重活」变成「pandas 5 行代码」
  • 核心结论:pandas = Python 版的 Excel,但比 Excel 强大 10 倍
  • 核心数据结构:DataFrame(类似 Excel 工作表)+ Series(类似 Excel 一列)
  • 10 大必会操作:读 / 写 / 筛选 / 排序 / 分组 / 聚合 / 合并 / 透视 / 缺失值 / 日期
  • 典型效果:把 2 小时的 Excel 工作压成 5 行 Python 代码
  • 阅读时长:约 10 分钟

一、场景引入:Excel 的「复杂公式噩梦」

[!example] 一个真实的周三下午
质管办小李要算「上月每个科室的主诊错误率」,他打开 Excel:

  1. 筛选:筛选 → 骨科 → 复制 → 新 sheet
  2. 计数:COUNTIF(B:B, "骨科") 算出骨科总数
  3. 错误数:COUNTIFS(B:B, "骨科", C:C, 1) 算出骨科错误数
  4. 错误率:=错误数/总数,4 个科室,公式拉 4 次
  5. 再加新科室:5 个、6 个……公式改 8 次,每次都重新拉
  6. 数据更新:新增 100 行 → 所有公式重新覆盖

1 小时后,他算完了,但主任又问「按职称分组呢?按医生呢?」

他想哭。

这是 Excel 复杂分析的典型困境——逻辑一变,公式全乱

[!quote] 狼叔的判断
“Excel 适合’最后一步’,pandas 适合’前面 90%’。” 这一期,带你掌握 pandas 的 10 大必会操作,把 2 小时压成 5 行代码。


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

2.1 pandas 是什么?

[!info] pandas 简介
pandas 是 Python 的数据分析库,Excel 的「超进化版」

江湖地位:

  • Python 数据分析 #1 库(90% 数据分析师的首选)
  • 医院数据场景 覆盖率最高(首页处理 / DRG 分析 / 报表自动化)

2.2 核心数据结构

1
2
3
4
5
6
7
8
9
10
11
┌─────────────────────────────────────────┐
│ DataFrame(类似 Excel 工作表) │
│ ┌──────┬──────┬──────┬──────┐ │
│ │ 病案号│ 科室 │ 出院日│ 错误 │ ← 列名 │
│ ├──────┼──────┼──────┼──────┤ │
│ │ BA001│ 骨科 │ 06-15│ 1 │ ← Series │
│ │ BA002│ 骨科 │ 06-16│ 0 │ ← Series │
│ └──────┴──────┴──────┴──────┘ │
└─────────────────────────────────────────┘

Series(类似 Excel 一列,带索引)

2.3 pandas vs Excel 对照表

概念 Excel pandas
工作簿 .xlsx 文件 不存在(内存中)
工作表 sheet DataFrame
一列 列(如 A 列) Series
一行 一条记录 DataFrame.loc[i]
筛选 筛选 df[df['列'] == 值]
排序 排序 df.sort_values()
分类汇总 数据透视表 df.pivot_table()
VLOOKUP VLOOKUP() df.merge()
公式 =A1+B1 df.eval()

三、医院实战:10 大必会操作

3.1 操作 1:读 Excel(读)

1
2
3
4
5
6
7
8
9
10
11
12
13
import pandas as pd

# 读 Excel 文件
df = pd.read_excel('本月首页.xlsx')

# 看前 5 行
print(df.head())

# 看基本信息(行数、列数、每列类型)
print(df.info())

# 看统计摘要(均值、标准差、最大最小)
print(df.describe())

[!tip] 读 CSV 用 pd.read_csv(),读 SQL 用 pd.read_sql(),几乎所有数据源都能读

3.2 操作 2:写 Excel(写)

1
2
3
4
5
6
7
# 写回 Excel
df.to_excel('处理结果.xlsx', index=False) # index=False 不写行号

# 写多个 sheet
with pd.ExcelWriter('输出.xlsx') as writer:
df_骨科.to_excel(writer, sheet_name='骨科', index=False)
df_神经科.to_excel(writer, sheet_name='神经科', index=False)

3.3 操作 3:筛选(按条件选行)

1
2
3
4
5
6
7
8
9
10
11
# 单条件:选骨科
df_骨科 = df[df['科室'] == '骨科']

# 多条件:骨科 + 主诊错误
df_骨科错误 = df[(df['科室'] == '骨科') & (df['主诊错误标记'] == 1)]

# 多条件:骨科 OR 神经科
df_骨科或神经科 = df[df['科室'].isin(['骨科', '神经科'])]

# 范围筛选:出院日期在 6 月
df_6月 = df[(df['出院日期'] >= '2026-06-01') & (df['出院日期'] < '2026-07-01')]

3.4 操作 4:排序(按值排)

1
2
3
4
5
# 按错误分值降序
df_排序 = df.sort_values('错误分值', ascending=False)

# 多列排序:先按科室,再按错误分值
df_多列排序 = df.sort_values(['科室', '错误分值'], ascending=[True, False])

3.5 操作 5:分组(groupby)+ 聚合

1
2
3
4
5
6
7
8
9
10
11
12
13
# 按科室分组,统计每组的错误数
分组统计 = df.groupby('科室').agg(
总病案数=('病案号', 'count'),
错误数=('主诊错误标记', 'sum'),
平均住院日=('住院天数', 'mean')
).reset_index()

# 加错误率列
分组统计['错误率_百分比'] = (
分组统计['错误数'] / 分组统计['总病案数'] * 100
).round(2)

print(分组统计)

典型输出:

1
2
3
4
    科室  总病案数  错误数   平均住院日  错误率_百分比
0 骨科 280 28 12.3 10.00
1 神经科 195 15 9.5 7.69
2 消化科 220 12 10.1 5.45

3.6 操作 6:合并(merge,代替 VLOOKUP)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
# 主页表 + 诊断表(按病案号合并)
df_合并 = pd.merge(
df_主表,
df_诊断表,
on='病案号', # 按哪个字段关联
how='left' # 左连接,保留主表所有记录
)

# 多字段关联
df_合并 = pd.merge(
df_主表,
df_诊断表,
on=['病案号', '诊断序号'], # 按多个字段关联
how='inner' # 内连接,只保留都有的
)
how 参数 含义
inner 内连接,只保留匹配的
left 左连接,保留左表所有
right 右连接,保留右表所有
outer 全连接,保留所有

3.7 操作 7:数据透视表(pivot_table)

1
2
3
4
5
6
7
8
9
# Excel 数据透视表的 pandas 版本
透视 = df.pivot_table(
index='科室', # 行
columns='职称', # 列
values='错误分值', # 值
aggfunc='sum', # 聚合方式
fill_value=0 # 缺失填 0
)
print(透视)

典型输出:

1
2
3
4
5
职称    主治医师  副主任  主任医师
科室
骨科 350 120 50
神经科 180 100 30
消化科 150 60 20

3.8 操作 8:缺失值处理

1
2
3
4
5
6
7
8
9
10
11
12
13
14
# 检查缺失值
print(df.isnull().sum())

# 缺失值数量
总缺失 = df.isnull().sum().sum()

# 填充缺失值
df['主诊ICD'] = df['主诊ICD'].fillna('未填')

# 删除缺失行
df_清洗 = df.dropna()

# 删除某列缺失的行
df_清洗 = df.dropna(subset=['主诊ICD'])

3.9 操作 9:日期处理

1
2
3
4
5
6
7
8
9
10
11
# 转日期类型
df['出院日期'] = pd.to_datetime(df['出院日期'])

# 提取日期部分
df['年'] = df['出院日期'].dt.year
df['月'] = df['出院日期'].dt.month
df['日'] = df['出院日期'].dt.day
df['星期'] = df['出院日期'].dt.day_name() # 星期几

# 计算日期差
df['住院天数'] = (df['出院日期'] - df['入院日期']).dt.days

3.10 操作 10:apply 函数(自定义处理)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
# 给每行打标签:错误分值 >= 80 的标「重大」
def 错误等级(分值):
if 分值 >= 80:
return '重大错误'
elif 分值 >= 50:
return '严重错误'
elif 分值 >= 20:
return '一般错误'
else:
return '轻微错误'

df['错误等级'] = df['错误分值'].apply(错误等级)

# 用 lambda 简写
df['错误等级'] = df['错误分值'].apply(
lambda x: '重大错误' if x >= 80
else '严重错误' if x >= 50
else '一般错误' if x >= 20
else '轻微错误'
)

四、避坑指南:5 个让新手”踩雷”的常见错误

[!warning] 雷区 ①:列名带空格
症状:df['主诊 ICD'] 报错,或选不到列。
原因:pandas 列名严格匹配,空格也算字符
解决:读入后立即清洗列名:

1
df.columns = df.columns.str.strip().str.replace(' ', '')

[!warning] 雷区 ②:数据类型不对
症状:df['主诊错误标记'].sum() 返回奇怪结果。
原因:错误标记是 object 类型(字符串),不是数字。
解决:先转换类型:

1
df['主诊错误标记'] = df['主诊错误标记'].astype(int)

[!warning] 雷区 ③:groupby 后忘了 reset_index
症状:df_grouped['科室'] 报错 KeyError。
原因:groupby 后索引变成「分组字段」,普通列名失效。
解决:.reset_index():

1
df_grouped = df.groupby('科室').sum().reset_index()

[!warning] 雷区 ④:合并时主键重复
症状:merge 后行数暴增。
原因:右表主键有重复,1 对多 → 行数变成 N 倍。
解决:先去重:

1
df_诊断去重 = df_诊断.drop_duplicates(subset=['病案号'])

[!warning] 雷区 ⑤:链式赋值 SettingWithCopyWarning
症状:df[df['科室']=='骨科']['主诊'] = 'XXX' 报警告。
原因:pandas 不知道你想改原数据还是副本。
解决:.loc:

1
df.loc[df['科室']=='骨科', '主诊'] = 'XXX'

五、下期预告

[!quote] 第 16 期
matplotlib 入门:质控图表自动生成 —— 4 类核心图表

这一期,带你用 Python 自动画图,把首页数据可视化:柱状图、折线图、饼图、热力图,告别手动画图。


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

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