使用具有mutate函数的多个字符串的向量进行Dplyr标准评估

时间:2018-02-17 19:45:43

标签: r dplyr rlang nse standard-evaluation

我正在尝试使用mutate()包向dplyr调用提供包含多个列名的向量。下面的可重复示例:

stackdf <- data.frame(jack = c(1,NA,2,NA,3,NA,4,NA,5,NA),
                      jill = c(1,2,NA,3,4,NA,5,6,NA,7),
                      jane = c(1,2,3,4,5,6,NA,NA,NA,NA))
two_names <- c('jack','jill')
one_name <- c('jack')

#   jack jill jane
#    1    1    1
#   NA    2    2
#    2   NA    3
#   NA    3    4
#    3    4    5
#   NA   NA    6
#    4    5   NA
#   NA    6   NA
#    5   NA   NA
#   NA    7   NA

我能够弄清楚如何使用&#34;一个变量&#34;版本,但不知道如何将其扩展到多个变量?

# the below works as expected, and is an example of the output I desire
stackdf %>% rowwise %>% mutate(test = anyNA(c(jack,jill)))

# A tibble: 10 x 4
    jack  jill  jane  test
   <dbl> <dbl> <dbl> <lgl>
 1     1     1     1 FALSE
 2    NA     2     2  TRUE
 3     2    NA     3  TRUE
 4    NA     3     4  TRUE
 5     3     4     5 FALSE
 6    NA    NA     6  TRUE
 7     4     5    NA FALSE
 8    NA     6    NA  TRUE
 9     5    NA    NA  TRUE
10    NA     7    NA  TRUE


# using the one_name variable works if I evaluate it and then convert to 
# a name before unquoting it
stackdf %>% rowwise %>% mutate(test = anyNA(!!as.name(eval(one_name))))

# A tibble: 10 x 4
    jack  jill  jane  test
   <dbl> <dbl> <dbl> <lgl>
 1     1     1     1 FALSE
 2    NA     2     2  TRUE
 3     2    NA     3 FALSE
 4    NA     3     4  TRUE
 5     3     4     5 FALSE
 6    NA    NA     6  TRUE
 7     4     5    NA FALSE
 8    NA     6    NA  TRUE
 9     5    NA    NA FALSE
10    NA     7    NA  TRUE

如何扩展上述方法以便我可以使用two_names向量?使用as.name只需要一个对象,因此它不起作用。

此处的问题类似:Pass a vector of variable names to arrange() in dplyr。该解决方案&#34;工作&#34;因为我可以使用以下代码:

two_names2 <- quos(c(jack, jill))
stackdf %>% rowwise %>% mutate(test = anyNA(!!!two_names2))

