Obtaining character version of a quasiquoted parameter

Within a function I want to use a parameter passed by quasiquotation in a select statement, and in the same function I want to use the character equivalent in a names_to clause of pivot_longer(). My function may be some arbitrary depth in the call stack from the original call where the real name of the variable is used. The function call deparse(substitute()) will work if the call is just one deep, but as the example shows the method does not work if the function call is two deep.
Back when I was using gather() I could just use the quasiquotation parameter in both places, but pivot_longer doesn't like that method. According to the tidyverse documentation, pivot_longer() is easier to use than gather, so there ought to be a way to get the needed character string

I think you might have forgotten to include the example. :flushed: (That, or I'm not sure which example you're trying to reference—either way, a reprex would help).

Aside: You can absolutely continue to use gather(). Saying something is "easier" is a statement of our beliefs about a population — on average, we believe most people will find pivot_longer() easier to use and remember than gather() . That doesn't imply that everyone will find them easier, particularly if you've already spend a lot of time learning spread() and gather(). We don't have plans to kill the pivot_*() predecessors off. :slightly_smiling_face: You can learn more about the "superseded" lifecycle status in this vignette.

Have you tried rlang::as_name(parameter_name)?

Do you have an example of code that you used to use and would like to update?

I have a very simple reprex, but I don't see how to include it in my post. The instructions for creating a reprex are very clear, but then they just leave one hanging. I guess everyone has figured this step out once, and then things are simple. But I am not yet a member of the "everyone" group. Would someone please let me know how to include a reprex in my post. Then I will reply to all the helpful folks who have made suggestions.

In essence, at the end, you will have markdown on your clipboard that you can paste right into the dialogue box here on the community site.

I think @andresrcs's explanation covers it well:

Thanks to everyone who has responded. I am now attaching a reprex, which shows failure of various things, including the old deparse(substitute()) trick, as well as rlang::as_name(). The code also contains a hack, which I have now included in the code I really care about. I actually used this hack before I learned about deparse(substitute()). I am not fond of this hack, but I’ll stick with it until something better comes around.

require(tidyverse)
#> Loading required package: tidyverse


smalldf <- function(dff, col1, col2, col3){
  newdff <- dff %>% select({{ col1 }}, {{ col2 }}, {{ col3 }}) # selects fine, as anticipated
  col1value <- deparse(substitute(col1)) # no error, but doesn't give what I want
  col2value <- deparse(substitute(col2)) # no error, but doesn't give what I want
  yacol2value <- NULL;  try(yacol2value <- rlang::as_name(col2)) # error captured by try()
  newerdff <- NULL
  try(newerdff <- newdff %>% 
        pivot_longer(-Model, names_to = rlang::as_name(col3), values_to = "value")) # error captured by try()
  newname <- names(newdff)[3]
  thishackworks <- newdff %>%
    pivot_longer(-{{ col1 }}, names_to = newname, values_to = "value")
  list(newdff = head(newdff), newerdff = head(newerdff), col1value = col1value, col2value = col2value, 
       yacol2value = yacol2value, newname = newname, thishackworks = head(thishackworks))
  }

twotimer <- function(df, col1, col2, col3){
  col2 <- enquo(col2)
  col3value <- deparse(substitute(col3)) # works at this level
iterations <- df %>% smalldf({{ col1 }}, !!col2, {{ col3 }})
 list(iterations = iterations, col3value = col3value)
}
  
mtcars %>% head() %>% 
  as_tibble(rownames = "Model") %>% 
              twotimer(Model, cyl, carb) 
#> Error : Quosures can only be unquoted within a quasiquotation context.
#> 
#>   # Bad:
#>   list(!!myquosure)
#> 
#>   # Good:
#>   dplyr::mutate(data, !!myquosure)
#> Error in is_quosure(x) : object 'carb' not found
#> $iterations
#> $iterations$newdff
#> # A tibble: 6 x 3
#>   Model               cyl  carb
#>   <chr>             <dbl> <dbl>
#> 1 Mazda RX4             6     4
#> 2 Mazda RX4 Wag         6     4
#> 3 Datsun 710            4     1
#> 4 Hornet 4 Drive        6     1
#> 5 Hornet Sportabout     8     2
#> 6 Valiant               6     1
#> 
#> $iterations$newerdff
#> NULL
#> 
#> $iterations$col1value
#> [1] "{"            "    {"        "        col1" "    }"        "}"           
#> 
#> $iterations$col2value
#> [1] "!!col2"
#> 
#> $iterations$yacol2value
#> NULL
#> 
#> $iterations$newname
#> [1] "carb"
#> 
#> $iterations$thishackworks
#> # A tibble: 6 x 3
#>   Model         carb  value
#>   <chr>         <chr> <dbl>
#> 1 Mazda RX4     cyl       6
#> 2 Mazda RX4     carb      4
#> 3 Mazda RX4 Wag cyl       6
#> 4 Mazda RX4 Wag carb      4
#> 5 Datsun 710    cyl       4
#> 6 Datsun 710    carb      1
#> 
#> 
#> $col3value
#> [1] "carb"

