使用data.table计算滚动均值未统计无值日期的问题与解决
问题:用data.table复现tidyverse的补全日期+滚动均值结果
背景
有两个按group分组的DataFrame,合并后需要完成两个操作:
- 补全所有
group的缺失日期 - 按
group计算90天滚动均值
tidyverse方法已得到预期结果,但原data.table实现存在偏差,需要修正并理解差异原因。
差异原因
- 日期补全范围不同
- tidyverse用合并后数据的全局最小/最大日期生成序列,所有
group都从全局最早日期开始补全 - 原data.table代码按每组自身的最小/最大日期生成序列,导致每组起始日期不一致,且缺少全局更早的日期
- tidyverse用合并后数据的全局最小/最大日期生成序列,所有
- 滚动均值计算的数据源差异
原data.table代码中,补全后的日期序列不包含全局最早到组内最早之间的日期(这些日期在tidyverse中被补为0),因此runner::mean_run计算时未将这些0值纳入,导致滚动均值结果偏高。
data.table解决方案
以下代码完全复现tidyverse的结果,包含全局日期补全和正确的滚动均值计算:
1. 加载依赖并生成示例数据
library(data.table) library(runner) library(lubridate) set.seed(123) # 确保结果可复现 df1 <- data.frame( group = sample(c('g1', 'g2', 'g3', 'g4'), 365, replace = TRUE), date = sample(seq(as.Date('2023-01-01'), as.Date('2023-12-31'), by = "day"), 365), values = rnorm(365, 10, 1) ) df2 <- data.frame( group = sample(c('g1', 'g2', 'g3', 'g4'), 365, replace = TRUE), date = sample(seq(as.Date('2023-01-01'), as.Date('2023-12-31'), by = "day"), 365), values = rnorm(365, 10, 1) )
2. 合并数据并补全全局日期
# 合并两个DataFrame dt <- rbindlist(list(df1, df2)) # 生成全局统一的日期序列(覆盖所有group的时间范围) global_dates <- seq(min(dt$date), max(dt$date), by = "day") # 生成所有group与全局日期的交叉表,确保每个group都有完整日期 full_idx <- dt[, .(date = global_dates), by = .(group = unique(dt$group))] # 合并原数据与交叉表,补全缺失日期对应的values为NA setkey(dt, group, date) setkey(full_idx, group, date) dt_full <- dt[full_idx]
3. 替换NA为0并计算滚动均值
# 按group处理:替换NA为0,计算90天滚动均值 dt_result <- dt_full[, `:=`( values = fifelse(is.na(values), 0, as.numeric(values)), date = ymd(date), running_values = mean_run(x = values, k = 90, idx = date) ), by = group ][order(group, date)]
结果验证
修正后的data.table结果与tidyverse完全一致:
- 所有
group从全局最早日期(2023-01-01)开始 - 缺失日期的
values被替换为0,且这些0值被纳入滚动均值计算(例如g1的2023-01-04的滚动均值为(0+0+0+9.412)/4=2.353)
内容的提问来源于stack exchange,提问作者user23687835
相关产品推荐
相关产品推荐

