Return variable name as string with dplyr in wrapper function

Viewed 62

I think this is hopefully a reasonably straightforward problem. I am creating a custom function that perform a battery of summary statistics and formats the data consistently for some reports. To accomplish this I write a function() that take 3 variables as input, a dataset, a grouping variable, and a response variable of interest. I perform the summary statistics using dplyr::summarize(). I know that to use dplyr::summarize in a custom function, I have to embrace the grouping variable and the response variable with curly curly notation within the dplyr:: functions. I want to record the name of the response variable in the output tibble. In a non-tidyverse:: world I would use deparse(substitute()) to accomplish this. However, this method apparently does not work within the tidyverse::. Here is my reproducible example. I will walk through it piecemeal, and then post the uninterrupted code at the end of my question.

For my first attempt, I tried the deparse(substitute({{}})) approach

library(tidyverse)
data("iris")

fxn1<-function(DF, grp, var){
  out<-DF %>% 
    group_by({{grp}}) %>% 
    summarize(Mean_Val=mean({{var}}, na.rm=TRUE),
              Var=deparse(substitute({{var}})))
}

Demo1<-fxn1(iris, Species, Petal.Width)
Demo1

Unfortunately this created some sort of expression in the Var column, and messes up the summarization.

# A tibble: 12 x 3
# Groups:   Species [3]
   Species    Mean_Val Var                                                         
   <fct>         <dbl> <chr>                                                       
 1 setosa        0.246 "(function (...) "                                          
 2 setosa        0.246 "{"                                                         
 3 setosa        0.246 "    .External2(ffi_tilde_eval, sys.call(), environment(), ~
 4 setosa        0.246 "})(Petal.Width)"                                           
 5 versicolor    1.33  "(function (...) "                                          
 6 versicolor    1.33  "{"                                                         
 7 versicolor    1.33  "    .External2(ffi_tilde_eval, sys.call(), environment(), ~
 8 versicolor    1.33  "})(Petal.Width)"                                           
 9 virginica     2.03  "(function (...) "                                          
10 virginica     2.03  "{"                                                         
11 virginica     2.03  "    .External2(ffi_tilde_eval, sys.call(), environment(), ~
12 virginica     2.03  "})(Petal.Width)"   

For my second attempt, I got rid of the curly curly notation in deparse(substitute())

fxn2<-function(DF, grp, var){
  out<-DF %>% 
    group_by({{grp}}) %>% 
    summarize(Mean_Val=mean({{var}}, na.rm=TRUE),
              Var=deparse(substitute(var)))
}

Demo2<-fxn2(iris, Species, Petal.Width)
Demo2

This almost correct, but instead of inputing "Petal.Width" into the Var column, it adds "var".

# A tibble: 3 x 3
  Species    Mean_Val Var  
  <fct>         <dbl> <chr>
1 setosa        0.246 var  
2 versicolor    1.33  var  
3 virginica     2.03  var  

What I want my data to look like is this

Desired<-iris %>% group_by(Species) %>%  summarize(Mean_Val=mean(Petal.Width), Var="Petal.Width")
Desired

Which looks like this:

# A tibble: 3 x 3
  Species    Mean_Val Var        
  <fct>         <dbl> <chr>      
1 setosa        0.246 Petal.Width
2 versicolor    1.33  Petal.Width
3 virginica     2.03  Petal.Width

Does anyone know how to get dplyr:: to do the equivalent of deparse(substitute), but actually return the name of the variable, and not the argument name? Any guidance would be much appreciated.

Here is the uninterrupted reproducible example:

library(tidyverse)
data("iris")

fxn1<-function(DF, grp, var){
  out<-DF %>% 
    group_by({{grp}}) %>% 
    summarize(Mean_Val=mean({{var}}, na.rm=TRUE),
              Var=deparse(substitute({{var}})))
}

Demo1<-fxn1(iris, Species, Petal.Width)
Demo1

