按组比较非重复行对并计算差值的高效dplyr实现
简洁dplyr解决方案:分组内非重复行对比较
可复现示例数据
首先生成测试用的数据集:
library(tibble) library(dplyr) library(tidyr) set.seed(123) df <- tibble( Name = rep(c("Alice", "Bob"), each = 4), Date = seq.Date(as.Date("2023-01-01"), as.Date("2023-01-08"), by = "day"), Value = sample(10:30, 8, replace = FALSE) )
核心解决方案
利用dplyr分组结合tidyr::crossing生成行对,避免繁琐的自连接和purrr映射,代码简洁且性能更优:
result <- df %>% group_by(Name) %>% mutate(row_id = row_number()) %>% # 分组内生成行序号,用于过滤重复行对 crossing(., ., name = c("x", "y")) %>% # 生成分组内所有行对组合 filter(row_id.x < row_id.y) %>% # 保留单向非重复行对(避免A-B和B-A重复计算) mutate( # 计算Value差值和日期差 Value_diff = abs(Value.x - Value.y), Date_diff = abs(as.numeric(Date.x - Date.y)), # 确定行对的日期区间 date_min = pmin(Date.x, Date.y), date_max = pmax(Date.x, Date.y) ) %>% # 计算区间内非极值记录数(定义:区间内Value既不是该区间最大值也不是最小值的记录数) mutate( interval_max = max(Value[Date >= date_min & Date <= date_max]), interval_min = min(Value[Date >= date_min & Date <= date_max]), Non_extreme_count = sum(Value[Date >= date_min & Date <= date_max] != interval_max & Value[Date >= date_min & Date <= date_max] != interval_min) ) %>% # 整理输出列 select( Name, Date1 = Date.x, Date2 = Date.y, Value1 = Value.x, Value2 = Value.y, Value_diff, Date_diff, Non_extreme_count ) %>% ungroup() # 查看结果示例 head(result)
结果示例
# A tibble: 6 × 8 Name Date1 Date2 Value1 Value2 Value_diff Date_diff Non_extreme_count <chr> <date> <date> <int> <int> <int> <dbl> <int> 1 Alice 2023-01-01 2023-01-02 28 14 14 1 0 2 Alice 2023-01-01 2023-01-03 28 21 7 2 1 3 Alice 2023-01-01 2023-01-04 28 17 11 3 2 4 Alice 2023-01-02 2023-01-03 14 21 7 1 0 5 Alice 2023-01-02 2023-01-04 14 17 3 2 1 6 Alice 2023-01-03 2023-01-04 21 17 4 1 0
自定义调整说明
- 如果需要保留双向行对(即同时包含A-B和B-A),删除
filter(row_id.x < row_id.y)即可,但会增加计算量,建议用单向对提升性能。 - 如果“非极值记录数”定义为区间内除当前行对之外的记录数,替换
Non_extreme_count的计算逻辑为:Non_extreme_count = sum(Date >= date_min & Date <= date_max) - 2
方案优势
- 无需嵌套
purrr::map,代码逻辑线性清晰,易维护 - 用
crossing替代自连接,减少数据冗余 - 通过过滤单向行对,直接减少50%的计算量,大幅提升性能
- 所有计算在分组管道内完成,避免多次数据拆分合并
内容的提问来源于stack exchange,提问作者Emman
相关产品推荐
相关产品推荐

