【问题标题】:How to filter a data frame programmatically with dplyr and tidy evaluation?如何使用 dplyr 和 tidy 评估以编程方式过滤数据框?
【发布时间】:2017-12-21 09:43:45
【问题描述】:

假设我想以编程方式过滤 starwars 数据框。这是一个简单的示例,可让我根据家园和物种进行过滤:

library(tidyverse)

# a function that allows the user to supply filters
filter_starwars <- function(filters) {
  for (filter in filters) {
    starwars = filter_at(starwars, filter$var, all_vars(. %in% filter$values))
  }

  return(starwars)
}

# filter Star Wars characters that are human, and from either Tatooine or Alderaan
filter_starwars(filters = list(
  list(var = "homeworld", values = c("Tatooine", "Alderaan")),
  list(var = "species", values = "Human")
))

但这并不允许我指定高度过滤器,因为我已经在 .vars_predicatefilter_at() 中硬编码了 %in% 运算符,并且高度过滤器将使用 @ 之一987654327@、&gt;=&lt;&lt;=== 运营商

编写filter_starwars() 函数的最佳方式是什么,以便用户可以提供足够通用的过滤器来过滤任何列并使用任何运算符?

NB 使用现在已弃用的filter_() 方法,我可以传递一个字符串:

filter_(starwars, "species == 'Human' & homeworld %in% c('Tatooine', 'Alderaan') & height > 175")

但同样,这已被弃用。

【问题讨论】:

标签: r dplyr tidyverse rlang


【解决方案1】:

这里有一些方法。

1) 对于这个特殊问题,我们实际上不需要filter_、rlang 或类似的东西。这有效:

filter_starwars <- function(...) {
    filter(starwars, ...)
}

# test
filter_starwars(species == 'Human', 
                homeworld %in% c('Tatooine', 'Alderaan'), 
                height > 175)
)

2)如果有字符参数很重要,那么:

library(rlang)

filter_starwars <- function(...) {
    filter(starwars, !!!parse_exprs(paste(..., sep = ";")))
}

# test
filter_starwars("species == 'Human'", 
                "homeworld %in% c('Tatooine', 'Alderaan')", 
                "height > 175")

2a) 或者如果要传递单个字符向量:

library(rlang)

filter_starwars <- function(filters) {
    filter(starwars, !!!parse_exprs(paste(filters, collapse = ";")))
}

# test 
filter_starwars(c("species == 'Human'", 
                  "homeworld %in% c('Tatooine', 'Alderaan')", 
                  "height > 175"))

【讨论】:

    【解决方案2】:

    试试

    filter_starwars <- function(...) {
      F <- quos(...)
      filter(starwars, !!!F)
    }
    
    filter_starwars(species == 'Human', homeworld %in% c('Tatooine', 'Alderaan'), height > 175)
    # # A tibble: 7 × 13
    #                  name height  mass  hair_color skin_color eye_color birth_year
    #                 <chr>  <int> <dbl>       <chr>      <chr>     <chr>      <dbl>
    # 1         Darth Vader    202   136        none      white    yellow       41.9
    # 2           Owen Lars    178   120 brown, grey      light      blue       52.0
    # 3   Biggs Darklighter    183    84       black      light     brown       24.0
    # 4    Anakin Skywalker    188    84       blond       fair      blue       41.9
    # 5         Cliegg Lars    183    NA       brown       fair      blue       82.0
    # 6 Bail Prestor Organa    191    NA       black        tan     brown       67.0
    # 7     Raymus Antilles    188    79       brown      light     brown         NA
    # # ... with 6 more variables: gender <chr>, homeworld <chr>, species <chr>,
    # #   films <list>, vehicles <list>, starships <list>
    

    https://cran.r-project.org/web/packages/dplyr/vignettes/programming.html。简而言之,quos... 捕获为列表,而不评估参数。 !!! 拼接和取消引用 filter() 中评估的参数。

    【讨论】:

    • 我们如何将此解决方案扩展到函数参数列表可以有其他参数(不是过滤器)的情况?例如,这会失败,因为函数调用中的前两个参数(物种和家园)将被解释为 xy - filter_starwars &lt;- function(x = 1, y = 2, ...) { # skipping for brevity } filter_starwars(species == 'Human', homeworld %in% c('Tatooine', 'Alderaan'), height &gt; 175) 的参数
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2017-12-12
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2022-11-13
    相关资源
    最近更新 更多