Рассчитать ранг по связям на основе более чем одной переменной

Я пытаюсь вычислить таблицу медалей для спортивного мероприятия.

Мои данные выглядят так:

test <- data.frame("ID" = c("1_1", "1_2", "1_3", "1_4","1_5","1_6"),
                   "gold"=c(10, 4, 1, 7, 7, 1),
                   "silver"=c(1, 3, 2, 19, 19, 2),
                   "bronze"=c(1, 8, 2, 0, 0, 2))

Во-первых, я хочу упорядочить данные по количеству "золото", "серебро" и "бронза", например:

(test_ordered <- with(test, test[order(-gold, -silver, -bronze), ]))

Затем вычислите окончательный ранг медали. Вот как должен выглядеть последний столбец рейтинга:

(test_ordered$rank<-c(1, 2, 2, 4, 5, 5))

 #    ID gold silver bronze rank
 # 1 1_1   10      1      1    1
 # 4 1_4    7     19      0    2
 # 5 1_5    7     19      0    2
 # 2 1_2    4      3      8    4
 # 3 1_3    1      2      2    5
 # 6 1_6    1      2      2    5

Поскольку идентификаторы "1_4" и "1_5" имеют выигранную одинаковую комбинацию медалей, они разделили бы ранг 2, например

Мои попытки использовать более двух критериев с rank (также dplyr::min_ranked) не удалось:

with(test, rank(-gold, -silver, -bronze, ties.method = "min")) 
# (...) unused argument (-bronze)

Также interaction не удалось

as.numeric(interaction(gl(-test$gold), gl(-test$silver), gl(-test$bronze), lex.order = TRUE))

Любые идеи, как рассчитать рейтинг на основе нескольких переменных?


решено с помощью идеи Генрика:

as.data.frame(setDT(test)[ , rank := frank(test, -gold, -silver, -bronze, ties.method = "min")]; setorder(test, rank))

2 ответа

Вы можете использовать data.table эквивалент base::rank, frank, Хорошая особенность с frank является то, что он принимает, а не только векторы (как в rank), но также data.frame или data.table в качестве ввода. Для этих типов объектов ранг может основываться на нескольких столбцах.

Используя ваш оригинал data.frame:

test$rank <- data.table::frank(test, -gold, -silver, -bronze, ties.method = "min")

Или, если вы хотите пойти ва-банк с data.table функции:

setDT(test)[ , rank := frank(test, -gold, -silver, -bronze, ties.method = "min")]
setorder(test, rank)

Базовое решение R будет:

test <- data.frame("ID"=c("1_1", "1_2", "1_3", "1_4","1_5","1_6"), 
                   "gold"=c(10,4,1,7,7,1), 
                   "silver"=c(1,3,2,19,19,2), 
                   "bronze"=c(1,8,2,0,0,2))

(test_ordered<-with(test, test[order(-gold,-silver,-bronze),]))

roll.any.greater <- function (mat) {
  mat.lead <- head(mat, -1)
  mat.lag <- tail(mat, -1)
  result <- rep(1, nrow(mat.lead) + 1)
  for (i in (2:length(result))) {
    result[i] <- ifelse(any(as.logical(abs(mat.lead[i-1, ] - mat.lag[i-1, ]))) != FALSE,
                        i, result[i-1])
  }
  return(result)
}
(want <- cbind(test_ordered,
               rank =
               roll.any.greater(test_ordered[colnames(test_ordered) %in% c("gold", "silver", "bronze")])))
Другие вопросы по тегам