在dplyr中如何通过动态列名实现分组过滤并生成多数据框?
解决dplyr中动态按列名过滤并合并数据框的问题
嗨,我来帮你搞定这个动态过滤的问题~ 你代码里的核心问题是:paste0(col.sub)只是把列名转成了字符串,并没有真正引用数据框里对应的列,所以过滤条件根本没生效。下面给你几种可行的解决方案:
方法1:使用dplyr的.data代词(推荐)
.data是dplyr提供的特殊代词,可以通过字符串直接引用列,非常适合这种动态场景。修改你的循环代码如下:
library(dplyr) library(data.table) col.names <- c("ref.day1","ref.day2","ref.day3") temp.list <- list() for(cl in seq_along(col.names)){ col.sub <- col.names[cl] columns <- c("loc.id","day",col.sub) df.sub <- df[,columns] # 这里用.data[[col.sub]]引用对应列 temp.dat <- df.sub %>% group_by(loc.id) %>% dplyr::filter(day >= .data[[col.sub]]) temp.list[[cl]] <- temp.dat } final.dat <- rbindlist(temp.list)
方法2:使用rlang的非标准求值(sym + !!)
如果你习惯用dplyr的非标准求值语法,可以把字符串列名转成符号,再用!!解引用:
library(dplyr) library(data.table) library(rlang) col.names <- c("ref.day1","ref.day2","ref.day3") temp.list <- list() for(cl in seq_along(col.names)){ col.sub <- col.names[cl] columns <- c("loc.id","day",col.sub) df.sub <- df[,columns] # 将字符串转成符号后解引用 temp.dat <- df.sub %>% group_by(loc.id) %>% dplyr::filter(day >= !!sym(col.sub)) temp.list[[cl]] <- temp.dat } final.dat <- rbindlist(temp.list)
方法3:用purrr替代循环(更简洁)
如果想摆脱循环,用purrr的map函数可以让代码更简洁优雅:
library(dplyr) library(purrr) library(data.table) col.names <- c("ref.day1","ref.day2","ref.day3") final.dat <- col.names %>% map(function(col_sub) { df %>% select(loc.id, day, all_of(col_sub)) %>% group_by(loc.id) %>% filter(day >= .data[[col_sub]]) }) %>% rbindlist()
补充说明
all_of(col_sub)在select里用来按字符串选择列,确保即使列名是动态的也能正确选中;- 三种方法最终效果一致,你可以根据自己的习惯选择;
- 其实这里
group_by(loc.id)是多余的,因为过滤条件day >= ref.dayX是逐行判断的,分组不会影响结果,你可以去掉它来提升效率。
内容的提问来源于stack exchange,提问作者89_Simple
相关产品推荐
相关产品推荐

