R использует dplyr :: mutate () в purrr :: map без дублирования строк - PullRequest
3 голосов
/ 09 января 2020

Вот данные:

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)

Я хочу использовать 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')))
  )

Однако теперь у 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

Что сделано Излишне дублируйте три строки ввода 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

У меня такое чувство, что purrr::reduce() может быть решением, но я не знаете, как его применить.

Любая помощь приветствуется!

Ответы [ 2 ]

5 голосов
/ 09 января 2020

Мы можем преобразовать строки в 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

или другим вариант 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

Или другой вариант - преобразовать его в «длинный» формат с помощью 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, .)

Или, если мы используйте devel версию dplyr (‘0.8.99.9000’), тогда можно использовать across вместе с summarise

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
1 голос
/ 09 января 2020

Мы можем использовать map_dfc с transmute

library(dplyr)
library(purrr)

bind_cols(df, map_dfc(col_pre, ~df %>% 
       transmute(!!paste0(.x, 3) := !!sym(paste0(.x, 1)) + !!sym(paste0(.x, 2)))))

# 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

В базе R мы можем использовать split.default

df[paste0(col_pre, 3)] <- lapply(split.default(df, 
                          sub('\\d', '', names(df))), rowSums)

Или без разделения мы можем данные подмножества, основанные на начальном имени столбца, как предложено @thelatemail

df[paste0(col_pre,3)] <- lapply(col_pre, function(x) 
                        rowSums(df[startsWith(names(df), x)]))
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...