【问题标题】:Tidy evaluation programming with dplyr::case_when使用 dplyr::case_when 进行整洁的评估编程
【发布时间】:2018-06-09 22:13:52
【问题描述】:

我尝试编写一个简单的函数来包裹 dplyr::case_when() 函数。我阅读了https://cran.r-project.org/web/packages/dplyr/vignettes/programming.html 上的 programming with dplyr 文档,但无法弄清楚它如何与 case_when() 函数一起使用。

我有以下数据:

data <- tibble(
   item_name = c("apple", "bmw", "bmw")
)

还有以下列表:

cat <- list(
   item_name == "apple" ~ "fruit",
   item_name == "bmw" ~ "car"
)

那我想写一个类似的函数:

category_fn <- function(df, ...){
   cat1 <- quos(...)
   df %>%
     mutate(category = case_when((!!!cat1)))
}

不幸的是,category_fn(data,cat) 在这种情况下给出了评估错误。我想获得与通过以下方式获得的输出相同的输出:

data %>% 
   mutate(category = case_when(item_name == "apple" ~ "fruit",
                               item_name == "bmw" ~ "car"))

这样做的方法是什么?

【问题讨论】:

标签: r dplyr lazy-evaluation nse


【解决方案1】:

这是另一种以 tidyverse 为中心的方法

cat <- tribble(
    ~name, ~category,
    "apple", "fruit",
    "bmw", "car"
) %>% 
    str_glue_data("item_name == '{name}' ~ '{category}'")

data %>% 
    mutate(category = case_when(!!! map(cat, rlang::parse_expr)))

【讨论】:

    【解决方案2】:

    1) 传递列表使用 wrapr 包中的 let 和问题中的 datacat,无需以任何方式修改输入。

    library(dplyr)
    library(wrapr)
    
    category_fn <- function(data, List) {
      let(c(CATEGORY = toString(sapply(List, format))),
          data %>% mutate(category = case_when(CATEGORY)),
          subsMethod = "stringsubs",
          strict = FALSE)
    }
    category_fn(data, cat) # test
    

    给予:

    # A tibble: 3 x 2
      item_name category
          <chr>    <chr>
    1     apple    fruit
    2       bmw      car
    3       bmw      car
    

    1a) 使用问题中的 tidyeval/rlang 和 datacat

    category_fn <- function(data, List) {
      cat_ <- lapply(List, function(x) do.call("substitute", list(x)))
      data %>% mutate(category = case_when(!!!cat_))
    }
    category_fn(data, cat)
    

    给出与上述相同的结果。

    2) 分别传递列表组件 如果您的意图是分别传递 cat 的每个组件而不是 cat 本身,那么这可行:

    category_fn <- function(data, ...) eval.parent(substitute({
       data %>% mutate(category = case_when(...))
    }))
    
    category_fn(data, item_name == "apple" ~ "fruit",
                       item_name == "bmw" ~ "car") # test
    

    给予:

    # A tibble: 3 x 2
      item_name category
          <chr>    <chr>
    1     apple    fruit
    2       bmw      car
    3       bmw      car
    

    2a) 如果您更喜欢 tidyeval/rlang,那么这种情况很简单:

    library(dplyr)
    library(rlang)
    
    category_fn <- function(data, ...) {
       cat_ <- quos(...)
       data %>% mutate(category = case_when(!!!cat_))
    }
    
    category_fn(data, item_name == "apple" ~ "fruit",
                       item_name == "bmw" ~ "car") # test
    

    【讨论】:

      【解决方案3】:

      首先引用列表中的每个元素:

      cat <- list(
        quo(item_name == "apple" ~ "fruit"),
        quo(item_name == "bmw" ~ "car")
      )
      

      然后您的函数不必引用 cat 对象本身。我还更改了“其他所有”...参数的使用,以在调用中显式引用类别参数:

      category_fn <- function(df, categories){
        df %>%
          mutate(category = case_when(!!!categories))
      }
      

      然后函数的输出和预期的一样:

      category_fn(data, cat)
      # A tibble: 3 x 2
        item_name category
            <chr>    <chr>
      1     apple    fruit
      2       bmw      car
      3       bmw      car
      

      为了完整起见,我注意到类别列表在使用基本 R quote() 函数定义时也适用于您的函数:

      cat <- list(
        quote(item_name == "apple" ~ "fruit"),
        quote(item_name == "bmw" ~ "car")
      )
      > cat
      [[1]]
      item_name == "apple" ~ "fruit"
      
      [[2]]
      item_name == "bmw" ~ "car"
      
      > category_fn(data, cat)
      # A tibble: 3 x 2
        item_name category
            <chr>    <chr>
      1     apple    fruit
      2       bmw      car
      3       bmw      car
      

      【讨论】:

      • 这个解决方案对我有用。但是有什么办法可以绕过为列表中的每个项目写quo这个词吗?我天真的方法是尝试定义quolist &lt;- function(...) { lapply(X = list(...), FUN = quo) },但这似乎不起作用。
      • 是的,原始问题中使用的代码不再产生错误消息。
      猜你喜欢
      • 2018-05-31
      • 1970-01-01
      • 2017-11-16
      • 1970-01-01
      • 2018-07-18
      • 2019-02-28
      • 1970-01-01
      • 2021-01-08
      • 2018-06-10
      相关资源
      最近更新 更多