但如果我必须直接输入c(jack, jill)而不是使用two_names变量,它就会失败。是否有一些类似的程序我可以直接使用two_names?这个答案How to pass a named vector to dplyr::select using quosures?使用rlang::syms但是这虽然适用于选择变量(即stackdf %>% select(!!! rlang::syms(two_names)),但它在变异时似乎不适用于提供参数(即stackdf %>% rowwise %>% mutate(test = anyNA(!!! rlang::syms(two_names)))。这个答案是类似的但不起作用:How to evaluate a constructed string with non-standard evaluation using dplyr?

2 个答案:

答案 0 :(得分:4)

您可以使用library(dplyr) stackdf <- data.frame(jack = c(1,NA,2,NA,3,NA,4,NA,5,NA), jill = c(1,2,NA,3,4,NA,5,6,NA,7), jane = c(1,2,3,4,5,6,NA,NA,NA,NA)) two_names <- c('jack','jill') stackdf %>% rowwise %>% mutate(test = anyNA(c(!!!syms(two_names)))) #> Source: local data frame [10 x 4] #> Groups: <by row> #> #> # A tibble: 10 x 4 #> jack jill jane test #> <dbl> <dbl> <dbl> <lgl> #> 1 1. 1. 1. FALSE #> 2 NA 2. 2. TRUE #> 3 2. NA 3. TRUE #> 4 NA 3. 4. TRUE #> 5 3. 4. 5. FALSE #> 6 NA NA 6. TRUE #> 7 4. 5. NA FALSE #> 8 NA 6. NA TRUE #> 9 5. NA NA TRUE #> 10 NA 7. NA TRUE (由dplyr重新导出;或者直接调用它)将字符串强制转换为quosures,所以

stackdf %>% mutate(test = rowSums(is.na(.[two_names])) > 0)
#>    jack jill jane  test
#> 1     1    1    1 FALSE
#> 2    NA    2    2  TRUE
#> 3     2   NA    3  TRUE
#> 4    NA    3    4  TRUE
#> 5     3    4    5 FALSE
#> 6    NA   NA    6  TRUE
#> 7     4    5   NA FALSE
#> 8    NA    6   NA  TRUE
#> 9     5   NA   NA  TRUE
#> 10   NA    7   NA  TRUE

或者,使用一点基础R代替整齐的eval:

rowwise

...这可能会快得多,因为迭代n会调用{{1}}而不是一个矢量化调用。

答案 1 :(得分:4)

解决这个问题有几个关键:

  • 访问字符向量中的字符串并将其与dplyr
  • 一起使用
  • 提供给mutate使用的函数的参数格式,此处为anyNA

此处的目标是复制此调用,但使用命名变量two_names而不是手动键入c(jack,jill)

stackdf %>% rowwise %>% mutate(test = anyNA(c(jack,jill)))

# A tibble: 10 x 4
    jack  jill  jane  test
   <dbl> <dbl> <dbl> <lgl>
 1     1     1     1 FALSE
 2    NA     2     2  TRUE
 3     2    NA     3  TRUE
 4    NA     3     4  TRUE
 5     3     4     5 FALSE
 6    NA    NA     6  TRUE
 7     4     5    NA FALSE
 8    NA     6    NA  TRUE
 9     5    NA    NA  TRUE
10    NA     7    NA  TRUE

<强> 1。使用动态变量和dplyr

  1. 使用quo / quos:不接受字符串作为输入。使用此方法的解决方案是:

    two_names2 <- quos(c(jack, jill))
    stackdf %>% rowwise %>% mutate(test = anyNA(!!! two_names2))
    

    请注意,quo只使用一个参数,因此使用!!取消引用,对于多个参数,您可以分别使用quos!!!。这是不可取的,因为我不使用two_names而是必须输入我想要使用的列。

  2. 使用as.namerlang::sym / rlang::symsas.namesym只需输入一次,但syms会占用> two_names [1] "jack" "jill" > as.name(two_names) jack > syms(two_names) [[1]] jack [[2]] jill 多个并返回符号对象的列表作为输出。

    as.name

    请注意syms忽略第一个元素后的所有内容。但是,mutate似乎在此处正常运行,因此现在我们需要在mutate调用中使用此功能。

  3. <强> 2。使用anyNA或其他变量

    syms中使用动态变量
    1. 直接使用anyNA> stackdf %>% rowwise %>% mutate(test = anyNA(!!! syms(two_names))) jack jill jane test <dbl> <dbl> <dbl> <lgl> 1 1 1 1 FALSE 2 NA 2 2 TRUE 3 2 NA 3 FALSE 4 NA 3 4 TRUE 5 3 4 5 FALSE 6 NA NA 6 TRUE 7 4 5 NA FALSE 8 NA 6 NA TRUE 9 5 NA NA FALSE 10 NA 7 NA TRUE 实际上并不会产生正确的结果。

      test

      检查sum表明这只考虑了第一个元素,而忽略了第二个元素。但是,如果我使用不同的函数,例如paste0> stackdf %>% rowwise %>% mutate(test = sum(!!! syms(two_names), na.rm = TRUE)) jack jill jane test <dbl> <dbl> <dbl> <dbl> 1 1 1 1 2 2 NA 2 2 2 3 2 NA 3 2 4 NA 3 4 3 5 3 4 5 7 6 NA NA 6 0 7 4 5 NA 9 8 NA 6 NA 6 9 5 NA NA 5 10 NA 7 NA 7 ,很明显两个元素都在使用:

      anyNA

      当您查看sumanyNA的参数时,原因就变得清晰了。

        

      function(x,recursive = FALSE).Primitive(&#34; anyNA&#34;)

           

      function(...,na.rm = FALSE).Primitive(&#34; sum&#34;)

      x需要单个对象sum,而(...)可以获取对象c()的变量列表。

    2. 只需提供> stackdf %>% rowwise %>% mutate(test = anyNA(c(!!! syms(two_names)))) jack jill jane test <dbl> <dbl> <dbl> <lgl> 1 1 1 1 FALSE 2 NA 2 2 TRUE 3 2 NA 3 TRUE 4 NA 3 4 TRUE 5 3 4 5 FALSE 6 NA NA 6 TRUE 7 4 5 NA FALSE 8 NA 6 NA TRUE 9 5 NA NA TRUE 10 NA 7 NA TRUE 即可解决此问题(请参阅alistaire的回答)。

      sapply
    3. 或者......出于教育目的,可以使用anyanyNAlist的组合来产生正确的结果。在这里,我们使用# this produces an error an error because the elements of !!! # are being passed to the arguments of sapply (X =, FUN = ) > stackdf %>% rowwise %>% mutate(test = any(sapply(!!! syms(two_names), anyNA))) Error in mutate_impl(.data, dots) : Evaluation error: object 'jill' of mode 'function' was not found. ,以便将结果作为单个列表对象提供。

      list

      提供# the below table is the familiar incorrect result that uses only the `jack` > stackdf %>% rowwise %>% mutate(test = any(sapply(X=as.list(!!! syms(two_names)), FUN=anyNA))) jack jill jane test <dbl> <dbl> <dbl> <lgl> 1 1 1 1 FALSE 2 NA 2 2 TRUE 3 2 NA 3 FALSE 4 NA 3 4 TRUE 5 3 4 5 FALSE 6 NA NA 6 TRUE 7 4 5 NA FALSE 8 NA 6 NA TRUE 9 5 NA NA FALSE 10 NA 7 NA TRUE # this produces the correct answer > stackdf %>% rowwise %>% mutate(test = any(X = sapply(list(!!! syms(two_names)), FUN = anyNA))) jack jill jane test <dbl> <dbl> <dbl> <lgl> 1 1 1 1 FALSE 2 NA 2 2 TRUE 3 2 NA 3 TRUE 4 NA 3 4 TRUE 5 3 4 5 FALSE 6 NA NA 6 TRUE 7 4 5 NA FALSE 8 NA 6 NA TRUE 9 5 NA NA TRUE 10 NA 7 NA TRUE 修复此问题,因为它将所有结果绑定到单个对象中。

      > as.list(two_names)
      [[1]]
      [1] "jack"
      
      [[2]]
      [1] "jill"
      
      > list(two_names)
      [[1]]
      [1] "jack" "jill"
      

      了解这两种行为的不同之处在比较他们的行为时才有意义!

      Bitmap bitmap = Picasso.with(this)
                              .load(productCoverImageURL)
                              .get();