在R语言中仅对同类别连续行进行数据分组聚合
问题描述
我有如下模拟数据框:
df_in <- data.frame( time = c(1,2,3,4,5,6,7,8,9,10), state = c(1,1,1,2,2,3,1,1,1,2), returns = c(0.5,0.2,0.3,0.4,0.1,0.2,1.1,0.8,0.5,0.2) )
数据预览:
time state returns 1 1 1 0.5 2 2 1 0.2 3 3 1 0.3 4 4 2 0.4 5 5 2 0.1 6 6 3 0.2 7 7 1 1.1 8 8 1 0.8 9 9 1 0.5 10 10 2 0.2
我需要对数据做聚合:将同一state的连续行合并计算returns,优先用复利计算,若实现困难,简单求和也可。期望输出如下:
df_out <- data.frame( time_start = c(1,4,6,7,10), time_end = c(3,5,6,9,10), state = c(1,2,3,1,2), returns = c(1.0,0.5,0.2,2.4,0.2) )
输出预览:
time_start time_end state returns 1 1 3 1 1.0 2 4 5 2 0.5 3 6 6 3 0.2 4 7 9 1 2.4 5 10 10 2 0.2
注:示例中用了简单求和,我更倾向于复利计算方案。我知道有类似的分组方法,但那些方法需要标识ID,我的数据里没有,希望用dplyr实现,我是dplyr新手,求帮助。
解决方案
核心是先给连续的同一state行创建分组标识,再按组聚合计算。
1. 简单求和版本
用dplyr生成连续分组后直接求和:
library(dplyr) df_sum <- df_in %>% # 生成连续state的分组ID:当前行state和上一行不同时,分组ID+1 mutate(group_id = cumsum(state != lag(state, default = first(state)))) %>% # 按分组ID和state聚合 group_by(group_id, state) %>% summarise( time_start = first(time), time_end = last(time), returns = sum(returns), .groups = "drop" ) %>% # 移除临时分组ID列 select(-group_id) print(df_sum)
输出和你期望的df_out完全一致:
state time_start time_end returns <dbl> <dbl> <dbl> <dbl> 1 1 1 3 1 2 2 4 5 0.5 3 3 6 6 0.2 4 1 7 9 2.4 5 2 10 10 0.2
2. 复利计算版本(最优方案)
如果returns是每期收益率,总复利收益用prod(1 + returns) - 1计算;如果是绝对收益,直接用prod(returns)即可,这里按常规收益率逻辑处理:
df_compound <- df_in %>% mutate(group_id = cumsum(state != lag(state, default = first(state)))) %>% group_by(group_id, state) %>% summarise( time_start = first(time), time_end = last(time), # 复利计算总收益 returns = prod(1 + returns) - 1, .groups = "drop" ) %>% select(-group_id) print(df_compound)
输出结果:
state time_start time_end returns <dbl> <dbl> <dbl> <dbl> 1 1 1 3 1.13 2 2 4 5 0.54 3 3 6 6 0.2 4 1 7 9 3.48 5 2 10 10 0.2
关键逻辑说明
cumsum(state != lag(state, default = first(state))):这是生成连续分组的核心,通过对比当前行和上一行的state,只要不一样就累加计数,这样就能把连续相同的state分到同一个组里,避免合并非连续的相同state(比如示例中两次出现的state=1会被分成两个独立组)。group_by(group_id, state):同时按分组ID和state分组,确保分组的准确性。
内容的提问来源于stack exchange,提问作者Theo
相关产品推荐
相关产品推荐