Created on 2020-03-19 by the reprex package (v0.3.0)

Not sure if this is along the lines that you were hoping for, but I translated your code into a version that I understand better, and thought I'd see if it helps:

library(tidyverse)
library(rlang)
#> 
#> Attaching package: 'rlang'
#> The following objects are masked from 'package:purrr':
#> 
#>     %@%, as_function, flatten, flatten_chr, flatten_dbl,
#>     flatten_int, flatten_lgl, flatten_raw, invoke, list_along,
#>     modify, prepend, splice

twotimer2 <- function(df, col1, col2, col3){
  col1 <- enexpr(col1)
  col2 <- enexpr(col2)
  col3 <- enexpr(col3)
  sm_df_call <- expr(df %>% smalldf2(!!col1, !!col2, !!col3))
  iterations <- eval(sm_df_call)
  list(iterations = iterations, col3value = as_name(col3))
}

smalldf2 <- function(dff, col1, col2, col3){
  col1 <- enexpr(col1)
  col2 <- enexpr(col2)
  col3 <- enexpr(col3)
  newdff_call <- expr(dff %>% select(!!col1, !!col2, !!col3))
  newdff <- eval(newdff_call)
  yacol2value <- NULL;  try(yacol2value <- rlang::as_name(col2)) # error captured by try()
  newerdff <- NULL
  try(newerdff <- newdff %>% 
        pivot_longer(-Model, names_to = rlang::as_name(col3), values_to = "value")) # error captured by try()
  newname <- names(newdff)[3]
  thishackworks <- newdff %>%
    pivot_longer(-!!col1, names_to = newname, values_to = "value")
  list(newdff = head(newdff), newerdff = head(newerdff),  col1value = as_name(col1), col2value = as_name(col2), 
       yacol2value = yacol2value, newname = newname, thishackworks = head(thishackworks))
}

mtcars %>% head() %>% 
  as_tibble(rownames = "Model") %>% 
  twotimer2(Model, cyl, carb) 
#> $iterations
#> $iterations$newdff
#> # A tibble: 6 x 3
#>   Model               cyl  carb
#>   <chr>             <dbl> <dbl>
#> 1 Mazda RX4             6     4
#> 2 Mazda RX4 Wag         6     4
#> 3 Datsun 710            4     1
#> 4 Hornet 4 Drive        6     1
#> 5 Hornet Sportabout     8     2
#> 6 Valiant               6     1
#> 
#> $iterations$newerdff
#> # A tibble: 6 x 3
#>   Model         carb  value
#>   <chr>         <chr> <dbl>
#> 1 Mazda RX4     cyl       6
#> 2 Mazda RX4     carb      4
#> 3 Mazda RX4 Wag cyl       6
#> 4 Mazda RX4 Wag carb      4
#> 5 Datsun 710    cyl       4
#> 6 Datsun 710    carb      1
#> 
#> $iterations$col1value
#> [1] "Model"
#> 
#> $iterations$col2value
#> [1] "cyl"
#> 
#> $iterations$yacol2value
#> [1] "cyl"
#> 
#> $iterations$newname
#> [1] "carb"
#> 
#> $iterations$thishackworks
#> # A tibble: 6 x 3
#>   Model         carb  value
#>   <chr>         <chr> <dbl>
#> 1 Mazda RX4     cyl       6
#> 2 Mazda RX4     carb      4
#> 3 Mazda RX4 Wag cyl       6
#> 4 Mazda RX4 Wag carb      4
#> 5 Datsun 710    cyl       4
#> 6 Datsun 710    carb      1
#> 
#> 
#> $col3value
#> [1] "carb"

Created on 2020-03-19 by the reprex package (v0.3.0)

1 Like

Thanks. I ran your code on my machine, and it works. I had never used the expr(), eval(), and enexpr() functions in such a context. Now I have to figure out why it works, but I guess that's up to me.

In case it helps, I learned it from here, in chapter 20, section 6, subsection 2.1, 'Wrapping modelling functions'.

Thanks again. I have 40+ years as a SAS user, and am now trying to learn as much as I can about R. And there is a lot to learn.

This topic was automatically closed 21 days after the last reply. New replies are no longer allowed.