R语言嵌套循环计算候诊患者数耗时30天,求优化方案
优化急诊等待患者统计代码:从30天到分钟级运行
问题背景
需要统计3.5年时间范围内(共122880个15分钟时间点),每个时间点的等待就诊患者数量及累计等待时长。现有数据:
dates:122880行的15分钟间隔时间序列episode:225000行患者记录,含id(患者ID)、begin(急诊到达时间)、end(急诊离开时间)care:含患者id及begin(医生接诊时间)
原代码采用双层嵌套循环,外层遍历时间点,内层遍历所有患者判断状态,预估运行耗时30天,拆分数据后仍需7天,核心瓶颈是逐行判断患者状态的逻辑。
原代码的核心问题
- 恐怖的时间复杂度:122880 × 225000 ≈ 2.76×10^10次迭代,完全无法高效运行
- 低效的ID匹配:每次通过
care$id==no_episode查找接诊时间,属于线性查找,累计开销极大 - 动态扩容的额外消耗:每次循环用
rbind拼接结果,频繁触发内存重分配,进一步拖慢速度
优化方案:向量化+高效数据操作
核心思路是彻底抛弃逐行循环,利用R的向量化特性或data.table的高效分组、连接能力,将患者的时间窗口与全局时间序列做批量匹配,再聚合统计。
步骤1:合并患者数据,统一时间字段
先把episode和care按患者ID合并,得到每个患者完整的时间线,避免后续反复匹配ID:
# 加载处理大数据更高效的data.table包 library(data.table) # 转换为data.table格式 setDT(episode) setDT(care) setDT(dates) # 合并数据:匹配患者ID,得到每个患者的到达、接诊、离开时间 patient_data <- merge(episode, care, by = "id", suffixes = c("_episode", "_care")) # 过滤无效数据:接诊时间需晚于到达、早于离开时间 patient_data <- patient_data[begin_care > begin_episode & begin_care < end_episode]
步骤2:批量匹配时间点与等待患者
利用data.table的非等连接,一次性找到所有落在患者等待窗口[begin_episode, begin_care)内的时间点:
# 为时间序列设置索引,加速连接(将time_col替换为dates表的实际时间列名) setkey(dates, time_col) setkey(patient_data, begin_episode, begin_care) # 非等连接:匹配每个时间点对应的所有等待患者 wait_matches <- dates[patient_data, on = .(time_col > begin_episode, time_col < begin_care), allow.cartesian = TRUE, .(time_col, id, begin_episode)]
步骤3:聚合统计每个时间点的指标
对匹配结果按时间点分组,直接计算等待人数和累计等待时长:
# 分组计算核心指标 result <- wait_matches[, .( nb_wait = .N, # 当前时间点等待患者数量 delay_total = sum(time_col - begin_episode) # 当前时间点累计等待时长 ), by = time_col] # 补全无等待患者的时间点(人数设为0,时长设为0) final_result <- merge(dates, result, by = "time_col", all.x = TRUE) final_result[is.na(nb_wait), `:=`(nb_wait = 0, delay_total = 0)]
优化效果说明
- 底层优化的非等连接:
data.table的连接逻辑由C语言实现,批量处理匹配,比R原生循环快几个数量级 - 避免冗余操作:合并数据后无需反复查询
care表,减少大量无效计算 - 向量化聚合:分组统计采用向量化操作,远快于逐行累加的循环逻辑
按照这个方案,处理22万患者+12万时间点的数据,预计运行时间在几分钟到几十分钟,完全替代原有的几十天耗时。
内容的提问来源于stack exchange,提问作者coffee_addiction
相关产品推荐
相关产品推荐

