如何在dplyr::join_by中结合分组变量与不等连接?
动态分组变量下的dplyr不等连接方案
要实现动态分组变量的不等连接,核心是动态生成join_by()的条件列表,把提取到的分组变量和固定不等条件结合起来。借助rlang包的符号转换与拼接功能即可完成,完全适配dplyr/Tidyverse生态,兼容dbplyr。
核心修改思路
- 将分组变量字符串(
group_vars)转为dplyr可识别的符号对象 - 合并分组变量的相等连接条件与固定不等条件(
size >= break_min、size < break_max)为统一条件列表 - 用
!!!运算符将条件列表动态传入join_by()
修改后的核心代码
library(rlang) # 动态构建join条件 join_conditions <- c( syms(group_vars), expr(size >= break_min), expr(size < break_max) ) # 执行连接 grouped_df %>% inner_join(breaks, by = join_by(!!!join_conditions)) %>% select(-break_min, -break_max) %>% select(all_of(group_vars), port_b, everything())
完整可运行代码
library(dplyr, warn.conflicts = FALSE) library(tidyr) library(rlang) # 生成数据代码 set.seed(2023) df <- expand_grid(month = seq(as.Date("2010-01-01"), as.Date("2020-01-01"), by = "1 month"), port_a = 1:5L, id = 1:100L) %>% mutate(size = runif( nrow(.), min = 1000, max = 10000), ret = rnorm(n = nrow(.))) get_breaks <- function(data, var, n_portfolios) { probs <- seq(0, 1, length.out = n_portfolios + 1) data %>% summarize(port_b = list(1:(n_portfolios + 1)), breaks = list(quantile({{ var }}, probs)), .groups = "keep") %>% unnest(cols = c(port_b, breaks)) %>% arrange(port_b) %>% mutate(break_min = breaks, break_max = lead(breaks)) %>% filter(port_b < n_portfolios + 1) %>% select(-breaks) %>% ungroup() } grouped_df <- df %>% group_by(month, port_a) group_vars <- group_vars(grouped_df) breaks <- get_breaks(grouped_df, size, 5) # 动态构建join条件并执行连接 join_conditions <- c(syms(group_vars), expr(size >= break_min), expr(size < break_max)) grouped_df %>% inner_join(breaks, by = join_by(!!!join_conditions)) %>% select(-break_min, -break_max) %>% select(all_of(group_vars), port_b, everything())
关键说明
syms(group_vars):把分组变量的字符串向量转为符号列表,让join_by()识别为同名列的相等连接条件expr():创建不等条件的表达式对象,保证语法符合dplyr要求!!!:将条件列表中的元素逐个“展开”传入join_by(),效果等价于手动写出所有条件- 该方案完全兼容dbplyr,所有操作都是Tidyverse的非标准求值语法,能被正确翻译为SQL
内容的提问来源于stack exchange,提问作者Ian Gow
相关产品推荐
相关产品推荐

