如何通过在用户定义的函数中将字符串作为列名传递来进行过滤?

How do you filter by passing a string as a column name in a user-defined function?

我正在编写一个函数,用户可以在其中指定他们想要过滤的列以及他们想要的截止值。在这个例子中,我想过滤掉任何低于 2 的预测试分数。这是一个示例数据集:

library(dplyr)

test <- tibble(name = c("Corey", "Justin", "Sibley", "Kate"),
               pretest_score = c(1:4),
               posttest_score = c(5:8),
               final_score = c(9:12))


filter_function <- function(data, test_type = c(pretest, posttest, final), value) {
  
  test_character <- deparse(substitute(test_type))
  test_score <- paste0(test_character, "_score")
  
  data %>%
    filter({{test_score}} > value)
  
}

filter_function(test, test_type = pretest, value = 2)

我也试过 !!test_score、test_score(周围什么都没有)和来自 rlang 的 ensym(test_score),都无济于事.

注意:我知道在 this 示例中,我可以指定 pretest_score、posttest_score 等作为测试类型,但在我的真实数据集中,我有这些测试的许多维度,用户可以确定(pretest_score、pretest_date、pretest_location 等)的截止值,因此 重要的是我将列前缀与函数本身的后缀(此处为 _score)合并。

感谢您的帮助!

将字符转换为 symbol 并用 !!

求值
filter_function <- function(data, test_type = c(pretest, posttest, 
      final), value) {
  
  test_character <- deparse(substitute(test_type))
  test_score <- paste0(test_character, "_score")
  
  data %>%
    filter(!! rlang::sym(test_score) > value)
  
}

-测试

> filter_function(test, test_type = pretest, value = 2)
# A tibble: 2 × 4
  name   pretest_score posttest_score final_score
  <chr>          <int>          <int>       <int>
1 Sibley             3              7          11
2 Kate               4              8          12

几点:

  • 通常,当在 R 中使用一组选项时,通常使用字符向量,而不是未计算的表达式。 R专门为此提供了match.arg。这也实现了第一个选项的默认值,因此如果我们使用 match.arg 调用函数的调用可以省略 type_test = "pretest",因为这是默认值。

  • .[[test_score]]可以用来指定指定的列

因此我们有

filter_function <- function(data, test_type = c("pretest", "posttest", "final"), 
  value) {

  test_type <- match.arg(test_type)
  test_score <- paste0(test_type, "_score")
  
  data %>%
    filter(.[[test_score]] > value)
  
}

filter_function(test, test_type = "pretest", value = 2)
# A tibble: 2 x 4
  name   pretest_score posttest_score final_score
  <chr>          <int>          <int>       <int>
1 Sibley             3              7          11
2 Kate               4              8          12

# pretest is the default
filter_function(test, value = 2)
# A tibble: 2 x 4
  name   pretest_score posttest_score final_score
  <chr>          <int>          <int>       <int>
1 Sibley             3              7          11
2 Kate               4              8          12

另请注意,我们可以像这样指定函数。用户仍然可以指定“pretest”,因为 match.arg 将匹配前导子字符串。事实上,他们甚至可以指定“pre”或“post”。

filter_function2 <- function(data, 
    test_type = c("pretest_score", "posttest_score", "final_score"), 
    value) {

  test_type <- match.arg(test_type)
  
  data %>%
    filter(.[[test_type]] > value)
  
}

filter_function2(test, test_type = "pretest", value = 2)

基础 R

这也可以在没有像这样的任何包的情况下完成:

filter_function3 <- function(data, test_type = c("pretest", "posttest", "final"), 
  value) {

  test_type <- match.arg(test_type)
  test_score <- paste0(test_type, "_score")
  
  data[data[[test_score]] > value, ]

}

filter_function3(test, test_type = "pretest", value = 2)