为什么dplyr中的filter()函数不能在函数内使用(即使用变量作为列名)?

8

这是一个使用dplyr函数过滤、分组和变换数据的函数。基本管道序列在函数外部运行良好,因此我使用真实的列名。但是将其放入函数中,其中列名是一个变量,一些函数可以正常工作,但有些函数无法正常工作,特别是dplyr :: filter()。例如:

var1 <- c('yes', NA, NA, 'yes', 'yes', NA, NA, NA, 'yes', NA, 'no', 'no', 'no', 'maybe', NA, 'maybe', 'maybe', 'maybe')

var2 <- c(1:18)

df <- data.frame(var1, var2)

这个代码很好用(即过滤了NA值):

df%>%filter(!is.na(var1))

...但是这个不行:

x <- "var1"

df%>%filter(!is.na(x))

...但这个可以:

df%>%select(x)

需要特别筛选的是NA。

尝试使用get("x"),但不起作用,还有切片:

df[!is.na(x),]

...也不太好。

你有什么想法如何在函数内(或外)传递变量给筛选器,并且为什么变量能与其他dplyr函数一起使用?

5个回答

7
我们可以使用sym将其转换为一个符号,然后再使用UQ对其进行评估。
library(rlang)
library(dplyr)
df %>%
   filter(!is.na(UQ(sym(x))))
#     var1 var2
#1    yes    1
#2    yes    4
#3    yes    5
#4    yes    9
#5     no   11
#6     no   12
#7     no   13
#8  maybe   14
#9  maybe   16
#10 maybe   17
#11 maybe   18

1
为什么只能使用filter函数,而不能使用select()函数呢? - Conner M.
@ConnerM。select接受未加引号和加引号的字符串,例如df%>% select('var1')df%>% select(var1)。这不仅适用于filter。如果您想要一些汇总信息,则必须执行相同的步骤。 - akrun
sym属于特定的包吗? - Conner M.
@ConnerM。这是在rlang中的,但是我认为如果你安装了tidyverse并加载了包,它应该会被加载。假设你想要总结df%>%summarise(count = sum(!is.na(UQ(sym(x))))) - akrun
有关 gather() 的任何想法吗? - Conner M.
假设您想基于 x 进行聚合,那么可以使用 gather(df, key, value, -one_of(x)) - akrun

4

由于我的声誉不足以在上面发表评论...我建议您查看我的答案:https://stackoverflow.com/a/45265617/6238025

如果您想用dplyr编写函数,您需要按照此网页上的说明操作:https://rpubs.com/hadley/dplyr-programming

library(tidyverse)
var1 <- c('yes', NA, NA, 'yes', 
          'yes', NA, NA, NA, 'yes', NA, 'no', 
          'no', 'no', 'maybe', NA, 'maybe', 
          'maybe', 'maybe')
var2 <- c(1:18)

df <- data_frame(var1, var2)

your_function <- function(df, filter) {
      # Make filter a quosure
      filter = enquo(filter)

      df %>% 
        filter(!is.na(!!filter)) -> new_df

      return(new_df)
}
new_df <- your_function(df = df, filter = var1)

您也可以在函数内部跳过filter = enquo(filter),然后进行调用:

your_function(df=df, filter=quo(var1))

不过第一种方式更好,因为这样可以更方便地调用函数,您不需要记住quo()

应该可以正常工作!


1

这种方法也可以,而且更简单 - 只需在方括号中引用包含列名的变量,并使用(.)来引用输入数据框:

> df %>% filter(!is.na((.)[x]))
    var1 var2
1    yes    1
2    yes    4
3    yes    5
4    yes    9
5     no   11
6     no   12
7     no   13
8  maybe   14
9  maybe   16
10 maybe   17
11 maybe   18

请注意,这也适用于函数内部:
myfun <- function(df, var) {
  df %>%  filter(!is.na((.)[var]))
 }

 x <- "var1"
 myfun(df, x)

    var1 var2
1    yes    1
2    yes    4
3    yes    5
4    yes    9
5     no   11
6     no   12
7     no   13
8  maybe   14
9  maybe   16
10 maybe   17
11 maybe   18

1

有一个新的包seplyr,它通过标准评估标准来传递到dplyr。请尝试使用它。您可以通过它将普通引用代码传递给dplyr。这使得在dplyr中传递参数和编写函数更加容易。

对于您的情况:

install.packages("seplyr")
library(seplyr)
x<-"var1"
df%>%filter_se(paste0("!is.na(", x , ")"))

0

使用!!rlang::parse_quo(),您可以使用字符变量进行过滤。

下面看两个可重现的例子:

# Create DF
df <- data.frame(
  var1 = c("yes", NA, NA, "yes", "yes", NA, NA, NA, "yes", NA, "no", "no", "no", "maybe", NA, "maybe", "maybe", "maybe"),
  var2 = c(1:18)
)

使用 x 定义变量

x <- "var1"
FILTER <- paste0("!is.na(", x, ")")
df |> dplyr::filter(!!rlang::parse_quo(FILTER, env = parent.frame()))
#>     var1 var2
#> 1    yes    1
#> 2    yes    4
#> 3    yes    5
#> 4    yes    9
#> 5     no   11
#> 6     no   12
#> 7     no   13
#> 8  maybe   14
#> 9  maybe   16
#> 10 maybe   17
#> 11 maybe   18

使用FILTER创建完整的过滤语句
FILTER <- "!is.na(var1)"
df |> dplyr::filter(!!rlang::parse_quo(FILTER, env = parent.frame()))
#>     var1 var2
#> 1    yes    1
#> 2    yes    4
#> 3    yes    5
#> 4    yes    9
#> 5     no   11
#> 6     no   12
#> 7     no   13
#> 8  maybe   14
#> 9  maybe   16
#> 10 maybe   17
#> 11 maybe   18

reprex package (v2.0.1)于2022年9月14日创建


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接