I have a df
where I want to recalculate some columns based on the value of that columns colnames
:
library(dplyr)
df <- data.frame(year = 1:3, "10" = 0:2, "20" = 3:5)
colnames(df)[2:3] <- c("10", "20")
df
year 10 20
1 1 0 3
2 2 1 4
3 3 2 5
The expected output is col_name - col_values
. I can generate the expected output with:
df %>% mutate(`10` = 10 - `10`) %>% mutate(`20` = 20 - `20`)
year 10 20
1 1 10 17
2 2 9 16
3 3 8 15
How can I generate the same output without explicitly copying the respecting colnames
value?
I tried the following code (which works):
df %>% mutate(`10` = as.numeric(colnames(.)[2]) - `10`) %>% mutate(`20` = as.numeric(colnames(.)[3]) - `20`)
So I tried to further reduce this but could only think of:
df %>% mutate_at(vars(-year), ~ as.numeric(colnames(.)[.]))
which can obviously not work since the .
has two meanings..
How can I achieve my expected output using mutate_at
or an alternative?
Here is one option with mutate_at
library(rlang)
library(tidyverse)
df %>%
mutate_at(2:3, list(~ as.numeric(as_name(quo(.)))- .))
# year 10 20
#1 1 10 17
#2 2 9 16
#3 3 8 15
Or this can be also done with deparse(substitute
df %>%
mutate_at(2:3, list(~ as.numeric(deparse(substitute(.))) - .))
Or using an option with map
map_dfc(names(df)[2:3], ~
df %>%
select(.x) %>%
mutate(!! .x := as.numeric(.x) - !! sym(.x))) %>%
bind_cols(df %>%
select(year), .)
Or with imap
df[-1] <- imap(df[-1], ~ as.numeric(.y) - .x)
In base R, we can use lapply
df[-1] <- lapply(names(df[-1]), function(x) as.numeric(x) - df[,x])
# year 10 20
#1 1 10 17
#2 2 9 16
#3 3 8 15
Or mapply
df[-1] <- mapply(`-`, as.numeric(names(df[-1])), df[-1])
Reshape, do stuff, then reshape again:
gather(df, key = "k", value = "v", -year) %>%
mutate(v = as.numeric(k) - v) %>%
spread(key = "k", value = "v")
# year 10 20
# 1 1 10 17
# 2 2 9 16
# 3 3 8 15
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.