Search code examples
rdplyrpiping

piping dplyr mutate with unknown variable name


I'm trying to use mutate from dplyr with dynamic variables names. I've found a couple of post on SO (here, here and here) which got me closer but not to a workable solution. I don't think much is missing, but I require your help for it.

Here is a reproducible example very similar to my problem. I have tables that have 2 fields, one of them called either AD or any other name. This field as to be a factor, but could be character or integer. My function need to convert to factor.

library(dplyr)

t1 <- data.frame(f1 = 1:4, AD = 1:4)
t2 <- data.frame(f1 = 1:4, FC = 1:4)

ff <- function(tt){

  # find the variable name
  if(any(colnames(tt)=="AD")){
    vv <- quo(AD)
  } else {
    vv <- colnames(tt) %>% .[.!="f1"]
    vv <- enquo(vv)
  }

  # make the mutate
  tt %>% mutate(!!quo_name(vv) := as.factor(!!vv))      
}

With the help of the link cited earlier, I manage to make the function work for table that have AD (using the quo, !! and := which were function I didn't know about before).

ff(tt=t1) %>% str
'data.frame':   4 obs. of  2 variables:
 $ f1: int  1 2 3 4
 $ AD: Factor w/ 4 levels "1","2","3","4": 1 2 3 4

This works well. but when I send a table with an unknown variable name:

ff(tt=t2) %>% str
'data.frame':   4 obs. of  2 variables:
 $ f1: int  1 2 3 4
 $ FC: Factor w/ 1 level "FC": 1 1 1 1

My FC is now wrong with only 1 factor being FC

I think the problem is in the way I set vv in the second option which give me the wrong env value:

quo(AD)
<quosure>
  expr: ^AD
  env:  global


vv <- colnames(tt) %>% .[.!="f1"]
enquo(vv)
<quosure>
  expr: ^"FC"
  env:  empty

Any idea how to fix my problem? I open to base R solution, however it as to be able to fit in a long piping procedure.


Solution

  • You don't need enquo there. That's for turning a value passed as a parameter into a quosure. Instead you need to turn a string into a symbol. For that you can use as.name() or rlang::sym()

    ff <- function(tt){
    
      # find the variable name
      if(any(colnames(tt)=="AD")){
        vv <- quo(AD)
      } else {
        vv <- colnames(tt) %>% .[.!="f1"]
        vv <- as.name(vv)
      }
    
      # make the mutate
      tt %>% mutate(!!quo_name(vv) := as.factor(!!vv))      
    }