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, .)