Non-standard eval in dplyr::mutate

2019-05-11 06:26发布

问题:

In theory this should work, as I've read the tidyverse guide on NSE, but it throws me an error as seen in the bottom of this example. Why is this? I understand how to do a simple quasiquotation of an object, but I do not understand how to evaluate a fraction of two quasiquoted objects. Can anyone help with this?

    tmp <- structure(list(qa11a = structure(c(1616, 7293, 1528, 1219, 2049, 286),
                                            label = "Total voters removed from Nov. 2008 to Nov. 2010",
                                            class = c("labelled","numeric")),
                          state_abbv = c("AL", "AL", "AL", "AL", "AL", "AL"),
                          fipscode = c("0100100000", "0100300000", "0100500000",
                                       "0100700000", "0100900000", "0101100000"),
                          qa1a = structure(c(34727, 114952, 16450, 12239, 31874, 7650),
                                           label = "Total registered & eligible to vote November 2010",
                                           class = c("labelled", "numeric")),
                          reg.pct = structure(c(0.0465343968669911, 0.0634438722249287,
                                                0.092887537993921, 0.0995996404935044,
                                                0.0642843697057163, 0.0373856209150327),
                                              label = "Total voters removed from Nov. 2008 to Nov. 2010",
                                              class = c("labelled", "numeric")),
                          precleared = c(1, 1, 1, 1, 1, 1),
                          year = c(2010, 2010, 2010, 2010, 2010, 2010),
                          shelby = c(0, 0, 0, 0, 0, 0), 
                          pres = c(FALSE, FALSE, FALSE, FALSE, FALSE, FALSE)),
                      .Names = c("qa11a", "state_abbv", "fipscode", "qa1a",
                                 "reg.pct", "precleared", "year", "shelby", "pres"),
                      row.names = c(NA, -6L),
                      class = c("tbl_df", "tbl", "data.frame"))

    require(tidyverse)
    require(rlang)

    upper="qa11a"
    lower="qa1a"
    pct.name="rej.reg"

    pct.reg <- quo_name(enquo(pct.name))
    upper <- enquo(upper)
    lower <- enquo(lower)

    h <- tmp %>%
        mutate(!! pct.reg := (!!upper)/(!!lower))

    #> Error in mutate_impl(.data, dots): Evaluation error: non-numeric argument to binary operator.

回答1:

We can convert the string to symbol and then evaluate

tmp %>% 
   mutate(!! pct.name := (!! sym(upper)/(!! sym(lower))))
# A tibble: 6 x 10
#  qa11a state_abbv fipscode     qa1a reg.pct precleared  year shelby pres  rej.reg
#  <dbl> <chr>      <chr>       <dbl>   <dbl>      <dbl> <dbl>  <dbl> <lgl>   <dbl>
#1  1616 AL         0100100000  34727  0.0465       1.00  2010      0 F      0.0465
#2  7293 AL         0100300000 114952  0.0634       1.00  2010      0 F      0.0634
#3  1528 AL         0100500000  16450  0.0929       1.00  2010      0 F      0.0929
#4  1219 AL         0100700000  12239  0.0996       1.00  2010      0 F      0.0996
#5  2049 AL         0100900000  31874  0.0643       1.00  2010      0 F      0.0643
#6   286 AL         0101100000   7650  0.0374       1.00  2010      0 F      0.0374

when we apply enquo on a string, it is converting to a quosure with quotes

enquo(upper)
# <quosure>
#  expr: ^"qa11a"
#  env:  empty

Instead of converting from a string, it could be easier to do

upper <- quo(qalla)
lower <- quo(qala)

In the OP's code, calling enquo i.e. converting to quosure on a string object will result in string quosure and that is not intended

upper <- "qa11a"
lower <- "qa1a"
enquo(upper)
#<quosure>
#  expr: ^"qa11a"
#  env:  empty

We can compare it to

upper <- quo(qa11a)
lower <- quo(qa1a)
upper
# <quosure>
#  expr: ^qalla
#  env:  global

and executing it

tmp %>% 
     mutate(!! pct.name := (!! upper)/ (!! lower))
# A tibble: 6 x 10
#  qa11a state_abbv fipscode     qa1a reg.pct precleared  year shelby pres  rej.reg
#  <dbl> <chr>      <chr>       <dbl>   <dbl>      <dbl> <dbl>  <dbl> <lgl>   <dbl>
#1  1616 AL         0100100000  34727  0.0465       1.00  2010      0 F      0.0465
#2  7293 AL         0100300000 114952  0.0634       1.00  2010      0 F      0.0634
#3  1528 AL         0100500000  16450  0.0929       1.00  2010      0 F      0.0929
#4  1219 AL         0100700000  12239  0.0996       1.00  2010      0 F      0.0996
#5  2049 AL         0100900000  31874  0.0643       1.00  2010      0 F      0.0643
#6   286 AL         0101100000   7650  0.0374       1.00  2010      0 F      0.0374