tidyverse вычисляет рейтинг по строке в нескольких столбцах

#r #rank #dplyr

#r #рейтинг #dplyr

Вопрос:

У меня есть следующий фрейм данных:

 dat <- data.frame(id = c("a", "b", "c", "d"),
                  x1 = c(1, 3, 5, 7),
                  x2 = c(4, 2, 6, 0),
                  x3 = c(2, 2, 5, 9))
  

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

а) в идеале, должно быть 4 новых столбца с соответствующими рангами или
б) будет новый вложенный столбец, который мне, вероятно, нужно как-то отменить.

Я попробовал следующее, которое, по крайней мере, дает мне столбец списка.

 dat %>%
  rowwise() %>%
  mutate(my_ranks = list(rank(c_across(starts_with("x")))))
  

Но когда я пытаюсь отменить проверку, это выдает мне ранги, но делает это путем создания новых строк (т. Е. Каждый исходный случай теперь появляется четыре раза). Хотя я предполагаю, что мог бы каким-то образом изменить этот результат с помощью pivot_wider , мне кажется неправильным следовать этим маршрутом.

Есть идея получше / проще? Спасибо.

Ответ №1:

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

 library(dplyr)
library(tidyr)
library(stringr)
dat %>%
   rowwise() %>%
   mutate(my_ranks = list(rank(c_across(starts_with("x"))))) %>%
   unnest_wider(c(my_ranks)) %>%
   rename_at(vars(starts_with("...")), ~ str_replace(., fixed("..."), "rank_x"))
# A tibble: 4 x 7
#  id       x1    x2    x3  rank_x1 rank_x2 rank_x3
#  <chr> <dbl> <dbl> <dbl>    <dbl>    <dbl>    <dbl>
#1 a         1     4     2      1        3        2  
#2 b         3     2     2      3        1.5      1.5
#3 c         5     6     5      1.5      3        1.5
#4 d         7     0     9      2        1        3  
  

Другой вариант pmap/as_tibble_row

 library(tibble)
library(purrr)
dat %>% 
     mutate(my_ranks = pmap(select(., starts_with('x')), ~ 
           as_tibble_row(rank(c(...)),
            .name_repair = ~ str_c('rank', seq_along(.))))) %>%
     unnest(c(my_ranks))
# A tibble: 4 x 7
#  id       x1    x2    x3 rank1 rank2 rank3
#  <chr> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl>
#1 a         1     4     2   1     3     2  
#2 b         3     2     2   3     1.5   1.5
#3 c         5     6     5   1.5   3     1.5
#4 d         7     0     9   2     1     3  
  

Это можно сделать более просто с помощью rowRanks из matrixStats

 library(matrixStats)
nm1 <- names(dat)[-1]
dat[paste0('rank', nm1)] <- rowRanks(as.matrix(dat[nm1]), ties.method = 'average')
  

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

1. Ах, конечно, unnest_wider. Спасибо! Это совершенно вылетело у меня из головы. Из любопытства… было бы также решение без этой повторяющейся штуковины со списком?

2. @deschen. Вам не нужен, as.list поскольку unnest_wider он все еще может работать без этого

3. @deschen Я добавил pmap подход. это могло бы быть быстрее

Ответ №2:

Я думаю, это своего рода tidyverse:

 dat %>%
  bind_cols(as_tibble(`colnames<-`(t(apply(dat[-1], 1, rank)), paste0("rank_x", 1:3))))
#>   id x1 x2 x3 rank_x1 rank_x2 rank_x3
#> 1  a  1  4  2     1.0     3.0     2.0
#> 2  b  3  2  2     3.0     1.5     1.5
#> 3  c  5  6  5     1.5     3.0     1.5
#> 4  d  7  0  9     2.0     1.0     3.0