Home > OS >  Generate multiple columns at once using mutate in R
Generate multiple columns at once using mutate in R

Time:11-27

Context

Say that I have a df which includes multiple columns (a1,a2,a3,a4,b1,b2,b3,b4).

I want to generate some new columns (c1,c2,c3,c4) base on the existed columns .

Now, I can do this by creating it one by one.


df = data.frame(a1 = 1:2, a2 = 3:4, a3 = 5:6, a4 = 7:8,
                b1 = 1:2, b2 = 3:4, b3 = 5:6, b4 = 7:8)

df %>% 
  mutate(c1 = a1 - b1,
         c2 = a2 - b2,
         c3 = a3 - b3,
         c4 = a4 - b4)

Question

Is there a way that can produce c1, c2, c3, and c4 all at once? Maybe using across()?

Reproducible code

df = data.frame(a1 = 1:2, a2 = 3:4, a3 = 5:6, a4 = 7:8,
                b1 = 1:2, b2 = 3:4, b3 = 5:6, b4 = 7:8)

df %>% 
  mutate(c1 = a1 - b1,
         c2 = a2 - b2,
         c3 = a3 - b3,
         c4 = a4 - b4)

# Maybe the way like this, though it cannot run correctly
df %>% 
  mutate(paste('c', 1:4) = paste('a', 1:4) - paste('b', 1:4))

CodePudding user response:

A base R option would be

df[paste0("c", 1:4)] <- df[1:4]- df[5:8]

Or in dplyr, we could use two across

library(dplyr)
library(stringr)
df <- df %>% 
  mutate(across(starts_with('a'), 
     .names = "{str_replace(.col, 'a', 'c')}") - across(starts_with('b')))

CodePudding user response:

Here a way to do it with across

df <-
  data.frame(
    a1 = 1:2, a2 = 3:4, a3 = 5:6, a4 = 7:8,
    b1 = 1:2, b2 = 1:2, b3 = 5:6, b4 = 1:2
    )

library(stringr)
library(dplyr)

df %>% 
  mutate(across(
    .cols = starts_with('a'),
    .fns = ~(. - get(str_replace(cur_column(), 'a', 'b'))),
    .names = "{str_replace(.col, 'a', 'c')}")
  )

  a1 a2 a3 a4 b1 b2 b3 b4 c1 c2 c3 c4
1  1  3  5  7  1  1  5  1  0  2  0  6
2  2  4  6  8  2  2  6  2  0  2  0  6

CodePudding user response:

Using purrr::map:

map_df(setNames(1:4, paste0("c", 1:4)),
       ~ df[[paste0("a", .x)]] - df[[paste0("b", .x)]]) %>% 
  cbind(df, .)

Another option:

map2_df(select(df, matches("a")),
        select(df, matches("b")),
        ~ .x - .y) %>%
  setNames(paste0("c", 1:4)) %>%
  cbind(df, .)
  • Related