在R中构建带时间戳数据的月度快照——高效实现方案问询
当然可以用tidyverse实现完全向量化的解决方案,不用循环,效率还更高!我来给你一步步拆解思路和代码:
核心思路
我们需要把每个保单的变更记录和目标每月1日合并,通过排序和填充操作,让每个目标日期自动继承最近的、不晚于它的变更值。这样就避免了逐行循环判断。
完整代码
library(tidyverse) # 原始数据 df <- data.frame(policy_no = c(1, 1, 1, 1, 2, 2, 2), date_stamp = as.Date(c("2020-01-15", "2020-04-03", "2020-05-31", "2020-07-22", "2020-01-05", "2020-03-17", "2020-06-04" )), value = c(1000, 2000, 3000, 2000, 500, 1000, 500)) # 生成预期输出 df_output <- df %>% # 按保单分组,确保每个保单单独处理 group_by(policy_no) %>% # 把每个保单需要的目标日期(2020-01-01到2020-08-01每月1日)绑定到对应组 bind_rows(tibble(as_at_date = seq(as.Date("2020-01-01"), as.Date("2020-08-01"), by = "month"))) %>% # 合并变更日期和目标日期为一个统一的日期列,方便排序 mutate(date = coalesce(date_stamp, as_at_date)) %>% # 按保单和日期从小到大排序 arrange(policy_no, date) %>% # 向下填充value:目标日期的空值会自动继承最近的前一个有效变更值 fill(value, .direction = "down") %>% # 只保留目标日期的记录,过滤掉原始变更记录 filter(!is.na(as_at_date)) %>% # 整理列顺序并取消分组 select(policy_no, as_at_date, value) %>% ungroup() # 查看结果 df_output
代码解释
- 分组绑定目标日期:用
group_by(policy_no)确保每个保单独立处理,bind_rows把目标日期序列添加到每个组里,这样每个保单都有了自己的变更记录+目标日期的完整数据集。 - 合并日期列:
coalesce(date_stamp, as_at_date)会优先取date_stamp(变更记录的日期),没有的话取as_at_date(目标日期),生成一个统一的排序依据。 - 排序+填充:
arrange按日期排序后,fill(value, .direction = "down")会把前面的有效变更值向下填充到后面的目标日期行——完美匹配“取最新的不晚于目标日期的变更值”的需求。 - 筛选整理:最后只保留目标日期的行,整理列顺序就得到了预期结果。
备选方案(用fuzzy_join)
如果你需要更灵活的日期匹配逻辑,可以用fuzzyjoin包的模糊连接:
library(fuzzyjoin) # 生成所有保单+目标日期的组合 target_dates <- tibble( policy_no = rep(unique(df$policy_no), each = 8), as_at_date = rep(seq(as.Date("2020-01-01"), as.Date("2020-08-01"), by = "month"), 2) ) # 模糊连接:匹配同保单,且目标日期 >= 变更日期,然后取每个组合的最新变更记录 df_output <- target_dates %>% fuzzy_left_join( df, by = c("policy_no" = "policy_no", "as_at_date" = "date_stamp"), match_fun = list(`==`, `>=`) ) %>% group_by(policy_no, as_at_date) %>% slice_max(date_stamp, n = 1, with_ties = FALSE) %>% ungroup() %>% select(policy_no, as_at_date, value)
这个方法通过模糊连接找到所有符合条件的变更记录,再用slice_max取最新的那条,同样是向量化操作,效率也很高。
内容的提问来源于stack exchange,提问作者Alan
相关产品推荐
相关产品推荐

