如何按ID分组创建滚动Top3出行频次最高的国家列?
问题:按ID分组实现滚动Top3出行国家统计
我们有一个包含ID、Country、Date三列的DataFrame,记录人员的出行历史。需要新增Pref1、Pref2、Pref3三列,分别表示每行日期之前,对应ID人员出行次数最多的滚动Top3国家(次数相同时,优先选择最近出行的国家)。
示例输入数据:
mydata <- data.frame( ID = c('A1B1', 'A1B1', 'A1B1', 'A1B1', 'A1B1', 'A1B1', 'A1B1', 'A1B1', 'A2B2', 'A2B2', 'A2B2', 'A2B2', 'A2B2', 'A2B2'), Country = c('Japan', 'USA', 'USA', 'USA', 'Germany', 'Germany', 'Japan', 'France', 'UK', 'Spain', 'Spain', 'UK', 'UK', 'Brazil'), Date = as.Date(c('2010/01/02', '2010/04/18', '2011/03/22', '2011/11/23', '2012/05/09', '2012/09/11', '2014/01/06', '2015/12/11', '2010/04/03', '2010/05/11', '2011/05/01', '2012/03/01', '2013/01/03', '2014/01/04')) )
期望输出的核心逻辑是:每一行的Top3只统计该行日期之前的出行记录,次数相同则按最近出行日期排序,不足3个的用NA填充。
解决方案(R语言实现)
我们可以用dplyr结合purrr来实现这个滚动统计,步骤清晰且容易理解:
- 先按
ID分组,确保每组内的记录按Date升序排列(这一步是基础,保证我们处理的都是历史记录) - 对每组内的每一行,提取该行之前的所有出行记录
- 统计这些历史记录中每个国家的出行次数,同时记录每个国家的最近出行日期
- 按「次数降序→最近日期降序」排序,取前3个国家作为
Pref1-Pref3 - 将结果合并回原DataFrame,清理中间辅助列
完整代码如下:
library(dplyr) library(purrr) result <- mydata %>% arrange(ID, Date) %>% group_by(ID) %>% mutate( # 对每一行,获取该行之前的所有历史出行记录 history = map(row_number(), ~ slice(cur_data(), 1:(.x - 1))), # 处理历史记录,生成符合要求的Top3国家列表 top_countries = map(history, function(df) { if(nrow(df) == 0) { # 没有历史记录时直接返回3个NA return(c(NA, NA, NA)) } # 统计每个国家的出行次数和最近出行日期 df_summary <- df %>% group_by(Country) %>% summarise( count = n(), last_date = max(Date), .groups = "drop" ) %>% # 先按出行次数降序,次数相同则按最近日期降序排序 arrange(desc(count), desc(last_date)) %>% pull(Country) # 填充到固定3个元素,不足的补NA c(df_summary[1:3], rep(NA, max(0, 3 - length(df_summary)))) }), # 将Top3列表拆分为三列 Pref1 = map_chr(top_countries, ~ .x[1]), Pref2 = map_chr(top_countries, ~ .x[2]), Pref3 = map_chr(top_countries, ~ .x[3]) ) %>% # 移除中间生成的辅助列 select(-history, -top_countries) %>% ungroup() # 查看最终结果 print(result)
代码逻辑细节解释
arrange(ID, Date):确保每个ID的记录严格按时间顺序排列,这样处理每一行时,前面的记录都是更早的历史出行数据。map(row_number(), ~ slice(cur_data(), 1:(.x - 1))):针对每组内的第n行,提取前n-1行作为该行的历史出行记录。summarise(count = n(), last_date = max(Date)):既统计每个国家的出行次数,又记录该国家最近一次出行的日期,用于解决次数相同时的排序问题。arrange(desc(count), desc(last_date)):先按出行次数从多到少排序,次数相同的情况下,最近去过的国家排在更前面,符合需求中的优先级规则。c(df_summary[1:3], rep(NA, max(0, 3 - length(df_summary)))):保证我们总是得到3个元素的列表,不足3个国家时用NA填充,方便后续拆分为三列。
运行这段代码后,得到的结果就和你期望的完全一致啦!
内容的提问来源于stack exchange,提问作者Bright
相关产品推荐
相关产品推荐

