在mutate_if调用中提取列名

时间:2018-02-19 14:23:20

标签: r dplyr nse

我想在函数调用mutate_if中提取列名。有了这个,我想在一个中查找一个值 不同的表并使用查找值填充缺失值。我尝试使用quosure语法,但它不起作用。 是否有可能直接提取列名?

示例数据

df <- structure(list(x = 1:10, 
               y = c(1L, 2L, 3L, NA, 1L, 2L, 3L, NA, 1L, 2L), 
               z = c(NA, 2L, 3L, NA, NA, 2L, 3L, NA, NA, 2L), 
               a = c("a", "b", "c", "d", "e", "a", "b", "c", "d", "e")), 
          .Names = c("x", "y", "z", "a"), 
          row.names = c(NA, -10L), 
          class = c("tbl_df", "tbl", "data.frame"))
df_lookup <- tibble(x = 0L, y = 5L, z = 8L)

无效

直接以某种方式提取名称不起作用。

df %>% 
  mutate_if(is.numeric, funs({
    x <- .
    x <- enquo(x)
    lookup_value <- df_lookup %>% pull(quo_name(x))
    x <- ifelse(is.na(x), lookup_value, x)
    return(x)
  }))

使用额外的功能我可以提取名称但是替换不再起作用。

custom_mutate <- function(v) {
  v <- enquo(v)
  lookup_value <- df_lookup %>% pull(quo_name(v))

  # ifelse(is.na((!!v)), lookup_value, (!!v))
}

df %>% 
  mutate_if(is.numeric, funs(custom_mutate(v = .)))

作品

如果我将df作为附加参数添加到我的自定义函数中,它可以工作,但有没有办法没有这个?这感觉不对,而不是dplyr是什么意思......如果我错了,请纠正我;)
除此之外,我必须使用UQE代替!!,而不是Programming with dplyr中所说的:

  

UQE()仅供专家使用

custom_mutate2 <- function(v, df) {
  v <- enquo(v)
  lookup_value <- df_lookup %>% pull(quo_name(v))

  df %>% 
    mutate(UQE(v) := ifelse(is.na((!!v)), lookup_value, (!!v))) %>% 
    pull(!!v)
}

df %>% 
  mutate_if(is.numeric, funs(custom_mutate2(v = ., df = df)))

预期输出

# A tibble: 10 x 4
#        x     y     z a    
#    <int> <int> <int> <chr>
#  1     1     1     8 a    
#  2     2     2     2 b    
#  3     3     3     3 c    
#  4     4     5     8 d    
#  5     5     1     8 e    
#  6     6     2     2 a    
#  7     7     3     3 b    
#  8     8     5     8 c    
#  9     9     1     8 d    
# 10    10     2     2 e   

2 个答案:

答案 0 :(得分:6)

您必须使用quo代替enquo

#enquo(.) :
<quosure: empty>
~function (expr) 
{
    enexpr(expr)
}
...

#quo(.) :
<quosure: frame>
~x
<quosure: frame>
~y
<quosure: frame>
~z

用你的例子:

mutate_if(df, is.numeric, funs({
  lookup_value <- df_lookup %>% pull(quo_name(quo(.)))
  ifelse(is.na(.), lookup_value, .)
}))

# A tibble: 10 x 4
       x     y     z a    
   <int> <int> <int> <chr>
 1     1     1     8 a    
 2     2     2     2 b    
 3     3     3     3 c    
 4     4     5     8 d    
 5     5     1     8 e    
 6     6     2     2 a    
 7     7     3     3 b    
 8     8     5     8 c    
 9     9     1     8 d    
10    10     2     2 e    

答案 1 :(得分:1)

Julien Nvarre's answer绝对正确(您需要使用quo)但是,因为我的第一个想法也是使用enquo我已经查看了为什么必须使用quo 1}}代替:

如果我们查看mutate_if的来源,我们可以看到它是如何构建的:

dplyr:::mutate_if
#> function (.tbl, .predicate, .funs, ...) 
#> {
#>     funs <- manip_if(.tbl, .predicate, .funs, enquo(.funs), caller_env(), 
#>         ...)
#>     mutate(.tbl, !(!(!funs)))
#> }
#> <environment: namespace:dplyr>

通过略微修改覆盖mutate_if中的dplyr函数,我可以插入对print()的调用,这样我就可以查看传递给funs的{​​{1}}对象{1}}:

mutate

然后,运行代码将使用此修改后的mutate_if <- function (.tbl, .predicate, .funs, ...) { funs <- dplyr:::manip_if(.tbl, .predicate, .funs, enquo(.funs), caller_env(), ...) print(funs) } 函数::

mutate_if

现在,我们可以看到传递给mutate调用的函数列表已经替换了df <- structure(list(x = 1:10, y = c(1L, 2L, 3L, NA, 1L, 2L, 3L, NA, 1L, 2L), z = c(NA, 2L, 3L, NA, NA, 2L, 3L, NA, NA, 2L), a = c("a", "b", "c", "d", "e", "a", "b", "c", "d", "e")), .Names = c("x", "y", "z", "a"), row.names = c(NA, -10L), class = c("tbl_df", "tbl", "data.frame")) df_lookup <- tibble(x = 0L, y = 5L, z = 8L) df %>% mutate_if(is.numeric, funs({ x <- . x <- enquo(x) lookup_value <- df_lookup %>% pull(quo_name(x)) x <- ifelse(is.na(x), lookup_value, x) return(x) })) #> $x #> <quosure> #> expr: ^{ #> x <- x #> x <- enquo(x) #> lookup_value <- df_lookup %>% pull(quo_name(x)) #> x <- ifelse(is.na(x), lookup_value, x) #> return(x) #> } #> env: 0000000007FBBFA0 #> #> $y #> <quosure> #> expr: ^{ #> x <- y #> x <- enquo(x) #> lookup_value <- df_lookup %>% pull(quo_name(x)) #> x <- ifelse(is.na(x), lookup_value, x) #> return(x) #> } #> env: 0000000007FBBFA0 #> #> $z #> <quosure> #> expr: ^{ #> x <- z #> x <- enquo(x) #> lookup_value <- df_lookup %>% pull(quo_name(x)) #> x <- ifelse(is.na(x), lookup_value, x) #> return(x) #> } #> env: 0000000007FBBFA0 变量的列名。这意味着,在语句中,有一个名为.xy的变量,其值来自z

想象一下这个简单的案例,我们有:

df

从此,希望您可以推断出为什么要使用library(rlang) x <- 1:10 quo(x) #> <quosure> #> expr: ^x #> env: 0000000007615318 enquo(x) #> <quosure> #> expr: ^<int: 1L, 2L, 3L, 4L, 5L, ...> #> env: empty 而不是quo。您位于列名称之后,该列名称是变量的名称 - 由enquo提供给您。

因此,使用quo代替quo而不首先将其分配给变量:

enquo