Комбинировать: rowwise (), mutate (), через (), для нескольких функций

Это как-то связано с этим вопросом: В принципе, я пытаюсь понять, как работают rowwise операции с mutate в нескольких столбцах, применяя более 1 функции, например (mean(), sum(), min() и т. Д.).

Я узнал, что эту работу выполняет across, а не c_across. Я узнал, что функция mean() отличается от функции min() тем, что mean() не работает с фреймами данных, и нам нужно изменить ее на вектор, что можно сделать с помощью unlist или as.matrix - ›узнал от Ронака Шаха здесь Что такое rowwise () и c_across ()

Теперь рассмотрим мой реальный случай: я смог выполнить эту задачу, но потерял один столбец d. Как я могу избежать потери столбца d в этой настройке.

My df:

df <- structure(list(a = 1:5, b = 6:10, c = 11:15, d = c("a", "b", 
"c", "d", "e"), e = 1:5), row.names = c(NA, -5L), class = c("tbl_df", 
"tbl", "data.frame"))

Не работает:

df %>% 
  rowwise() %>% 
  mutate(across(a:e), 
         avg = mean(unlist(cur_data()), na.rm = TRUE),
         min = min(unlist(cur_data()), na.rm = TRUE), 
         max = max(unlist(cur_data()), na.rm = TRUE)
  )

# Output:
      a     b     c d         e   avg min   max  
  <int> <int> <int> <chr> <int> <dbl> <chr> <chr>
1     1     6    11 a         1    NA 1     a    
2     2     7    12 b         2    NA 12    b    
3     3     8    13 c         3    NA 13    c    
4     4     9    14 d         4    NA 14    d    
5     5    10    15 e         5    NA 10    e 

Работает, но я теряю столбец d:

df %>% 
  select(-d) %>% 
  rowwise() %>% 
  mutate(across(a:e), 
         avg = mean(unlist(cur_data()), na.rm = TRUE),
         min = min(unlist(cur_data()), na.rm = TRUE), 
         max = max(unlist(cur_data()), na.rm = TRUE)
  )

      a     b     c     e   avg   min   max
  <int> <int> <int> <int> <dbl> <dbl> <dbl>
1     1     6    11     1  4.75     1    11
2     2     7    12     2  5.75     2    12
3     3     8    13     3  6.75     3    13
4     4     9    14     4  7.75     4    14
5     5    10    15     5  8.75     5    15

person TarJae    schedule 01.05.2021    source источник


Ответы (3)


Использование pmap() из purrr может быть более предпочтительным, поскольку вам нужно выбрать данные только один раз, и вы можете использовать помощники выбора:

df %>% 
 mutate(pmap_dfr(across(where(is.numeric)),
                 ~ data.frame(max = max(c(...)),
                              min = min(c(...)),
                              avg = mean(c(...)))))

      a     b     c d         e   max   min   avg
  <int> <int> <int> <chr> <int> <int> <int> <dbl>
1     1     6    11 a         1    11     1  4.75
2     2     7    12 b         2    12     2  5.75
3     3     8    13 c         3    13     3  6.75
4     4     9    14 d         4    14     4  7.75
5     5    10    15 e         5    15     5  8.75

Или с добавлением tidyr:

df %>% 
 mutate(res = pmap(across(where(is.numeric)),
                   ~ list(max = max(c(...)),
                          min = min(c(...)),
                          avg = mean(c(...))))) %>%
 unnest_wider(res)
person tmfmnk    schedule 01.05.2021
comment
Спасибо tmfmnk. Не могли бы вы коротко прокомментировать c(...). - person TarJae; 01.05.2021
comment
см. это вопрос. Это может помочь - person AnilGoyal; 01.05.2021
comment
Если использование purrr в порядке, нет ничего лучше этого. +1. - person AnilGoyal; 01.05.2021
comment
@TarJae Эти два также могут помочь: stackoverflow. com / questions / 67049561 / using-pmap-with-c-part-2 stackoverflow.com/questions/67037099/ - person Anoushiravan R; 01.05.2021

Изменить:

Лучший выход отсюда

df %>%
  rowwise() %>% 
  mutate(min = min(c_across(a:e & where(is.numeric)), na.rm = TRUE),
         max = max(c_across(a:e & where(is.numeric)), na.rm = TRUE), 
         avg = mean(c_across(a:e & where(is.numeric)), na.rm = TRUE)
  )

# A tibble: 5 x 8
# Rowwise: 
      a     b     c d         e   min   max   avg
  <int> <int> <int> <chr> <int> <int> <int> <dbl>
1     1     6    11 a         1     1    11  4.75
2     2     7    12 b         2     2    12  5.75
3     3     8    13 c         3     3    13  6.75
4     4     9    14 d         4     4    14  7.75
5     5    10    15 e         5     5    15  8.75

Предыдущий ответ. Ваш this will work даже не будет работать должным образом, если вы измените последовательность вывода, см.

