如何统计患者症状发作次数及实现超48小时无症状的康复计数?
症状发作次数统计与康复计数实现方案
我来帮你一步步解决这个问题,咱们先拆解需求,再用R的data.table工具实现你要的统计逻辑,最终结果会和你给出的示例完全匹配。
需求回顾
- symptom_episode(症状发作次数):当症状从0变为1时,开启新的发作周期;所有连续的1属于同一episode,0对应的episode为0。
- recovery(康复计数):当症状消失(symptom=0)的区间是最后一段无发作的区间,且这段区间时长超过48小时,那么该区间内的所有0都标记为1;其他情况为0(和你给出的示例逻辑一致)。
分步实现代码
1. 加载数据并处理日期格式
首先把原始数据的date列转为时间格式,方便后续计算时间差:
library(data.table) # 原始数据 dat <- data.table( date=c("2015-01-01 06:00:00 UTC","2015-01-01 16:53:00 UTC","2015-01-02 05:15:00 UTC","2015-01-03 05:28:00 UTC","2015-01-04 05:13:00 UTC","2015-01-05 05:25:00 UTC","2015-01-06 05:11:00 UTC","2015-01-07 05:25:00 UTC","2015-01-08 05:20:00 UTC","2015-01-09 05:17:00 UTC","2015-01-09 15:25:00 UTC","2015-01-10 05:22:00 UTC","2015-01-11 05:19:00 UTC"), symptom=c(0,1,1,1,0,0,1,1,0,0,0,0,0) ) # 转换日期为POSIXct格式(带时区) dat[, date := as.POSIXct(date, tz = "UTC")]
2. 计算症状发作次数symptom_episode
核心思路是标记新发作的起点,再通过累加得到episode编号:
# 标记新发作的起点:当前症状为1,且前一行症状为0 dat[, new_episode := as.integer(symptom == 1 & shift(symptom, fill = 0) == 0)] # 累加新发作标记得到episode编号,症状为0时设为0 dat[, symptom_episode := cumsum(new_episode) * symptom]
这里用shift函数获取前一行的症状值,fill=0处理第一行没有前一行的情况;cumsum累加后乘以symptom,确保0对应的episode为0,完全符合示例要求。
3. 计算康复计数recovery
根据示例逻辑,我们需要判断每一段无发作的区间是否是最终的康复区间(后面没有再发作,且时长超过48小时):
# 按发作分组:每个连续的0块属于上一个发作的组 dat[, group := cumsum(new_episode)] # 标记每个0块是否是最后一段无发作区间(后面没有再出现症状) dat[, is_last_block := ifelse(symptom == 0, shift(symptom, type = "lead", fill = 0) == 0, FALSE)] # 计算每个0块的持续时长(小时) dat[, block_duration := difftime(max(date), min(date), units = "hours"), by = group] # 标记recovery:最后一段0块且时长超过48小时则为1,否则0;症状为1的行设为0 dat[symptom == 0, recovery := as.integer(is_last_block & block_duration > 48), by = group] dat[symptom == 1, recovery := 0] # 清理中间辅助列(可选,如果你不需要保留的话) dat[, c("new_episode", "group", "is_last_block", "block_duration") := NULL]
最终结果
运行完上述代码后,你会得到和示例完全一致的输出:
date symptom symptom_episode recovery 1: 2015-01-01 06:00:00 0 0 0 2: 2015-01-01 16:53:00 1 1 0 3: 2015-01-02 05:15:00 1 1 0 4: 2015-01-03 05:28:00 1 1 0 5: 2015-01-04 05:13:00 0 0 0 6: 2015-01-05 05:25:00 0 0 0 7: 2015-01-06 05:11:00 1 2 0 8: 2015-01-07 05:25:00 1 2 0 9: 2015-01-08 05:20:00 0 0 1 10: 2015-01-09 05:17:00 0 0 1 11: 2015-01-09 15:25:00 0 0 1 12: 2015-01-10 05:22:00 0 0 1 13: 2015-01-11 05:19:00 0 0 1
额外建议
- 如果你的实际康复逻辑是**“症状消失后,从某个时间点开始超过48小时没有发作就标记为康复”**(而不是只标记最终的康复区间),可以把recovery的计算逻辑换成:用
nafill填充最近一次症状发作的时间,计算当前时间与该时间的差,再用cummax标记超过48小时后的所有0为1,我可以帮你调整代码。 - 你提到的
rollapply方法也可以实现,但data.table的分组和窗口函数在处理时间序列时效率更高,尤其是数据量较大的场景,更适合生产环境使用。
内容的提问来源于stack exchange,提问作者traily
相关产品推荐
相关产品推荐

