Использование `map` для поиска значений строк по именам столбцов

#r #dplyr #purrr

#r #dplyr #муррр

Вопрос:

У меня есть набор данных с последовательно названными столбцами, и я хотел бы получить среднее значение столбцов по их группе, например,

 library(dplyr)
library(purrr)
library(glue)

df <- tibble(`1_x_blind` = 1:3, 
       `1_y_blind` = 7:9,
       `2_x_blind` = 4:6, 
       `2_y_blind` = 5:7)
  
df %>% 
  mutate(`1_overall_test` = rowMeans(select(., matches(glue("^1_.*_blind$")))))
#> # A tibble: 3 x 5
#>   `1_x_blind` `1_y_blind` `2_x_blind` `2_y_blind` `1_overall_test`
#>         <int>       <int>       <int>       <int>            <dbl>
#> 1           1           7           4           5                4
#> 2           2           8           5           6                5
#> 3           3           9           6           7                6
  

Этот метод работает нормально. Следующим шагом для меня было бы масштабировать его так, чтобы я мог выполнять всю серию столбцов, например, что-то вроде

 df %>% 
  mutate(overall_blind = map(1:2, ~rowMeans(select(., matches(glue("^{.x}_.*_blind$"))))))
#> Error: Problem with `mutate()` input `overall_blind`.
#> x no applicable method for 'select' applied to an object of class "c('integer', 'numeric')"
#> ℹ Input `overall_blind` is `map(1:2, ~rowMeans(select(., matches(glue("^{.x}_.*_blind$")))))`.
  

Я думаю, что проблема здесь в том, что select это сбивает с толку . оператора. Возможно ли map таким образом переопределить ряд имен столбцов? В идеале я бы хотел, чтобы имена столбцов следовали {.x}_overall шаблону, как в примере выше.

Комментарии:

1. Вы хотите, чтобы в вашем выводе был только 1 дополнительный overall_blind столбец списков для хранения значений строк всех подгрупп, или вам нужно много столбцов, таких как 1_overall_test , 2_overall_test , и т.д.?

2. Было бы лучше иметь много столбцов

3. Количество столбцов в группе фиксировано (всегда 2, т.Е. 1_x, 1_y) Или они различаются для каждой группы?

4. Число всегда будет фиксированным

Ответ №1:

Обновление Вот более чистый способ, который не требует rename или bind_cols :

 map_dfc(1:2, 
        function(x) df %>% 
          select(matches(glue("^{x}_.*_blind$"))) %>%
          mutate("{x}_overall_blind" := rowMeans(.))
        )

# A tibble: 3 x 6
  `1_x_blind` `1_y_blind` `1_overall_blind` `2_x_blind` `2_y_blind` `2_overall_blind`
        <int>       <int>             <dbl>       <int>       <int>             <dbl>
1           1           7                 4           4           5               4.5
2           2           8                 5           5           6               5.5
3           3           9                 6           6           7               6.5
  

Предыдущая страница
Вот такой map подход.
Задача состоит в изменении двух новых столбцов на основе отдельных групп существующих столбцов. Проще всего просто сделать это самостоятельно map_dfc() , а затем привязать его к существующему df .

 
df %>%
  bind_cols(
    map_dfc(1:2, ~rowMeans(df %>% select(matches(glue("^{.x}_.*_blind$"))))) %>%
      rename_with(~paste0(str_replace(., "\...", ""), "_overall_blind"))
  )

# A tibble: 3 x 6
  `1_x_blind` `1_y_blind` `2_x_blind` `2_y_blind` `1_overall_blind` `2_overall_blind`
        <int>       <int>       <int>       <int>             <dbl>             <dbl>
1           1           7           4           5                 4               4.5
2           2           8           5           6                 5               5.5
3           3           9           6           7                 6               6.5
  

И вот способ получить средние значения по группам столбцов по строкам с использованием сводных данных, что позволяет избежать регулярных выражений и mutate / map операций:

 df %>%
  mutate(row = row_number()) %>%
  pivot_longer(-row) %>%
  separate(name, c("grp"), sep = "_", extra = "drop") %>%
  group_by(row, grp) %>%
  summarise(overall_blind = mean(value)) %>%
  ungroup() %>%
  pivot_wider(id_cols = row, names_from = grp, values_from = overall_blind, 
              names_glue = "{grp}_{.value}") %>%
  bind_cols(df)

# A tibble: 3 x 6
  `1_overall_blind` `2_overall_blind` `1_x_blind` `1_y_blind` `2_x_blind` `2_y_blind`
              <dbl>             <dbl>       <int>       <int>       <int>       <int>
1                 4               4.5           1           7           4           5
2                 5               5.5           2           8           5           6
3                 6               6.5           3           9           6           7
  

Ответ №2:

Вот одно из решений:

 map_dfc(1:2, function(x) {
  select(df, matches(glue("^{x}_.*_blind$"))) %>%
    mutate(overall_blind = rowMeans(select(., matches(glue("^{x}_.*_blind$"))))) %>%
    
    # General but not perfect names
    # set_names(paste0(x, "_", names(.)))
    
    # Hand-tailored names
    set_names(c(names(.)[1], names(.)[2], paste0(x, "_", names(.)[3])))
  })

#> # A tibble: 3 x 6
#>   `1_x_blind` `1_y_blind` `1_overall_blind` `2_x_blind` `2_y_blind` `2_overall_blind`
#>         <int>       <int>             <dbl>       <int>       <int>             <dbl>
#> 1           1           7                 4           4           5               4.5
#> 2           2           8                 5           5           6               5.5
#> 3           3           9                 6           6           7               6.5
  

Я добавил две возможности присвоения имен overall_blind столбцам для каждой группы: одно более общее, но не идеальное имя (оно дублирует 1_ или 2_ для столбцов данных), а другое, которое дает нужные имена, но требует заранее знать количество столбцов в группе.

Ответ №3:

Мы можем использовать split.default для разделения данных на list наборы данных на основе шаблона имен столбцов, затем получить rowMeans и связать с исходными данными

 library(dplyr)
library(purrr)
library(stringr)
df %>%
      split.default(readr::parse_number(names(.))) %>%
      map_dfc(rowMeans) %>% 
      set_names(str_c(names(.), "_overall_blind")) %>%
      bind_cols(df, .)
# A tibble: 3 x 6
#  `1_x_blind` `1_y_blind` `2_x_blind` `2_y_blind` `1_overall_blind` `2_overall_blind`
#        <int>       <int>       <int>       <int>             <dbl>             <dbl>
#1           1           7           4           5                 4               4.5
#2           2           8           5           6                 5               5.5
#3           3           9           6           7                 6               6.5