如何在data.table中按组传播特定事件后的值?
问题:按组传播data.table中的特殊事件值
在data.table中按id分组,event列的基础值为1或-1。当出现非1/-1的特殊事件值时,需要将该值在组内向下传播,直到遇到下一个特殊事件为止。
提供的数据结构
temp_dt = structure(list(date = structure(c(18263L, 18264L, 18267L, 18268L, 18269L, 18270L, 18271L, 18274L, 18275L, 18276L, 18277L, 18278L, 18282L, 18283L, 18284L, 18285L, 18288L, 18289L, 18290L, 18291L, 18292L, 18295L, 18296L, 18297L, 18298L, 18299L, 18302L, 18303L, 18304L, 18305L, 18306L, 18310L, 18311L, 18312L, 18313L, 18316L, 18317L, 18318L, 18319L, 18320L, 18323L, 18324L, 18325L, 18326L, 18327L, 18330L, 18331L, 18332L, 18333L, 18334L, 18337L, 18338L, 18339L, 18340L, 18341L, 18344L, 18345L, 18346L, 18347L, 18348L ), class = c("IDate", "Date")), id = c(43L, 43L, 43L, 43L, 43L, 43L, 43L, 43L, 44L, 44L, 44L, 44L, 44L, 44L, 44L, 44L, 44L, 44L, 44L, 44L, 44L, 44L, 44L, 44L, 44L, 44L, 44L, 44L, 44L, 44L, 44L, 44L, 44L, 44L, 44L, 44L, 44L, 45L, 45L, 45L, 45L, 45L, 45L, 45L, 45L, 45L, 45L, 45L, 45L, 45L, 45L, 45L, 45L, 45L, 45L, 45L, 45L, 45L, 45L, 45L), event = c(-1, -1, -1, -1, -1, -1, -1, -1, 1, 1, 1, 1, 1, 1, -4, 1, 1, 1, 1, 1, 1, -2, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, -1, -2, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 4, -1, -1, -1, -1, -1, -1, -1)), row.names = c(NA, -60L), class = c("data.table", "data.frame"))
前20行数据预览:
> head(temp_dt, 20) date id event 1: 2020-01-02 43 -1 2: 2020-01-03 43 -1 3: 2020-01-06 43 -1 4: 2020-01-07 43 -1 5: 2020-01-08 43 -1 6: 2020-01-09 43 -1 7: 2020-01-10 43 -1 8: 2020-01-13 43 -1 9: 2020-01-14 44 1 10: 2020-01-15 44 1 11: 2020-01-16 44 1 12: 2020-01-17 44 1 13: 2020-01-21 44 1 14: 2020-01-22 44 1 15: 2020-01-23 44 -4 16: 2020-01-24 44 1 17: 2020-01-27 44 1 18: 2020-01-28 44 1 19: 2020-01-29 44 1 20: 2020-01-30 44 1
期望输出结果
date id event signal 1: 2020-01-02 43 -1 -1 2: 2020-01-03 43 -1 -1 3: 2020-01-06 43 -1 -1 4: 2020-01-07 43 -1 -1 5: 2020-01-08 43 -1 -1 6: 2020-01-09 43 -1 -1 7: 2020-01-10 43 -1 -1 8: 2020-01-13 43 -1 -1 9: 2020-01-14 44 1 1 10: 2020-01-15 44 1 1 11: 2020-01-16 44 1 1 12: 2020-01-17 44 1 1 13: 2020-01-21 44 1 1 14: 2020-01-22 44 1 1 15: 2020-01-23 44 -4 -4 16: 2020-01-24 44 1 -4 17: 2020-01-27 44 1 -4 18: 2020-01-28 44 1 -4 19: 2020-01-29 44 1 -4 20: 2020-01-30 44 1 -4 21: 2020-01-31 44 1 -4 22: 2020-02-03 44 -2 -2 23: 2020-02-04 44 1 -2 24: 2020-02-05 44 1 -2 25: 2020-02-06 44 1 -2 26: 2020-02-07 44 1 -2 27: 2020-02-10 44 1 -2 28: 2020-02-11 44 1 -2 29: 2020-02-12 44 1 -2 30: 2020-02-13 44 1 -2 31: 2020-02-14 44 1 -2 32: 2020-02-18 44 1 -2 33: 2020-02-19 44 1 -2 34: 2020-02-20 44 1 -2 35: 2020-02-21 44 1 -2 36: 2020-02-24 44 1 -2 37: 2020-02-25 44 1 -2 38: 2020-02-26 45 -1 -1 39: 2020-02-27 45 -2 -2 40: 2020-02-28 45 -1 -2 41: 2020-03-02 45 -1 -2 42: 2020-03-03 45 -1 -2 43: 2020-03-04 45 -1 -2 44: 2020-03-05 45 -1 -2 45: 2020-03-06 45 -1 -2 46: 2020-03-09 45 -1 -2 47: 2020-03-10 45 -1 -2 48: 2020-03-11 45 -1 -2 49: 2020-03-12 45 -1 -2 50: 2020-03-13 45 -1 -2 51: 2020-03-16 45 -1 -2 52: 2020-03-17 45 -1 -2 53: 2020-03-18 45 4 4 54: 2020-03-19 45 -1 4 55: 2020-03-20 45 -1 4 56: 2020-03-23 45 -1 4 57: 2020-03-24 45 -1 4 58: 2020-03-25 45 -1 4 59: 2020-03-26 45 -1 4 60: 2020-03-27 45 -1 4 date id event signal
我的尝试及错误分析
尝试一
temp_dt[, signal:= ifelse(abs(event) == 1 & shift(abs(event)) != 1, shift(event), event) , by = id]
错误原因:仅处理了当前行是1/-1且上一行是特殊值的单次传播,无法将特殊值持续传递到后续所有行;组内首行会生成NA。
尝试二
temp_dt[, signal := { rn <- 1:.N i2 <- which(abs(event) != 1) fcase( rn %inrange% .(i2 + 1, i2 + 3), event, rn %in% i2, event )}, by = id]
错误原因:硬编码了固定的传播范围(仅3行),但实际需要传播到下一个特殊值前的所有行;未处理无特殊值的组,会产生大量NA。
尝试三
temp_dt[, signal := cummax(abs(event)), by = id]
错误原因:cummax取绝对值的累积最大值,丢失了原始值的符号;无法正确切换到下一个特殊值(比如id=44中-4之后的-2,累积最大值仍为4)。
简洁data.table解决方案
方法一:利用nafill填充
temp_dt[, signal := nafill(replace(event, abs(event) == 1, NA), type = "locf"), by = id]
解释:将所有1/-1替换为NA,再用locf(最近观测值向前填充)规则,用最近的非NA值(特殊值或组初始值)填充后续所有NA,实现特殊值的持续传播。
方法二:分组标记后填充
temp_dt[, grp := cumsum(abs(event) != 1), by = id] temp_dt[, signal := event[1], by = .(id, grp)] temp_dt[, grp := NULL] # 可选,删除临时分组列
解释:按id分组,用cumsum生成新的子分组标记(每次遇到特殊值就新增分组),然后每个子分组内取第一个值作为signal,自动完成特殊值的组内传播。
内容的提问来源于stack exchange,提问作者Saurabh
相关产品推荐
相关产品推荐

