Search code examples
rtidyverseacross

How to dynamically create columns with tidy evaluation?


I'd like to create variables using a specific pattern. I need to include the variable name as a tidy evaluation in the mutate function. I made a custom example below:

  iris %>%
  rename(Sepal = Sepal.Width, Petal = Petal.Width) %>%
  mutate_at(c('Sepal', 'Petal'), list(test = ~ . / !!sym(paste0(., '.Length'))))

Solution

  • In the newer version of dplyr, we can use across

    library(dplyr)
    library(stringr)
    iris %>%
       rename(Sepal = Sepal.Width, Petal = Petal.Width) %>%
       mutate(across(c('Sepal', 'Petal'), ~ 
                  ./get(str_c(cur_column(), '.Length')), .names = '{.col}_test'))
    

    -output

    #    Sepal.Length Sepal Petal.Length Petal    Species Sepal_test Petal_test
    #1            5.1   3.5          1.4   0.2     setosa  0.6862745 0.14285714
    #2            4.9   3.0          1.4   0.2     setosa  0.6122449 0.14285714
    #3            4.7   3.2          1.3   0.2     setosa  0.6808511 0.15384615
    #4            4.6   3.1          1.5   0.2     setosa  0.6739130 0.13333333
    #5            5.0   3.6          1.4   0.2     setosa  0.7200000 0.14285714
    #6            5.4   3.9          1.7   0.4     setosa  0.7222222 0.23529412
    #7            4.6   3.4          1.4   0.3     setosa  0.7391304 0.21428571
    #8            5.0   3.4          1.5   0.2     setosa  0.6800000 0.13333333
    
    # ...
    

    We don't need to rename just for doing the division. It can also be achieved by keeping the original column names

    iris %>%
      mutate(across(ends_with('Width'), ~
                  ./get(str_replace(cur_column(), 'Width', 'Length')),
             .names = '{.col}_test'))
    

    The . returns the value and not the column name. So, paste0(., '.Length') would be pasting substring .Length with the corresponding column values