2

我正在尝试使用 dplyr 根据动态变量进行过滤。

我发现要让过滤器工作,我需要将变量名括在括号中。但是,如果我将它编程为一个函数,它就不能正常工作。

df_ex <- data.frame(a = 1:10, b = 11:20)

param <- quo(a)

# returns df_ex with column a, only, as expected
df_ex %>%
dplyr::select(!!param)

# returns expected df
df_ex %>%
dplyr::filter((!!param)==5)

# Now for the function
testfun <- function(test_df, filt_var){
   filt_var_mod <- quo(filt_var)

   test_df %>%
    dplyr::filter((!!filt_var_mod)==5)
}

# returns empty df, not as expected
testfun(df_ex, "a")

我想学习为自己找到这些问题的答案,这些问题类型的问题是关于 dplyr 的,所以请随时向我推荐编程小插曲的相关部分

4

4 回答 4

4

如果您的函数接受列名作为字符,则无需引用它,另一方面,您需要将其转换为符号并立即在函数中filter使用UQ或语法评估它们:!!nse

testfun <- function(test_df, filt_var){
    test_df %>%
        dplyr::filter((!!rlang::sym(filt_var)) == 5)
}

testfun(df_ex, "a")
#  a  b
#1 5 15

如果要键入不带引号的列名,则需要enquo,

接受一个引用函数参数的符号,引用提供给该参数的 R 代码,捕获调用函数的环境(以及输入 R 代码的环境),并将它们捆绑在一个 quosure 中。

testfun <- function(test_df, filt_var){
    filt_var_mod <- enquo(filt_var)
    test_df %>%
        dplyr::filter((!!filt_var_mod) == 5)
}

testfun(df_ex, a)
#  a  b
#1 5 15
于 2017-10-26T22:17:03.840 回答
3

从技术上讲,您不需要 rlang 或 tidyeval 或 tibbles 或 dplyr 来解决此类问题,base R 几乎没有留下任何神圣的奶牛,您可以使用 quote、eval、parse 和其他从底部烘焙的 NSE 工具向上。

编辑:@thelatemail 提出的更优雅的解决方案

df_ex <- data.frame(a = 1:10, b = 11:20)

testfun <- function(test_df, filt_var) {
  test_df[test_df[,filt_var] == 5,]
}    

testfun(df_ex, "a")

退货

  a  b
5 5 15

只是为了好玩,一个 data.table 选项也可以工作:

library(data.table)

df_ex <- data.frame(a = 1:10, b = 11:20)

testfun <- function(test_df, filt_var) {
  setDT(test_df,key = filt_var)[.(5)]
}

testfun(df_ex, "a")

回报:

   a  b
1: 5 15
于 2017-10-26T22:37:13.397 回答
0

Base NSE 似乎也可以工作:

testfun2 <- function(test_df, filt_var){
  filt_var_mod <- substitute(filt_var)
  test_df %>% 
    dplyr::filter(eval(filt_var_mod) == 5)
}

testfun2(df_ex, a)


  a  b
1 5 15
于 2017-11-02T19:53:45.040 回答
0

有时,在制作简单函数时,可以使用作用域版本动词来代替潮汐。

例如,如果您想在函数中将名称作为字符串传递,filter_at则可以选择。

下面是使用filter_at变量作为字符串的样子。您传递要过滤的变量,然后在all_varsor中给出谓词函数any_vars。通过使用单个变量进行过滤,您使用哪个变量并不重要。

filter_at(df_ex, "a", all_vars(. == 5) )

 a  b
1 5 15

filter_at可以很容易地在函数中使用。

testfun = function(test_df, filt_var){

    test_df %>%
        dplyr::filter_at(filt_var, all_vars(. == 5) )
}

testfun(df_ex, "a")

  a  b
1 5 15
于 2017-11-02T00:56:21.983 回答