fxn2<-function(DF, grp, var){
  out<-DF %>% 
    group_by({{grp}}) %>% 
    summarize(Mean_Val=mean({{var}}, na.rm=TRUE),
              Var=deparse(substitute(var)))
}

Demo2<-fxn2(iris, Species, Petal.Width)
Demo2

Desired<-iris %>% group_by(Species) %>%  summarize(Mean_Val=mean(Petal.Width), Var="Petal.Width")
Desired
1 Answers

This kind of provides your expected output ("Var" is a list, so not ideal); does it solve your problem?

library(tidyverse)
data("iris")

fxn1<-function(DF, grp, var){
  out<-DF %>% 
    group_by({{grp}}) %>% 
    summarize(Mean_Val=mean({{var}}, na.rm=TRUE),
              Var=deparse(substitute({{var}})))
}

Demo1<-fxn1(iris, Species, Petal.Width)
#> `summarise()` has grouped output by 'Species'. You can override using the
#> `.groups` argument.
Demo1
#> # A tibble: 12 × 3
#> # Groups:   Species [3]
#>    Species    Mean_Val Var                                                      
#>    <fct>         <dbl> <chr>                                                    
#>  1 setosa        0.246 "(function (...) "                                       
#>  2 setosa        0.246 "{"                                                      
#>  3 setosa        0.246 "    .External2(ffi_tilde_eval, sys.call(), environment(…
#>  4 setosa        0.246 "})(Petal.Width)"                                        
#>  5 versicolor    1.33  "(function (...) "                                       
#>  6 versicolor    1.33  "{"                                                      
#>  7 versicolor    1.33  "    .External2(ffi_tilde_eval, sys.call(), environment(…
#>  8 versicolor    1.33  "})(Petal.Width)"                                        
#>  9 virginica     2.03  "(function (...) "                                       
#> 10 virginica     2.03  "{"                                                      
#> 11 virginica     2.03  "    .External2(ffi_tilde_eval, sys.call(), environment(…
#> 12 virginica     2.03  "})(Petal.Width)"

fxn2<-function(DF, grp, var){
  out<-DF %>% 
    group_by({{grp}}) %>% 
    summarize(Mean_Val=mean({{var}}, na.rm=TRUE),
              Var=deparse(substitute(var)))
}

Demo2<-fxn2(iris, Species, Petal.Width)
Demo2
#> # A tibble: 3 × 3
#>   Species    Mean_Val Var  
#>   <fct>         <dbl> <chr>
#> 1 setosa        0.246 var  
#> 2 versicolor    1.33  var  
#> 3 virginica     2.03  var

Desired<-iris %>% group_by(Species) %>%  summarize(Mean_Val=mean(Petal.Width), Var="Petal.Width")
Desired
#> # A tibble: 3 × 3
#>   Species    Mean_Val Var        
#>   <fct>         <dbl> <chr>      
#> 1 setosa        0.246 Petal.Width
#> 2 versicolor    1.33  Petal.Width
#> 3 virginica     2.03  Petal.Width

fxn3 <- function(DF, grp, var){
  DF %>% 
    group_by({{grp}}) %>% 
    summarize(Mean_Val=mean({{var}}, na.rm=TRUE),
              Var=c(ensym(var)))
}

Demo3 <- fxn3(iris, Species, Petal.Width)
Demo3
#> # A tibble: 3 × 3
#>   Species    Mean_Val Var   
#>   <fct>         <dbl> <list>
#> 1 setosa        0.246 <sym> 
#> 2 versicolor    1.33  <sym> 
#> 3 virginica     2.03  <sym>

print.data.frame(Demo3)
#>      Species Mean_Val         Var
#> 1     setosa    0.246 Petal.Width
#> 2 versicolor    1.326 Petal.Width
#> 3  virginica    2.026 Petal.Width

Created on 2022-04-21 by the reprex package (v2.0.1)

Related