简体   繁体   中英

Use numeric value of `colnames` to recalculate columns in a data.frame using `dplyr`'s `mutate_at` or an alternative

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.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM