ARTICLE DETAIL

资讯详情

深耕网站建设、视觉设计与SEO优化的一线实战洞察。

Python实现医院运营数据分析与报表自动化

Python实现医院运营数据分析与报表自动化 近年来“数智赋能、提质增效”已经成为公立医院高质量发展讨论中的高频词。各类学术年会、行业论坛都在围绕同一个问题展开当医疗业务数据越来越多管理要求越来越细医院如何真正把数据变成管理抓手而不是停留在报表层面本文不讨论政策解读而是从技术落地角度出发聚焦医院运营数据分析中最常见、也最实用的场景指标体系构建、数据清洗、绩效指标计算与分析报告自动生成。我会用一套可运行的 Python 代码带大家完成从原始数据到运营分析结果的全流程并给出数据治理、指标口径管理和安全合规方面的方法建议。无论你是医院信息科工程师、数据分析岗还是正在学习医疗数据分析的开发者这篇文章都能为你提供一条清晰可复用的实践路径。1. 背景与核心概念1.1 为什么要做“数智赋能、提质增效”公立医院的管理正在从“规模扩张型”转向“质量效益型”。过去医院管理者关注的是门诊量、住院量、床位规模这些绝对数量指标现在大家更关心的是费用结构是否合理、住院天数是否可控、资源配置效率是否达标、学科发展是否均衡。这些管理诉求落到技术层面核心就是“用数据说话”。但实际工作中“数据”和“数据之间”往往存在严重脱节HIS医院信息系统里存的是业务流程数据病案首页里是医疗质量数据财务系统里是经济核算数据人事系统里是人力配置数据。数据分散在各个业务系统里口径不统一、标准不一致、质量参差不齐想直接用于分析几乎不可能。所以“数智赋能”的第一步并不是上多高级的算法模型而是先把数据治理做好把指标口径定清楚把计算逻辑固化下来再通过自动化工具形成稳定的分析报告体系。这一步做完医院管理者才能随时看到“哪个科室药占比偏高”“哪些病组平均住院日超标”“次均费用增长的驱动因素是什么”进而做出有针对性的管理决策。1.2 技术层面要解决的核心问题如果抛开医院行业的业务细节从纯技术视角看“提质增效”对应的其实是三件事。第一是数据质量。原始数据能不能直接用于计算取决于有无缺失、重复、异常值以及字段格式是否统一。数据分析行业有句老话叫“Garbage In, Garbage Out”——数据质量不过关再复杂的分析模型也是白搭。第二是指标口径。同一个指标不同部门可能理解不同。比如“平均住院日”是只算出院患者还是包含在院患者是自然日还是实际占用床日数这些如果不定义清楚计算结果没有可比性管理决策也就无从谈起。第三是计算与呈现效率。传统的 Excel 手工汇总方式在数据量大、指标多、更新频繁的情况下既耗时又容易出错。更合理的方式是编写可复用的脚本实现数据自动清洗、指标自动计算、报告自动生成让人从重复劳动中解放出来。1.3 本文的定位与读者对象本文定位为一篇实战型教程不是医院管理理论文章。我会以医院运营管理中最常见的指标——平均住院日、药占比、次均费用、床位使用率、费用结构等为例演示如何用 Python 完成一次完整的分析流程。适合的读者包括医院信息中心、大数据中心的工程师需要搭建运营分析报表医疗数据分析岗位人员希望系统化掌握指标计算逻辑卫生管理、医院管理相关专业的学生想了解数据分析如何落地对医疗大数据感兴趣的后端或数据分析开发者。文中所有代码基于模拟数据不涉及真实患者隐私信息可以放心在本地运行测试。2. 环境准备与版本说明2.1 开发环境建议本文使用的技术栈以 Python 为核心涉及的主要库如下pandas用于数据处理和指标计算numpy用于数值计算openpyxl 或 xlsxwriter用于 Excel 报告生成matplotlib用于生成图表可选本文做简要演示。版本方面建议使用 Python 3.8 及以上版本pandas 使用 1.3 以上版本即可。不同版本间 API 差异不大即使你的环境版本和我不同代码也能正常运行。如果你使用的是 Anaconda 发行版这些库一般已经预装不需要额外安装。操作系统不限Windows、Linux、macOS 都可以运行本文示例代码。2.2 安装依赖在命令行中执行以下命令安装所需依赖pip install pandas numpy openpyxl matplotlib如果你使用的是国内镜像源可以指定镜像地址加快下载速度pip install pandas numpy openpyxl matplotlib -i https://pypi.tuna.tsinghua.edu.cn/simple安装完成后可以执行以下命令验证版本import pandas as pd import numpy as np print(pd.__version__) print(np.__version__)输出类似于2.0.3 1.24.32.3 项目结构为方便阅读和复用建议按照下面的目录结构组织代码hospital_kpi/ ├── data/ # 数据文件目录 │ ├── raw/ # 原始数据 │ └── processed/ # 清洗后数据 ├── scripts/ # 脚本目录 │ ├── data_generator.py # 模拟数据生成 │ ├── data_clean.py # 数据清洗 │ ├── kpi_calc.py # 指标计算 │ └── report.py # 报告生成 ├── output/ # 输出结果目录 └── main.py # 主流程入口实际项目中原始数据通常由 HIS、病案首页、财务系统等导出不需要自己生成模拟数据。但考虑到很多读者手边没有真实医院数据本文会先提供一个数据生成脚本方便大家完整跑通流程。3. 核心概念拆解从业务指标到数据实现3.1 指标口径与数据来源在写代码之前先把涉及的指标口径梳理清楚。以下面 5 个指标为例平均住院日计算方法出院患者占用总床日数 ÷ 出院患者人数。这个指标反映的是患者平均在医院住了多少天是衡量医疗效率的重要指标。注意这里使用的是“出院患者”不是“在院患者”口径不同结果会差很多。药占比计算方法药品费用 ÷ 医疗总费用 × 100%。药占比反映的是医院收入结构中药品费用的比例。药占比过高通常说明合理用药管理还有提升空间。不同级别、不同专科的医院药占比控制目标也不一样分析时需要结合科室实际情况判断。次均费用计算方法总医疗费用 ÷ 就诊人次。次均费用可以按门诊计算也可以按住院计算。本文示例中按住院患者计算即住院总费用除以出院人次。床位使用率计算方法实际占用总床日数 ÷ 实际开放总床日数 × 100%。床位使用率过高说明病床紧张、患者排队压力大过低则说明床位资源闲置。这个指标通常按月份、按科室统计更有参考价值。费用结构计算方法将医疗费用拆分为药品费、耗材费、检查检验费、医疗服务费等类别分别计算各类别占比。费用结构分析可以直观地看出一个科室的收入构成是否合理特别是医疗服务性收入占比是体现医务人员劳务价值的重要指标。这些指标在代码层面并不复杂都是“加总 除法”的组合运算。真正的难点在于原始数据里往往没有现成的字段直接叫“出院患者占用总床日数”而是需要根据多个字段组合计算。比如“入院日期”“出院日期”可以计算出住院天数但这个天数是否包含入院当天和出院当天业务上要有明确规定。3.2 数据质量问题的常见类型无论做哪个行业的数据分析数据清洗这一步都躲不掉。结合医院数据的特点重点要处理以下几类问题缺失值比如某条记录的出院日期为空无法计算住院天数或者费用字段为空无法计算次均费用。处理策略分情况处理。如果关键字段缺失导致核心指标无法计算可以选择删除该记录如果只是辅助字段缺失可以考虑填充如用中位数、均值填充或者标记为“未知”。重复记录同一个病历号在系统中被录入了两次或者同一笔费用记录重复上传。处理策略根据主键如住院号 费用类别判断重复保留唯一记录。异常值比如住院天数为负数住院费用为 0患者年龄为 200 岁等。处理策略通过业务规则过滤。住院天数小于 0 的记录直接剔除费用为 0 的记录需要结合业务流程判断是真实免费还是录入错误。口径不一致比如有的记录中费用单位是“元”有的是“万元”有的日期格式是2024-01-01有的是2024/01/01。处理策略统一转换为标准格式。日期统一用datetime类型金额统一转换为“元”。3.3 从传统手工汇总到自动化计算的升级传统的运营指标统计在很多医院里仍然是“信息科跑数 → 下发科室 → 人工核对 → Excel 汇总 → 手工汇报”的模式。这种模式的问题很明显数据口径靠人传人容易出现偏差计算过程不透明结果有争议时难以追溯报表周期长往往是月底才能看到上个月的数据重复工作量大每次统计都要重新做一遍。自动化计算的核心思路是“把口径固化到代码里”。写一个脚本输入是原始数据文件输出是标准化指标结果。以后每个月只需要把当月的原始数据放到指定目录运行一次脚本结果自动出来。谁有疑问看代码就能知道这个指标是怎么算的哪个月的数据有问题直接查日志和中间结果。这也是“提质增效”在技术上的真正含义不是增加工作量而是把重复劳动标准化、自动化让人去处理更复杂、更灵活的分析任务。4. 完整实战用 Python 完成医院运营指标分析这套示例代码围绕一个核心场景对某医院一个季度内的住院数据进行分析输出各科室关键运营指标并生成一份包含明细结果和汇总图表的数据分析报告。整体流程如下生成模拟数据模拟 HIS 导出数据数据清洗与标准化计算科室维度 KPI生成 Excel 报告与可视化图表输出关键结论。4.1 生成模拟数据首先创建一个data_generator.py文件用于生成模拟的住院数据。# 文件路径scripts/data_generator.py import random import pandas as pd import numpy as np from datetime import datetime, timedelta def generate_inpatient_data(num_records5000): 生成模拟住院患者数据 字段说明 - case_id: 住院号 - dept: 科室 - doctor: 主治医生 - admission_date: 入院日期 - discharge_date: 出院日期 - total_cost: 住院总费用元 - drug_cost: 药品费用元 - consumable_cost: 耗材费用元 - exam_cost: 检查检验费用元 - service_cost: 医疗服务费元 - bed_days: 住院天数 - outcome: 离院方式 np.random.seed(42) random.seed(42) departments [心血管内科, 呼吸内科, 消化内科, 骨科, 神经外科] doctors { 心血管内科: [张伟, 李娜, 王强], 呼吸内科: [赵敏, 刘洋, 陈晨], 消化内科: [孙丽, 周杰, 吴昊], 骨科: [郑华, 王芳, 林峰], 神经外科: [冯军, 蒋涛, 韩雪] } outcomes [治愈, 好转, 未愈, 死亡, 其他] # 不同科室的费用基线和住院天数基线不同 dept_params { 心血管内科: {base_cost: 12000, base_days: 8, drug_ratio: 0.35}, 呼吸内科: {base_cost: 9000, base_days: 7, drug_ratio: 0.40}, 消化内科: {base_cost: 8000, base_days: 6, drug_ratio: 0.38}, 骨科: {base_cost: 15000, base_days: 10, drug_ratio: 0.20}, 神经外科: {base_cost: 20000, base_days: 12, drug_ratio: 0.25} } records [] for i in range(1, num_records 1): dept np.random.choice(departments) doctor np.random.choice(doctors[dept]) params dept_params[dept] # 入院日期2024年1月1日到2024年3月31日之间 start datetime(2024, 1, 1) end datetime(2024, 3, 31) admission_date start timedelta(daysnp.random.randint(0, (end - start).days 1)) # 住院天数基于基线加随机波动 bed_days max(1, int(np.random.normal(params[base_days], 3))) # 让11月12月的数据带一些缺失模拟真实场景 discharge_date admission_date timedelta(daysbed_days) # 总费用基于科室基线和住院天数生成 total_cost float( params[base_cost] bed_days * np.random.uniform(500, 1200) np.random.uniform(-2000, 5000) ) total_cost max(1000, round(total_cost, 2)) drug_cost round(total_cost * params[drug_ratio] * np.random.uniform(0.8, 1.2), 2) consumable_cost round(total_cost * np.random.uniform(0.1, 0.25), 2) exam_cost round(total_cost * np.random.uniform(0.15, 0.30), 2) service_cost round( max(0, total_cost - drug_cost - consumable_cost - exam_cost), 2 ) # 人工构造一些异常数据 if i % 500 0: bed_days -3 # 异常住院天数为负数 if i % 700 0: total_cost None # 异常费用缺失 if i % 800 0: admission_date None # 异常日期缺失 record { case_id: fCASE{i:06d}, dept: dept, doctor: doctor, admission_date: admission_date, discharge_date: discharge_date, total_cost: total_cost, drug_cost: drug_cost, consumable_cost: consumable_cost, exam_cost: exam_cost, service_cost: service_cost, bed_days: bed_days, outcome: np.random.choice(outcomes, p[0.75, 0.15, 0.05, 0.02, 0.03]) } records.append(record) df pd.DataFrame(records) return df if __name__ __main__: df generate_inpatient_data(5000) df.to_csv(../data/raw/inpatient_2024_q1.csv, indexFalse, encodingutf-8-sig) print(f模拟数据已生成共 {len(df)} 条记录) print(df.head())运行后会在data/raw目录下生成inpatient_2024_q1.csv文件包含 5000 条模拟住院记录。这里故意插入了一些异常数据bed_days为负数、total_cost缺失、admission_date缺失。目的是让后续的数据清洗步骤有内容可做。4.2 数据清洗与标准化接下来编写数据清洗脚本主要完成四件事缺省值处理、日期标准化、异常值过滤、费用字段处理。# 文件路径scripts/data_clean.py import pandas as pd import numpy as np def load_raw_data(file_path): 加载原始 CSV 数据 df pd.read_csv(file_path, encodingutf-8-sig) print(f原始数据加载完成共 {len(df)} 条记录) return df def clean_data(df): 数据清洗主函数 清洗流程 1. 日期字段标准化 2. 缺失关键字段的记录删除 3. 异常值过滤 4. 费用字段补全校验 # 记录清洗前的数据量 total_records len(df) # 1. 日期字段标准化 for col in [admission_date, discharge_date]: df[col] pd.to_datetime(df[col], errorscoerce) # 2. 删除入院日期缺失、出院日期缺失、住院天数缺失的记录 before_drop len(df) df df.dropna(subset[admission_date, discharge_date]) after_drop_date len(df) print(f日期字段缺失过滤{before_drop} - {after_drop_date}删除 {before_drop - after_drop_date} 条) # 3. 异常住院天数过滤 before_drop len(df) df df[df[bed_days] 0] after_drop_bed_days len(df) print(f异常住院天数过滤{before_drop} - {after_drop_bed_days}删除 {before_drop - after_drop_bed_days} 条) # 4. 费用字段缺失处理 # 如果总费用缺失尝试用分项费用求和补全 cost_cols [drug_cost, consumable_cost, exam_cost, service_cost] # 先计算分项费用和 df[subtotal_cost] df[cost_cols].sum(axis1, skipnaTrue) # 总费用缺失时用分项费用和填充 df[total_cost] df[total_cost].fillna(df[subtotal_cost]) # 如果补全后总费用仍然缺失或小于0删除该记录 before_drop len(df) df df[df[total_cost].notna() (df[total_cost] 0)] after_drop_cost len(df) print(f费用异常过滤{before_drop} - {after_drop_cost}删除 {before_drop - after_drop_cost} 条) # 5. 重新计算住院天数以入院出院日期为准 # 这里采用常见口径住院天数 (出院日期 - 入院日期).days # 如果结果为0按1天处理当天入院当天出院 df[calc_bed_days] (df[discharge_date] - df[admission_date]).dt.days df[calc_bed_days] df[calc_bed_days].apply(lambda x: x if x 0 else 1) # 对比原始bed_days和计算bed_days df[bed_days_diff] abs(df[bed_days] - df[calc_bed_days]) print(f住院天数不一致记录数{(df[bed_days_diff] 0).sum()} 条) # 以计算值为准 df[bed_days] df[calc_bed_days] # 6. 生成年月字段便于后续按月份汇总 df[discharge_month] df[discharge_date].dt.to_period(M).astype(str) # 删除辅助列 df df.drop(columns[subtotal_cost, calc_bed_days, bed_days_diff]) print(f数据清洗完成{total_records} - {len(df)}删除 {total_records - len(df)} 条记录) return df def save_clean_data(df, output_path): 保存清洗后的数据 df.to_csv(output_path, indexFalse, encodingutf-8-sig) print(f清洗后数据已保存至 {output_path})清洗逻辑中有一个值得留意的点bed_days字段与(discharge_date - admission_date).days计算的结果可能会不一致。这种情况在真实医院数据中经常出现原因往往是手工录入误差、跨科室转科记录拆分、或者业务上对“入院当天是否计算”的口径差异。我的处理原则是以日期计算值为准同时保留差异统计方便后续核对原始数据。4.3 KPI 指标计算完成清洗后进入核心的指标计算环节。# 文件路径scripts/kpi_calc.py import pandas as pd import numpy as np def calc_dept_kpi(df): 按科室维度计算核心运营指标 指标说明 - 出院人次 - 平均住院日 - 次均费用 - 药占比 - 耗材占比 - 检查检验占比 - 医疗服务收入占比 - 床位使用率简化口径住院天数总和 / (科室开放床位数 * 期间天数) # 科室开放床位数本例假设固定值 bed_config { 心血管内科: 60, 呼吸内科: 50, 消化内科: 45, 骨科: 55, 神经外科: 40 } # 计算期间天数按数据中的最大月份统计 months df[discharge_month].unique() days_in_period len(months) * 30 # 简化处理每月按30天 results [] for dept, group in df.groupby(dept): total_cases len(group) total_bed_days group[bed_days].sum() total_cost group[total_cost].sum() drug_cost group[drug_cost].sum() consumable_cost group[consumable_cost].sum() exam_cost group[exam_cost].sum() service_cost group[service_cost].sum() avg_hospital_days total_bed_days / total_cases if total_cases 0 else 0 avg_cost_per_case total_cost / total_cases if total_cases 0 else 0 drug_ratio drug_cost / total_cost * 100 if total_cost 0 else 0 consumable_ratio consumable_cost / total_cost * 100 if total_cost 0 else 0 exam_ratio exam_cost / total_cost * 100 if total_cost 0 else 0 service_ratio service_cost / total_cost * 100 if total_cost 0 else 0 open_beds bed_config.get(dept, 50) bed_usage_rate total_bed_days / (open_beds * days_in_period) * 100 results.append({ 科室: dept, 出院人次: total_cases, 平均住院日: round(avg_hospital_days, 2), 次均费用(元): round(avg_cost_per_case, 2), 药占比(%): round(drug_ratio, 2), 耗材占比(%): round(consumable_ratio, 2), 检查检验占比(%): round(exam_ratio, 2), 医疗服务收入占比(%): round(service_ratio, 2), 床位使用率(%): round(bed_usage_rate, 2) }) dept_kpi_df pd.DataFrame(results) dept_kpi_df dept_kpi_df.sort_values(出院人次, ascendingFalse).reset_index(dropTrue) return dept_kpi_df def calc_monthly_kpi(df): 按月份维度计算全院核心指标趋势 results [] for month, group in df.groupby(discharge_month): total_cases len(group) total_bed_days group[bed_days].sum() total_cost group[total_cost].sum() drug_cost group[drug_cost].sum() results.append({ 月份: month, 出院人次: total_cases, 平均住院日: round(total_bed_days / total_cases, 2) if total_cases 0 else 0, 次均费用(元): round(total_cost / total_cases, 2) if total_cases 0 else 0, 药占比(%): round(drug_cost / total_cost * 100, 2) if total_cost 0 else 0 }) monthly_kpi_df pd.DataFrame(results) monthly_kpi_df monthly_kpi_df.sort_values(月份).reset_index(dropTrue) return monthly_kpi_df这里有几个细节值得展开说明。一是床位使用率的计算。真实场景中床位使用率需要“实际占用总床日数 ÷ 实际开放总床日数”而“实际开放总床日数”需要知道每天实际开放的床位数。本文示例为了简化假设科室开放床位固定、期间按 30 天/月估算演示的是“口径和算法”而非精确业务值。真实项目中开放床位数应该从床位管理系统中取数。二是费用结构占比。计算时需要注意分母统一使用总费用四个分项占比加总应该接近 100%。由于模拟数据中total_cost是四舍五入后的值四个分项之和可能与total_cost略有差异这在实际情况中很常见。如果差异过大说明费用分类规则可能有问题需要追溯源头。4.4 结果可视化与报告输出指标计算完成后需要把结果输出为方便阅读的形式。除了打印在控制台我还会生成一个 Excel 报告文件和两张图表。# 文件路径scripts/report.py import pandas as pd import matplotlib.pyplot as plt from matplotlib import rcParams # 设置中文字体避免图表中文乱码 rcParams[font.sans-serif] [SimHei, Microsoft YaHei, PingFang SC] rcParams[axes.unicode_minus] False def save_chart(dept_kpi_df, output_path../output/dept_cost_structure.png): 生成各科室费用结构堆叠柱状图 dept_names dept_kpi_df[科室] drug_ratio dept_kpi_df[药占比(%)] consumable_ratio dept_kpi_df[耗材占比(%)] exam_ratio dept_kpi_df[检查检验占比(%)] service_ratio dept_kpi_df[医疗服务收入占比(%)] plt.figure(figsize(10, 6)) plt.bar(dept_names, drug_ratio, label药品, color#4472C4) plt.bar(dept_names, consumable_ratio, bottomdrug_ratio, label耗材, color#ED7D31) plt.bar(dept_names, exam_ratio, bottomdrug_ratio consumable_ratio, label检查检验, color#A5A5A5) plt.bar(dept_names, service_ratio, bottomdrug_ratio consumable_ratio exam_ratio, label医疗服务, color#70AD47) plt.xlabel(科室) plt.ylabel(占比(%)) plt.title(各科室费用结构分析) plt.legend() plt.xticks(rotation15) plt.tight_layout() plt.savefig(output_path, dpi150) plt.close() print(f费用结构图已保存至 {output_path}) def save_excel_report(dept_kpi_df, monthly_kpi_df, output_path../output/hospital_kpi_report.xlsx): 生成 Excel 报告包含两个工作表 with pd.ExcelWriter(output_path, engineopenpyxl) as writer: dept_kpi_df.to_excel(writer, sheet_name科室KPI分析, indexFalse) monthly_kpi_df.to_excel(writer, sheet_name月度趋势分析, indexFalse) print(fExcel 报告已保存至 {output_path})然后编写主流程入口main.py把这些步骤串起来# 文件路径main.py import os import sys sys.path.append(os.path.join(os.path.dirname(__file__), scripts)) from data_generator import generate_inpatient_data from data_clean import load_raw_data, clean_data, save_clean_data from kpi_calc import calc_dept_kpi, calc_monthly_kpi from report import save_chart, save_excel_report def main(): # 1. 生成模拟数据 print( 第一步生成模拟数据 ) os.makedirs(data/raw, exist_okTrue) os.makedirs(data/processed, exist_okTrue) os.makedirs(output, exist_okTrue) raw_path data/raw/inpatient_2024_q1.csv if not os.path.exists(raw_path): df_raw generate_inpatient_data(5000) df_raw.to_csv(raw_path, indexFalse, encodingutf-8-sig) print(f模拟数据已生成{raw_path}) else: print(f检测到已有原始数据{raw_path}) # 2. 加载并清洗数据 print(\n 第二步数据清洗 ) df load_raw_data(raw_path) df_clean clean_data(df) processed_path data/processed/inpatient_2024_q1_clean.csv save_clean_data(df_clean, processed_path) # 3. 指标计算 print(\n 第三步KPI 指标计算 ) dept_kpi_df calc_dept_kpi(df_clean) monthly_kpi_df calc_monthly_kpi(df_clean) print(\n【科室 KPI 汇总】) print(dept_kpi_df.to_string(indexFalse)) print(\n【月度趋势】) print(monthly_kpi_df.to_string(indexFalse)) # 4. 报表输出 print(\n 第四步报表输出 ) save_excel_report(dept_kpi_df, monthly_kpi_df) save_chart(dept_kpi_df) print(\n✅ 全流程执行完成请查看 output 目录下的文件) if __name__ __main__: main()4.5 运行与预期结果在项目根目录执行python main.py正常情况下控制台会输出以下信息 第一步生成模拟数据 模拟数据已生成data/raw/inpatient_2024_q1.csv 第二步数据清洗 原始数据加载完成共 5000 条记录 日期字段缺失过滤5000 - 4946删除 54 条 异常住院天数过滤4946 - 4901删除 45 条 费用异常过滤4901 - 4889删除 12 条 住院天数不一致记录数约 1500 条模拟数据随机波动导致 数据清洗完成5000 - 4889删除 111 条记录 清洗后数据已保存至 data/processed/inpatient_2024_q1_clean.csv 第三步KPI 指标计算 【科室 KPI 汇总】 科室 出院人次 平均住院日 次均费用(元) 药占比(%) ...由于模拟数据使用了随机数具体数值每次运行可能略有不同但整体分布应该与预期一致骨科、神经外科的次均费用较高消化内科的药占比相对较高心血管内科的出院人次可能位列前茅。Excel 报告和费用结构图会生成在output目录下方便后续汇报使用。5. 常见问题与排查思路在实际跑数据、做分析的过程中最容易遇到的问题集中在数据质量、指标口径和代码执行三个层面。下面按错误现象列出排查思路。问题现象常见原因解决思路原始数据量很大但清洗后数据量骤减过滤条件设置过严如日期缺失、费用为 0、住院天数异常逐条打印过滤日志确认每条过滤规则删除的记录数先统计再删除避免误删图表中文显示为方块乱码matplotlib 默认字体不含中文字符设置rcParams[font.sans-serif]指定系统中文字体计算出的药占比超过 100%分子分母口径不一致如药品费用包含门诊药品分母只统计住院费用检查数据来源和字段含义统一统计口径平均住院日为 0 或负数入院日期晚于出院日期或日期格式转换失败清洗阶段统一使用pd.to_datetime(..., errorscoerce)处理非法日期过滤异常记录同一患者有多条住院记录导致人次重复计算未按住院号去重或转科产生了多条记录根据业务规则区分“一次住院多个科室”和“真正的重复录入”按住院主键去重代码运行时提示找不到模块脚本目录未添加到sys.path或依赖库未安装检查main.py中的sys.path.append并确认pip install已执行Excel 报告打不开文件被占用或 openpyxl 版本兼容问题关闭已打开的文件重新运行升级 openpyxlpip install --upgrade openpyxl这里特别强调一个容易被忽视的问题清洗顺序会影响最终结果。比如如果先做费用异常过滤再做日期缺失过滤统计日志中的删除顺序就不一样最终保留的记录数也可能不同。建议在项目中固定清洗流程顺序并把每一步的删除数量写到日志里方便追溯。另外如果你接手的是一个已经在跑的分析脚本不要急着改逻辑。先运行一遍确认当前的输出结果再针对问题做定向优化。很多时候“指标算出来不对”并不是代码写错了而是口径定义本身就有歧义。6. 最佳实践与工程建议6.1 数据治理先定标准再写代码我在多个数据分析项目中得到的经验是数据治理工作 80% 的问题出在源头而不是分析环节。具体到医院场景信息科推动数据治理时重点要关注三件事第一编码规范化。科室编码、医生编码、收费项目编码、诊断编码尽量使用国家或行业标准编码避免各科室自行编号。编码不一致后面的关联分析会非常痛苦。第二主数据管理。药品字典、耗材字典、科室字典、人员字典要有统一维护机制保证各业务系统引用同一套主数据。第三数据质量规则前置。在数据录入界面增加必要的校验规则比如住院日期不能早于出生日期、费用不能为负数。源头录入错误少了下游清洗工作量自然降低。6.2 指标口径用文档固化用代码落地指标口径不能只存在分析人员的脑子里必须形成正式文档并在代码中体现。我的习惯是为每个指标建立一个配置化字段包含指标名称、计算方法、数据来源、更新频率、责任人等信息。代码中只负责实现口径变更时走正式的评审流程更新文档和代码而不是某个人悄悄改了一下算法。这看起来有点重但对于医院这种多科室、多角色的协作环境口径一致比计算效率更重要。6.3 性能优化大数据量下的处理策略本文示例只有 5000 条记录跑起来非常快。但真实医院一个季度的住院数据可能达到几十万甚至上百万条加上费用明细表、检查检验表数据规模会大得多。遇到大数据量场景建议按以下层级优化数据筛选前置。尽量在 SQL 层面完成时间过滤、科室过滤只把需要的字段导入 Python。使用合适的数据类型。将object类型的字符串列转为category可以减少内存占用在大数据集上提升分组计算速度。避免逐行循环。pandas 中apply和for循环性能较差优先使用向量化操作。分块加载。如果 CSV 文件实在太大可以使用pd.read_csv(..., chunksize100000)分块读取处理。必要时引入数据库。数据量超过单机内存时建议使用 PostgreSQL、Doris、ClickHouse 等数据库完成聚合计算Python 只负责结果分析和可视化。6.4 安全与合规患者隐私是红线医疗数据的特殊性在于任何分析都必须优先考虑患者隐私和数据安全。在开发分析工具时有几个原则必须遵守分析数据尽量脱敏。在进入分析库之前去除姓名、身份证号、详细住址、联系电话等直接标识信息。遵循最小权限原则。不同角色只能访问与工作相关的数据不要给所有分析人员开放全库权限。使用模拟数据开发测试。本文使用模拟数据就是这个原因真实数据只在经过授权和安全审计的环境中使用。建立数据审计机制。谁在什么时间访问了什么数据要有留痕。内部数据严禁外传。特别是原始明细数据即使脱敏后也应根据管理要求确定是否可以对外提供。这些原则在代码层面未必能完全体现但作为数据分析工程师必须在设计系统时就把合规要求考虑进去。6.5 工程化从脚本到可持续运行一个分析脚本能跑通和能在生产环境稳定运行中间还有很大距离。工程化建议如下配置外置。数据库连接信息、文件路径、参数配置不要硬编码在脚本里使用 YAML、环境变量或配置中心管理。任务调度。使用 cronLinux或任务计划程序Windows定时执行月报脚本实现每月自动跑数。日志记录。每个步骤输出日志记录处理时间、数据量、异常信息方便问题回溯。版本管理。代码纳入 Git 管理变更走分支和评审流程避免随意改动影响线上报表。结果校验。每次生成报告前与上月结果做一致性交叉校验发现异常及时预警。做到这几点数据分析才能真正从“一次性工作”变成“可持续的数据服务”。7. 下一步可以做什么如果你已经完整跑通了本文的示例并且理解了指标计算和数据清洗的逻辑接下来可以从以下几个方向继续深入。方向一接入真实数据源。尝试从医院 HIS 或病案系统中导出一份脱敏数据按照本文的清洗流程重新处理你会发现真实数据的“脏乱差”程度远远超过模拟数据。遇到具体问题时结合第 5 节的排查思路逐项解决这是数据能力提升最快的路径。方向二扩展指标体系。把 DRG/DIP 相关的分组数据、病例组合指数CMI、时间消耗指数、费用消耗指数纳入分析框架。这些指标更贴近医保支付改革后的医院管理实际计算逻辑也更复杂值得花时间深入研究。方向三搭建可视化看板。本文只生成了静态图表和 Excel 报告。你可以在此基础上使用 Superset、FineBI、Power BI 等工具把 KPI 结果做成实时看板让管理者随时看到运营状态。方向四构建底层数据仓库。当分析需求越来越多直接在业务库上跑分析会影响业务系统性能。此时需要考虑建设医院数据仓库或运营数据中心ODR将各业务系统数据抽取、转换、加载到分析库中形成统一的指标口径管理平台。数据分析在医院场景中的价值并不在于把指标算得多精确而在于让管理者和临床科室基于同一份可信数据展开对话。希望本文的代码和思路能帮你迈出第一步。如果你在自己的项目里遇到了数据清洗、指标口径或者报表自动化方面的问题欢迎在评论区留言交流。
返回列表