基于R语言处理月度与季度混合面板数据:按ID分组实现当期与上期值的变动方向对比
解决分组后多频率面板数据的变动方向判断问题
这个场景的核心难点是不同id的数据频率不一致:M01、M02是月度连续数据,而S01是季度数据(只有部分月份有实际值),需要让每个有实际值的行,对应上一个有实际值的行的实际值来对比,而不是固定滞后1期。下面是完整的解决方案:
1. 加载依赖包并处理日期格式
首先确保你安装了dplyr和lubridate包,然后把原数据中的date因子转换成标准日期格式,保证时间顺序绝对正确:
library(dplyr) library(lubridate) # 你的原始数据 df <- structure(list(id = structure(c(1L, 1L, 1L, 1L, 1L, 1L, 1L, 2L, 2L, 2L, 2L, 2L, 2L, 2L, 3L, 3L, 3L, 3L, 3L, 3L, 3L, 3L), .Label = c("M01", "M02", "S01"), class = "factor"), date = structure(c(2L, 3L, 4L, 5L, 6L, 7L, 8L, 2L, 3L, 4L, 5L, 6L, 7L, 8L, 1L, 2L, 3L, 4L, 5L, 6L, 7L, 8L), .Label = c("2020-12", "2021-01", "2021-02", "2021-03", "2021-04", "2021-05", "2021-06", "2021-07"), class = "factor"), actual = c(3.4, 5.4, 7.4, 7.4, 7.5, 8, 8.9, 10.8, 10.1, 8.2, 10.1, 9.4, 10.1, 9.4, -0.3, NA, NA, 8.6, NA, NA, 8.3, NA), pred = c(3.288889774, 5.819407687, 6.705608369, 6.054457292, 5.582409131, 7.01052472, 9.742902434, 10.98571396, 6.522003651, 9.688977242, 10.39801463, 9.398991615, 9.764616936, 9.855033457, 0.493311422, 8.403722942, 8.174854517, 8.573117852, 8.403065801, 8.684289455, 8.719079247, 8.259439468)), class = "data.frame", row.names = c(NA, -22L)) # 转换日期格式 df <- df %>% mutate(date = ym(date))
2. 创建对比周期分组,匹配对应上期实际值
这里的关键是给每个id下的有效实际值行(actual非NA)创建递增分组,让每个分组内的所有行共享上一个有效实际值:
df <- df %>% group_by(id) %>% mutate( # 标记哪些行有实际值 has_actual = !is.na(actual), # 为有效实际值行创建连续分组,非有效行继承上一个有效行的分组 cycle_group = cumsum(has_actual) ) %>% # 按id和cycle_group分组,提取每组的第一个实际值(即上一个有效周期的实际值) group_by(id, cycle_group) %>% mutate(prev_actual = first(actual)) %>% # 回到id分组,把第一个周期的prev_actual设为NA(无上期数据) group_by(id) %>% mutate(prev_actual = ifelse(cycle_group == 1, NA, prev_actual)) %>% ungroup()
比如S01的2020-12是第一个周期(cycle_group=1),prev_actual为NA;2021-01到2021-03属于cycle_group=2,prev_actual就是2020-12的-0.3;2021-04到2021-06属于cycle_group=3,prev_actual就是2021-03的8.6,完美匹配季度对比逻辑。
3. 计算变动方向并整理结果
最后按照需求判断实际值和预测值的变动方向,同时处理NA情况:
df_final <- df %>% mutate( act_direction = case_when( # 任一值为NA则返回NA is.na(actual) | is.na(prev_actual) ~ NA_character_, actual > prev_actual ~ "increase", actual < prev_actual ~ "decrease", actual == prev_actual ~ "unchanged" ), pred_direction = case_when( is.na(pred) | is.na(prev_actual) ~ NA_character_, pred > prev_actual ~ "increase", pred < prev_actual ~ "decrease", pred == prev_actual ~ "unchanged" ) ) %>% # 保留需要的列,并把日期转回原格式 select(id, date, actual, pred, act_direction, pred_direction) %>% mutate(date = format(date, "%Y-%m"))
结果验证
查看S01的关键行:
- 2021-03的
act_direction是increase(8.6 > -0.3),pred_direction是increase(8.57 > -0.3),符合预期 - 2021-06的
act_direction是decrease(8.3 < 8.6),pred_direction是increase(8.719 > 8.6),和你给出的预期输出完全一致
内容的提问来源于stack exchange,提问作者ah bon
相关产品推荐
相关产品推荐

