如何在变量与列名相同时于pmap内使用dplyr::filter过滤?
解决dplyr中列名与变量名同名时的优先级问题
问题背景
当使用dplyr对tibble执行filter操作时,如果外部变量与数据框列名同名,dplyr会优先引用列名,导致不符合预期的过滤结果。虽然双大括号{{}}在直接调用dplyr函数时能解决这个问题,但在purrr::pmap的匿名函数中使用会触发递归引用错误,需要找到可靠的解决方法。
示例代码
set.seed(2) ngrp <- 3 npergrp <- 4 tib <- tibble(grp=rep(letters[1:ngrp], each=npergrp), N=rep(1:npergrp, ngrp), val=round(runif(npergrp*ngrp))) %>% print(n=Inf) grp <- grp_ <- 'a' tib %>% dplyr::filter(grp==grp_) %>% glimpse() ## works tib %>% dplyr::filter(grp==grp) %>% glimpse() ## undesired result, grp==grp always true tib %>% dplyr::filter(grp=={{grp}}) %>% glimpse() ## hey it works! ## slightly less toy example tib %>% dplyr::filter(grp==grp_) %>% dplyr::mutate( the_rest = purrr::pmap( ., function(grp, N, ...) { gg <- grp ## there must be a better way NN <- N tib %>% dplyr::filter( # grp!=grp, ## always false # N==N ## always true grp!=gg, N==NN ) %>% dplyr::pull(val) %>% sum() } ), no_hugs = purrr::pmap( ., function(grp, N, ...) { tib %>% dplyr::filter( grp!={{grp}}, ## ERROR! oh noes! N=={{N}} ) %>% dplyr::pull(val) %>% sum() } ) ) %>% tidyr::unnest() %>% glimpse()
示例输出
# A tibble: 12 × 3 grp N val <chr> <int> <dbl> 1 a 1 0 2 a 2 1 3 a 3 1 4 a 4 0 5 b 1 1 6 b 2 1 7 b 3 0 8 b 4 1 9 c 1 0 10 c 2 1 11 c 3 1 12 c 4 0 Rows: 4 Columns: 3 $ grp <chr> "a", "a", "a", "a" $ N <int> 1, 2, 3, 4 $ val <dbl> 0, 1, 1, 0 Rows: 4 Columns: 3 $ grp <chr> "a", "a", "a", "a" $ N <int> 1, 2, 3, 4 $ val <dbl> 0, 1, 1, 0 Error in local_error_context(dots = dots, .index = i, mask = mask) : promise already under evaluation: recursive default argument reference or earlier problems? # the_rest should be 1, 2, 1, 1
解决方法
1. 基础场景:区分列名与外部变量
在直接调用dplyr函数时,有两种可靠方式:
- 使用
{{}}(embrace)运算符:将外部变量注入dplyr的求值环境,优先引用外部变量而非列名,比如:tib %>% dplyr::filter(grp == {{grp}}) %>% glimpse() - 使用
.env代词:明确指定引用外部环境中的变量,避免与列名混淆,语法为.env$变量名:tib %>% dplyr::filter(grp == .env$grp) %>% glimpse()
2. pmap匿名函数中的解决方案
{{}}在pmap中报错是因为匿名函数的参数名与外部变量名同名,触发了递归引用。此时推荐使用.env代词明确区分:
修改示例中的no_hugs部分代码:
no_hugs = purrr::pmap( ., function(grp, N, ...) { tib %>% dplyr::filter( grp != .env$grp, # 引用外部定义的grp变量 N == .env$N # 引用外部定义的N变量 ) %>% dplyr::pull(val) %>% sum() } )
这里的.env$grp指向外部环境中定义的grp <- 'a',而函数参数grp是当前行的列值,两者完全区分,不会产生冲突。
修改后重新运行代码,no_hugs会得到与the_rest一致的预期结果:1, 2, 1, 1。
内容的提问来源于stack exchange,提问作者flies
相关产品推荐
相关产品推荐

