Assign multiple columns from lapply() using dplyr and tibble

763 views Asked by At

I have a case in which I'd like to enhance the code to be more straight, assigning multiple elements of lapply's results as columns for tibble.

I know how do this for data.table, the operation would be like this:

data <- data.table(x = 1:9)
data[,  c("y","z") := lapply(2:3, function(p) x ^ p)]

#   x  y   z
#1: 1  1   1
#2: 2  4   8
#3: 3  9  27
#4: 4 16  64
#5: 5 25 125

Now, I'd like to do the same with dplyr. I have already tried alternatives, for example:

data <- tibble(x = 1:9)
data <- data %>% 
            mutate(c("y","z") = lapply(2:3, function(p) x ^ p))

but it goes wrong.

3

There are 3 answers

0
akrun On

We can use the tidyverse option

library(tidyverse)
map2(2:3, c("y", "z"), ~set_names(data_frame(data$x^.x), .y)) %>%
            bind_cols(data, .)   
#   x  y   z
#1: 1  1   1
#2: 2  4   8
#3: 3  9  27
#4: 4 16  64
#5: 5 25 125
#6: 6 36 216
#7: 7 49 343
#8: 8 64 512
#9: 9 81 729

Or we can remove the data_frame call

data %>%
     map2(2:3, ~ .^.y) %>%
     set_names(., c("y", "z")) %>%
     bind_cols(data, .)
0
user63230 On

Another option following using lapply:

library(dplyr)
df <- data.frame(x = 1:9)
df[c("y","z")] <- lapply(2:3, function(p) 
  df %>% 
    mutate(new = x^p) %>% 
    pull(new))
#   x  y   z
# 1 1  1   1
# 2 2  4   8
# 3 3  9  27
# 4 4 16  64
# 5 5 25 125
# 6 6 36 216
# 7 7 49 343
# 8 8 64 512
# 9 9 81 729
0
LMc On

Here is another alternative using only dplyr:

library(dplyr)

data %>%
  mutate(!!!setNames(2:3, c("y", "z")),
         across(y:z, ~ x ^ .)) 

Output

      x     y     z
  <int> <dbl> <dbl>
1     1     1     1
2     2     4     8
3     3     9    27
4     4    16    64
5     5    25   125
6     6    36   216
7     7    49   343
8     8    64   512
9     9    81   729

How it works

From ?rlang::`nse-force` :

The big-bang operator !!! forces-splice a list of objects. The elements of the list are spliced in place, meaning that they each become one single argument.

To show you what the expression evaluates to use rlang::qq_show:

rlang::qq_show(mutate(!!!setNames(2:3, c("y", "z"))))

mutate(y = 2L, z = 3L)

Here you can see the names because the variable names and the elements become the variable values.

Then across iterates the function x ^ . over the specified variable range. The . is substituted with the current column iteration, y and then z in this case.