df %>% 
  select(-d) %>% 
  rowwise() %>% 
  mutate(across(a:e), 
         min = min(unlist(cur_data()), na.rm = TRUE),
         max = max(unlist(cur_data()), na.rm = TRUE), 
         avg = mean(unlist(cur_data()), na.rm = TRUE)
  )

# A tibble: 5 x 7
# Rowwise: 
      a     b     c     e   min   max   avg
  <int> <int> <int> <int> <int> <int> <dbl>
1     1     6    11     1     1    11  5.17
2     2     7    12     2     2    12  6.17
3     3     8    13     3     3    13  7.17
4     4     9    14     4     4    14  8.17
5     5    10    15     5     5    15  9.17

Поэтому рекомендуется делать это так:

df %>% 
  select(-d) %>% 
  rowwise() %>% 
  mutate(min = min(c_across(a:e), na.rm = TRUE),
         max = max(c_across(a:e), na.rm = TRUE), 
         avg = mean(c_across(a:e), na.rm = TRUE)
  )

# A tibble: 5 x 7
# Rowwise: 
      a     b     c     e   min   max   avg
  <int> <int> <int> <int> <int> <int> <dbl>
1     1     6    11     1     1    11  4.75
2     2     7    12     2     2    12  5.75
3     3     8    13     3     3    13  6.75
4     4     9    14     4     4    14  7.75
5     5    10    15     5     5    15  8.75

Еще одна альтернатива -

cols <- c('a', 'b', 'c', 'e')
df %>%
  rowwise() %>% 
  mutate(min = min(c_across(cols), na.rm = TRUE),
         max = max(c_across(cols), na.rm = TRUE), 
         avg = mean(c_across(cols), na.rm = TRUE)
  )

# A tibble: 5 x 8
# Rowwise: 
      a     b     c d         e   min   max   avg
  <int> <int> <int> <chr> <int> <int> <int> <dbl>
1     1     6    11 a         1     1    11  4.75
2     2     7    12 b         2     2    12  5.75
3     3     8    13 c         3     3    13  6.75
4     4     9    14 d         4     4    14  7.75
5     5    10    15 e         5     5    15  8.75

Даже предложенный @Sinh подход group_by не будет работать должным образом в этих случаях.

person AnilGoyal    schedule 01.05.2021
comment
Большое спасибо AnilGoyal. Я очень ценю ваши усилия, понимаю все ваши мысли и согласен с вами. Но почему-то мне не нравится повторять c_across(a:e & where(is.numeric)), na.rm = TRUE) три раза. Я полагаю, что можно предотвратить это повторение и использовать его один раз с across. - person TarJae; 01.05.2021
comment
TarJae, я боюсь, что то, что вы пытаетесь сделать только с dplyr, может оказаться невозможным, mutate(across.. работает немного иначе. Он изменяет все существующие в нем столбцы. Даже если вы используете cur_data, он будет включать новые добавленные столбцы, как показано выше. Более того, вам все равно придется повторять cur_data..bla..bla снова n раз, в отличие от mutate(across. Однако это может быть возможно со многими методами в purrr - один, как показано в ответе. - person AnilGoyal; 01.05.2021

Вот один метод, который сохранит атрибут data.frame в mutate, если мы хотим установить для определенного столбца атрибут имени строки (column_to_rownames), а затем вернуть атрибут после преобразования.

library(dplyr)
library(tibble)
library(purrr)
df %>% 
   column_to_rownames('d') %>%
   mutate(max = reduce(., pmax), min = reduce(., pmin), 
         avg = rowMeans(.)) %>% 
   rownames_to_column('d')
#  d a  b  c e max min  avg
#1 a 1  6 11 1  11   1 4.75
#2 b 2  7 12 2  12   2 5.75
#3 c 3  8 13 3  13   3 6.75
#4 d 4  9 14 4  14   4 7.75
#5 e 5 10 15 5  15   5 8.75
person akrun    schedule 01.05.2021
comment
Уважаемый Арун, можно ли применить все три функции к каждой строке с помощью invoke_map или exec? - person Anoushiravan R; 01.05.2021
comment
Да спасибо. Я также включил mean в список функций, но, поскольку это операция по столбцам, она не приведет к желаемому результату. Однако pmin и pmax позволяют получить максимальное и минимальное значение для каждой строки из-за их функциональности. Мне было просто любопытно. - person Anoushiravan R; 01.05.2021
comment
@AnoushiravanR функции pmin/pmax и mean имеют разное поведение. pmin / pmax действует со строками параллельно, где, как mean, ожидает вектор и не векторизуется. Вы можете ожидать, что rowMeans будет работать, но аргументы этой функции - всего лишь один, то есть x, который может быть матрицей или data.frame / tibble. где, как в pmax/pmin, это ..., т.е. он может варьироваться. Итак, invoke_map(list(pmax, pmin), list(df %>% select(-d))) работает, поскольку аргументы - это столбцы data.frame - person akrun; 01.05.2021
comment
Спасибо Вам большое. Это сопоставление аргументов - важный фактор, который мне нужно принять во внимание в такой момент. Но рад, что наконец-то ознакомился с параллельными максимумами и минимумами. - person Anoushiravan R; 01.05.2021