R 在 purrr::map 中使用 dplyr::mutate() 而不复制行

DSH*_*DSH 3 r dplyr purrr tidyeval

这是数据:

library(tidyverse)
col_pre <- c('a', 'b', 'c')
df <- tibble(a1 = 1:3, a2 = 4:6, b1 = 7:9, b2 = 10:12, c1 = 13:15, c2 = 16:18)
Run Code Online (Sandbox Code Playgroud)

我想使用purrr::map()dplyr::mutate()创建三个新列,它们是df. 我可以map()用来迭代 a、b、c 列前缀的向量。我想出了这些tidyeval操作,以便下面的代码可以正常运行。

out <- col_pre %>%
  map_df(~ df %>% 
            mutate(!!as.name(paste0(.x, '3')) := !!as.name(paste0(.x, '1')) + !!as.name(paste0(.x, '2')))
  )
Run Code Online (Sandbox Code Playgroud)

但是,out现在有六个伪行:

     a1    a2    b1    b2    c1    c2    a3    b3    c3
1     1     4     7    10    13    16     5    NA    NA
2     2     5     8    11    14    17     7    NA    NA
3     3     6     9    12    15    18     9    NA    NA
4     1     4     7    10    13    16    NA    17    NA
5     2     5     8    11    14    17    NA    19    NA
6     3     6     9    12    15    18    NA    21    NA
7     1     4     7    10    13    16    NA    NA    29
8     2     5     8    11    14    17    NA    NA    31
9     3     6     9    12    15    18    NA    NA    33
Run Code Online (Sandbox Code Playgroud)

它所做的是不必要地复制 input 的三行df

这是我想要的输出:

     a1    a2    b1    b2   c1    c2    a3     b3    c3
1     1     4     7    10    13    16     5    17    29
2     2     5     8    11    14    17     7    19    31
3     3     6     9    12    15    18     9    21    33
Run Code Online (Sandbox Code Playgroud)

我有一种感觉purrr::reduce()可能是解决方案,但我不确定如何应用它。

任何帮助表示赞赏!

akr*_*run 5

我们可以sym在进行评估之前将字符串转换为bol,而不是mutate使用transmute然后将列与原始数据集绑定

library(stringr)
library(purrr)
library(dplyr)
col_pre %>%
     map_dfc(~ df %>%
           transmute(!! str_c(.x, '3') :=  !! rlang::sym(str_c(.x, '1'))  + 
         !! rlang::sym(str_c(.x, 2)))) %>%
     bind_cols(df, .)
# A tibble: 3 x 9
#    a1    a2    b1    b2    c1    c2    a3    b3    c3
#   <int> <int> <int> <int> <int> <int> <int> <int> <int>
#1     1     4     7    10    13    16     5    17    29
#2     2     5     8    11    14    17     7    19    31
#3     3     6     9    12    15    18     9    21    33
Run Code Online (Sandbox Code Playgroud)

或者另一种选择是 parse_exprs

df %>%
    mutate(!!! rlang::parse_exprs(str_c(sprintf("%s1 + %s2",
           col_pre, col_pre), collapse=";"))) %>% 
   rename_at(vars(contains("+")), ~ str_c(col_pre, 3))
# A tibble: 3 x 9
#     a1    a2    b1    b2    c1    c2    a3    b3    c3
#  <int> <int> <int> <int> <int> <int> <int> <int> <int>
#1     1     4     7    10    13    16     5    17    29
#2     2     5     8    11    14    17     7    19    31
#3     3     6     9    12    15    18     9    21    33
Run Code Online (Sandbox Code Playgroud)

或者另一种选择是将其转换为“长”格式,pivot_longer然后进行计算

library(tidyr)
df %>%
   mutate(rn = row_number()) %>%
   pivot_longer(cols = -rn, names_to = c(".value", "group"),
          names_sep ="(?<=[a-z])(?=[0-9])") %>%
   group_by(rn) %>%
   summarise_at(vars(col_pre), list(`3` = sum)) %>% 
   select(-rn) %>%
   bind_cols(df, .)
Run Code Online (Sandbox Code Playgroud)

或者,如果我们使用( )的devel版本,则可以与dplyr‘0.8.99.9000’acrosssummarise

df %>%
     mutate(rn = row_number()) %>%
     pivot_longer(cols = -rn, names_to = c(".value", "group"),
           names_sep ="(?<=[a-z])(?=[0-9])") %>%
     group_by(rn) %>%
     summarise(across(col_pre, sum)) %>% 
     select(-rn) %>%
     rename_all(~ str_c(., 3)) %>% 
     bind_cols(df, .)
# A tibble: 3 x 9
#     a1    a2    b1    b2    c1    c2    a3    b3    c3
#  <int> <int> <int> <int> <int> <int> <int> <int> <int>
#1     1     4     7    10    13    16     5    17    29
#2     2     5     8    11    14    17     7    19    31
#3     3     6     9    12    15    18     9    21    33
Run Code Online (Sandbox Code Playgroud)