如何让 dplyr::mutate() 在函数内部调用时使用变量名?

How to get dplyr::mutate() to work with variable names when called inside a function?

我正在研究 Pokemon API 中的数据(实际上并没有使用 API,只是从 github 中提取 .csv 文件)。在一个名为 pokemon_types.csv 的包含每个神奇宝贝类型的窄格式文件中(一个神奇宝贝最多可以有两种类型),这些类型被编码为整数(本质上是因子)。我想通过使用查找 table (types.csv) 来标记这些级别,也来自 API,其中包含作为 id (1, 2, 3,等)和我想用作标签的相应 identifier(正常、战斗、飞行等)。

> head(read_csv(path("pokemon_types.csv")), 10)
# A tibble: 10 x 3
   pokemon_id type_id  slot
        <dbl>   <dbl> <dbl>
 1          1      12     1
 2          1       4     2
 3          2      12     1
 4          2       4     2
 5          3      12     1
 6          3       4     2
 7          4      10     1
 8          5      10     1
 9          6      10     1
10          6       3     2
> head(read_csv(path("types.csv")))
# A tibble: 6 x 4
     id identifier generation_id damage_class_id
  <dbl> <chr>              <dbl>           <dbl>
1     1 normal                 1               2
2     2 fighting               1               2
3     3 flying                 1               2
4     4 poison                 1               2
5     5 ground                 1               2
6     6 rock                   1               2

当我将所有步骤分别通过管道传输时,我的代码可以正常工作,但由于我将至少执行此标记步骤十几次左右,所以我尝试将其放入一个函数中。问题是当我改为调用该函数时(据我所知它具有完全相同的步骤)它会抛出 object not found 错误。

设置:

library(readr)
library(magrittr)
library(dplyr)
library(tidyr)

options(readr.num_columns = 0)

# Append web directory to filename
path <- function(x) {
  paste0("https://raw.githubusercontent.com/",
         "PokeAPI/pokeapi/master/data/v2/csv/", x)
}

违规函数:

# Use lookup table to label factor variables
label <- function(data, variable, lookup) {
  mutate(data, variable = factor(variable, 
                                 levels = read_csv(path(lookup))$id,
                                 labels = read_csv(path(lookup))$identifier))
}

这个没有使用该功能的版本可以工作:

df.types <-
  read_csv(path("pokemon_types.csv")) %>%
  mutate(type_id = factor(type_id, 
                          levels = read_csv(path("types.csv"))$id,
                          labels = read_csv(path("types.csv"))$identifier)) %>%
  spread(slot, type_id)

head(df.types)

它returns:

# A tibble: 6 x 3
  pokemon_id `1`   `2`   
       <dbl> <fct> <fct> 
1          1 grass poison
2          2 grass poison
3          3 grass poison
4          4 fire  NA    
5          5 fire  NA    
6          6 fire  flying

使用该功能的这个版本没有:

df.types <-
  read_csv(path("pokemon_types.csv")) %>%
  label(type_id, "types.csv") %>%
  spread(slot, type_id)

它returns:

Error in factor(variable, 
                levels = read_csv(path(lookup))$id, 
                labels = read_csv(path(lookup))$identifier) : 
  object 'type_id' not found 

我知道这里有几件事情可能不是最理想的(例如,每次下载 lookup 两次)但我更感兴趣的是为什么一个看起来与某些书面代码相同的函数能够做到这一点不工作了。我确定我只是犯了一个愚蠢的错误。

多亏了有用的评论,我才能够了解所有关于非标准评估的知识并找出解决方案:

label <- function(data, variable, lookup) {
  variable <- enquo(variable)
  data %>%
    mutate(!!variable := factor(!!variable, 
                                 levels = read_csv(path(lookup))$id,
                                 labels = read_csv(path(lookup))$identifier))
}

主要特征是 enquo(),它充当 "quasiquote",!!,"unquotes" 变量,因此可以通过参数进行解释,以及 :=,这允许在两侧取消引用。

我尝试实施完全避免 dplyr 的解决方案,但未能成功,但至少这是可行的。