以整洁的方式转换多列

Transform multiple columns the tidy way

不知是否有在管道内转换多列的解决方案。

假设我们有一个包含三列的标题。 iq_preiq_post 必须在对数刻度上转换并保存到新列中。

library(tidyverse)
library(magrittr)

df <- tibble(
  iq_pre = rnorm(10, 100, 15),
  iq_post = rnorm(10, 100, 18),
  gender = rep(c("m", "f"), each = 5)
)

我知道我可以通过

获得基数 R 的结果
df[c("iq_pre_lg", "iq_post_lg")] <- log(df[c("iq_pre", "iq_post")])

或使用 lapply 遍历列。

我想出的唯一整洁的解决方案是像这样对每一列手动使用 mutate

df %<>% 
  mutate(iq_pre_lg = log(iq_pre),
         iq_post_lg = log(iq_post))

由于要转换的列的名称以相同的字母开头,我也可以使用

df %<>% 
  mutate_at(vars(starts_with("iq")), funs(lg = log(.)))

但是如果我想转换 20 个不同名称的列怎么办?有没有办法使用 purrr::map 甚至 tidyr::nest 以更优雅的方式解决这个问题?

我们可以使用

df %>%
     mutate_at(vars(matches("iq")), log)

matches 的一个优点是它可以在一次调用中匹配多个模式。例如,如果我们需要在以 'iq' 开始 (^) 或 (|) 以 'oq' 结束 ($) 的列上应用该函数, 这可以传递到单个 matches

df %>%
  mutate_at(vars(matches('^iq|oq$'), log)

如果列名完全不同,并且n列有n种模式,但是如果列的位置还是有一定的顺序,那么列的位置编号就可以了传进了vars。在当前示例中,'iq' 列是第 1 和第 2 列

df %>% 
   mutate_at(1:2, log)

同理,如果这20列占据了前20个位置

df %>%
   mutate_at(1:20, log)

或者如果位置是1到6、8到12,41:50

df %>%
    mutate_at(vars(1:6, 8:12, 41:50), log)