You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何用Python和Pandas基于患者历史预约计算医疗预约爽约率?

问题:计算患者历史预约爽约率

我正在使用巴西医疗预约数据集,该数据集包含巴西医疗预约信息及患者是否到场的记录,字段包括预约ID、患者ID、预约日期时间、挂号日期时间等。我需要为每个预约计算基于该患者此前所有预约的爽约率:例如患者有3次预约,其中2次到场,其第4次预约的爽约率为1/3;首次预约的爽约率为0。

我尝试了以下代码,但它没有正确高效地计算历史预约的爽约率:

# convert appointment and scheduled dates to datetime format
df['AppointmentDay'] = pd.to_datetime(df['AppointmentDay'])
df['ScheduledDay'] = pd.to_datetime(df['ScheduledDay'])

# sort the DataFrame by PatientId and AppointmentDay
df = df.sort_values(['PatientId', 'AppointmentDay'])

# create a new column with the time difference between scheduled and appointment date
df['time_diff'] = (df['AppointmentDay'].dt.date - df['ScheduledDay'].dt.date).dt.days

# create a new column to store the no-show rate for each appointment
df['no_show_rate'] = 0

# loop through each row of the DataFrame
for index, row in df.iterrows():
    # get the PatientId and AppointmentDay for the current row
    patient_id = row['PatientId']
    appointment_day = row['AppointmentDay']
    
    # select all previous appointments for the current patient
    previous_appointments = df.loc[(df['PatientId'] == patient_id) & (df['AppointmentDay'] < appointment_day)]
    
    # calculate the no-show rate based on the previous appointments
    appointment_count = len(previous_appointments)
    no_show_count = len(previous_appointments.loc[previous_appointments['No-show'] == 'Yes'])
    if appointment_count > 0:
        no_show_rate = no_show_count / appointment_count
    else:
        no_show_rate = 0
    
    # update the 'no_show_rate' column for the current row
    df.at[index, 'no_show_rate'] = no_show_rate

# print first 5 rows
print(df.head())

问题分析

原代码逻辑方向正确,但存在两个核心问题:

  1. 效率极低:iterrows()逐行循环处理大数据集时速度极慢,Pandas原生的向量化操作性能远超循环。
  2. 日期筛选误差:如果AppointmentDay包含时间戳,AppointmentDay < appointment_day会排除同一天晚于当前预约的记录,但实际上同一天的预约如果是先发生的应该被计入历史(需根据业务逻辑调整,这里默认按日期排序后,同一天的预约按顺序处理)。

修正后的代码

使用Pandas的分组和窗口函数实现高效计算:

import pandas as pd

# 转换日期格式并提取日期部分,避免时间戳干扰
df['AppointmentDay'] = pd.to_datetime(df['AppointmentDay']).dt.date
df['ScheduledDay'] = pd.to_datetime(df['ScheduledDay']).dt.date

# 按患者ID和预约日期排序,确保历史顺序正确
df = df.sort_values(['PatientId', 'AppointmentDay']).reset_index(drop=True)

# 将爽约标记转为数值:爽约为1,到场为0
df['no_show_flag'] = (df['No-show'] == 'Yes').astype(int)

# 计算每个患者累计爽约数(排除当前预约,用shift(1)滞后一行)
df['cumulative_no_shows'] = df.groupby('PatientId')['no_show_flag'].cumsum().shift(1)
# 计算每个患者累计预约数(排除当前预约,cumcount从0开始计数)
df['cumulative_appointments'] = df.groupby('PatientId').cumcount()

# 计算历史爽约率,首次预约爽约率为0
df['no_show_rate'] = df.apply(
    lambda row: row['cumulative_no_shows'] / row['cumulative_appointments'] 
    if row['cumulative_appointments'] > 0 else 0,
    axis=1
)

# 可选:删除中间辅助字段
df = df.drop(['no_show_flag', 'cumulative_no_shows', 'cumulative_appointments'], axis=1)

print(df.head())

代码说明

  • 日期处理:用dt.date提取日期部分,确保同一天的预约能按排序顺序正确区分历史与当前。
  • 爽约标记数值化:将字符串转为整数,方便后续累计计算。
  • 累计计算:
    • cumsum().shift(1):先累计每个患者的爽约数,再将结果向后偏移一行,这样当前行的数值就是之前所有预约的爽约总数。
    • cumcount():对每个患者的预约从0开始计数,数值正好等于当前预约之前的历史预约数量。
  • 爽约率计算:通过apply处理除以0的情况,首次预约时累计数为0,爽约率设为0。

内容的提问来源于stack exchange,提问作者pooya ensafi

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.29 19:20:55