dplyr 1.1.0更新后case_when()中lag()函数运行变慢求助
dplyr 1.1.0中lag() + case_when()组合性能骤降的解决方案
问题确认
你遇到的是dplyr 1.1.0版本的已知性能回归问题,已有不少用户反馈:在分组环境下,case_when()内多次调用lag()会触发大量重复计算,导致性能暴跌。根源是1.1.0版本调整了窗口函数的求值逻辑,每次lag()调用都会重新遍历整个分组数据,而非复用之前的计算结果,在多分组、多lag调用的场景下,重复计算的开销会被急剧放大。
快速解决办法
1. 提前预计算lag变量,避免重复调用
把case_when()里用到的所有lag(event)和lag(date)提前计算好,再在条件判断中直接引用预计算的变量,让每个lag值只计算一次:
library(tibble) library(dplyr) ids <- rep(c("01A", "02B", "03C"), each = 7) dates <- seq(as.Date("2022-01-01"), by = "month", length.out = 21) events <- sample(c(0, 1), 21, replace = TRUE) df <- tibble(id = ids, date = dates, event = events) start_time <- Sys.time() df2 <- df |> arrange(id, date) |> group_by(id) |> # 提前计算所有需要的lag变量 mutate( lag_event1 = lag(event, n=1), lag_date1 = lag(date, n=1), lag_event2 = lag(event, n=2), lag_date2 = lag(date, n=2), lag_event3 = lag(event, n=3), lag_date3 = lag(date, n=3), lag_event4 = lag(event, n=4), lag_date4 = lag(date, n=4), lag_event5 = lag(event, n=5), lag_date5 = lag(date, n=5), lag_event6 = lag(event, n=6), lag_date6 = lag(date, n=6), lag_event7 = lag(event, n=7), lag_date7 = lag(date, n=7), # 用预计算的变量做判断 prev_event = case_when( event == 1 ~ 0, lag_event1 == 1 & (date - lag_date1 <= 65) & (date - lag_date1 >= 15) ~ 1, lag_event2 == 1 & (date - lag_date2 <= 65) & (date - lag_date2 >= 15) ~ 1, lag_event3 == 1 & (date - lag_date3 <= 65) & (date - lag_date3 >= 15) ~ 1, lag_event4 == 1 & (date - lag_date4 <= 65) & (date - lag_date4 >= 15) ~ 1, lag_event5 == 1 & (date - lag_date5 <= 65) & (date - lag_date5 >= 15) ~ 1, lag_event6 == 1 & (date - lag_date6 <= 65) & (date - lag_date6 >= 15) ~ 1, lag_event7 == 1 & (date - lag_date7 <= 65) & (date - lag_date7 >= 15) ~ 1, TRUE ~ 0 ) ) |> # 可选:删除预计算的中间变量 select(-starts_with("lag_event"), -starts_with("lag_date")) end_time <- Sys.time() end_time - start_time
这个调整能直接减少70%以上的计算量,在你的测试数据上,dplyr 1.1.0下的运行时间会回到接近1.0.10的水平。
2. 用滑动窗口替代多lag判断
如果你的时间窗口固定为15-65天,可以用slider包的滑动窗口函数直接判断区间内是否有事件发生,逻辑更简洁,性能也更稳定:
library(slider) df2 <- df |> arrange(id, date) |> group_by(id) |> mutate( prev_event = case_when( event == 1 ~ 0, # 滑动窗口:当前日期往前推65天到15天的区间内,是否存在event=1 slide_index_lgl( .x = event, .i = date, .f = ~any(.x == 1), .before = 65, .after = -15 # 排除间隔小于15天的最近记录 ) ~ 1, TRUE ~ 0 ) )
这种方法不需要手动指定n值,能自动适配不规则的随访间隔,同时从根源上避免了多lag调用的重复计算问题。
3. 临时回退到dplyr 1.0.10版本
如果暂时不想修改代码,可以用以下命令回退版本:
install.packages("dplyr", version = "1.0.10", repos = "https://cran.r-project.org")
等待dplyr官方修复该性能回归问题后再升级。
内容的提问来源于stack exchange,提问作者Edwin Armitage
相关产品推荐
相关产品推荐

