Using `mutate` to create column copies using a named vector

Viewed 199

I have a tibble and a named vector. I would like to make copies of all of the columns in my named vector using the vector names while preserving the original names.

I know how to rename all the columns fairly simply:

library(dplyr)

named_vector <- 
  c("var1" = "x1",
    "var2" = "x2",
    "var3" = "x3")

tibble(x1 = 1:3, x2 = 1:3, x3 = 1:3, z = 68, zz = 69) %>% 
  rename(!!!named_vector)
#> # A tibble: 3 x 5
#>    var1  var2  var3     z    zz
#>   <int> <int> <int> <dbl> <dbl>
#> 1     1     1     1    68    69
#> 2     2     2     2    68    69
#> 3     3     3     3    68    69

Created on 2021-08-27 by the reprex package (v0.3.0)

But I don't know how to do the equivalent using mutate. How can I make copies of the columns in a way that preserves the original names and but also has the vector names?

My expected output would be the equivalent of:

library(dplyr)

named_vector <- 
  c("var1" = "x1",
    "var2" = "x2",
    "var3" = "x3")

tibble(x1 = 1:3, x2 = 1:3, x3 = 1:3, z = 68, zz = 69) %>% 
  mutate(var1 = x1,
         var2 = x2,
         var3 = x3)
#> # A tibble: 3 x 8
#>      x1    x2    x3     z    zz  var1  var2  var3
#>   <int> <int> <int> <dbl> <dbl> <int> <int> <int>
#> 1     1     1     1    68    69     1     1     1
#> 2     2     2     2    68    69     2     2     2
#> 3     3     3     3    68    69     3     3     3

Created on 2021-08-27 by the reprex package (v0.3.0)

5 Answers

We may use across with mutate, and rename with str_replace by replacing the substring 'x' with 'var' to create new columns

library(dplyr)
library(stringr)
tibble(x1 = 1:3, x2 = 1:3, x3 = 1:3) %>%
    mutate(across(everything(), 
       .names = "{str_replace(.col, 'x', 'var')}"))

-output

# A tibble: 3 x 6
     x1    x2    x3  var1  var2  var3
  <int> <int> <int> <int> <int> <int>
1     1     1     1     1     1     1
2     2     2     2     2     2     2
3     3     3     3     3     3     3

Or use match to named_vector in .names

tibble(x1 = 1:3, x2 = 1:3, x3 = 1:3) %>% 
    mutate(across(all_of(unname(named_vector)),
      .names = "{names(named_vector)[match(.col, named_vector)]}"))

-output

# A tibble: 3 x 6
     x1    x2    x3  var1  var2  var3
  <int> <int> <int> <int> <int> <int>
1     1     1     1     1     1     1
2     2     2     2     2     2     2
3     3     3     3     3     3     3

With the updated post also the solution works

tibble(x1 = 1:3, x2 = 1:3, x3 = 1:3, z = 68, zz = 69)  %>%  
    mutate(across(all_of(unname(named_vector)),
       .names = "{names(named_vector)[match(.col, named_vector)]}"))
# A tibble: 3 x 8
     x1    x2    x3     z    zz  var1  var2  var3
  <int> <int> <int> <dbl> <dbl> <int> <int> <int>
1     1     1     1    68    69     1     1     1
2     2     2     2    68    69     2     2     2
3     3     3     3    68    69     3     3     3

You can create new columns from existing columns using -

data <- tibble::tibble(x1 = 1:3, x2 = 1:3, x3 = 1:3, z = 68, zz = 69)
data[names(named_vector)] <- data[named_vector]
data

#     x1    x2    x3     z    zz  var1  var2  var3
#  <int> <int> <int> <dbl> <dbl> <int> <int> <int>
#1     1     1     1    68    69     1     1     1
#2     2     2     2    68    69     2     2     2
#3     3     3     3    68    69     3     3     3

The new variable names will be taken from the named vector just by doing:

library(dplyr)

tibble(x1 = 1:3, x2 = 4:6, x3 = 7:9, z = 68, zz = 69) %>% 
   mutate(across(all_of(named_vector)))

# A tibble: 3 x 8
     x1    x2    x3     z    zz  var1  var2  var3
  <int> <int> <int> <dbl> <dbl> <int> <int> <int>
1     1     1     1    68    69     1     1     1
2     2     2     2    68    69     2     2     2
3     3     3     3    68    69     3     3     3

Another option is to use rename_with from dplyr package after cbind

library(dplyr)
df %>% 
    cbind(df[,1:3]) %>% 
    rename_with(.cols = 6:8, ~ names(named_vector), .name_repair = c("minimal"))

output:

 x1 x2 x3  z zz var1 var2 var3
1  1  1  1 68 69    1    1    1
2  2  2  2 68 69    2    2    2
3  3  3  3 68 69    3    3    3

Although there are already a lot of answers, there is one more important way to do this kind of programming with {dplyr}. mutate can evaluate list of expressions. So instead of providing a named_vector we can create a named list of expressions exp_ls.

library(dplyr)
exp_ls <- list("var1" = expr(x1),
               "var2" = expr(x2),
               "var3" = expr(x3))

tibble(x1 = 1:3, x2 = 1:3, x3 = 1:3, z = 68, zz = 69) %>% 
  mutate(!!! exp_ls)

#> # A tibble: 3 x 8
#>      x1    x2    x3     z    zz  var1  var2  var3
#>   <int> <int> <int> <dbl> <dbl> <int> <int> <int>
#> 1     1     1     1    68    69     1     1     1
#> 2     2     2     2    68    69     2     2     2
#> 3     3     3     3    68    69     3     3     3

We can also easily transform the given named_vector to a list of expressions using syms(named_vector):*

named_vector <- 
  c("var1" = "x1",
    "var2" = "x2",
    "var3" = "x3")

tibble(x1 = 1:3, x2 = 1:3, x3 = 1:3, z = 68, zz = 69) %>% 
  mutate(!!! syms(named_vector)

#> # A tibble: 3 x 8
#>      x1    x2    x3     z    zz  var1  var2  var3
#>   <int> <int> <int> <dbl> <dbl> <int> <int> <int>
#> 1     1     1     1    68    69     1     1     1
#> 2     2     2     2    68    69     2     2     2
#> 3     3     3     3    68    69     3     3     3

Created on 2021-08-28 by the reprex package (v0.3.0)
* Thanks to @27 ϕ 9 who suggested syms instead of sapply(named_vector, str2lang).

Related