Как ранжировать вектор, используя второй вектор на ie прерывателе? - PullRequest
2 голосов
/ 14 февраля 2020

Мне нужно реализовать алгоритм ранжирования для чисел c векторов. Я не знаю, возможно ли сделать это, используя такие функции, как rank (), order () или sort () в R, или мне нужно жестко его кодировать. В любом случае, я не мог этого сделать.

Алгоритм работает следующим образом:

Пусть x = (x_1, x_2 ..., x_n) и y = (y_1, y_2, .. .y_n) быть двумя векторами. Нам нужно построить вектор z, состоящий из ранжированных элементов x следующим образом:

  1. If x_i < x_j then z_i < z_j

  2. If x_i = x_j then

    • z_i < z_j if y_i < y_j
    • z_i > z_j if y_i > y_j
    • z_i = z_j if y_i = y_j
  3. If x_i is NA (missing) then

    • z_i > z_j if z_j is not NA
    • z_i = z_j if z_j is NA

Например, если x = (30,15,27,49,15) и y = (12,11,10,9,8) тогда z = (4,2,3,5,1)

Я думаю, что мог бы использовать order(order(x,y, na.last=T)), и на самом деле это работало до тех пор, пока связи в х не т ie в у, а также. Если это так, то order() будет ранжировать их в порядке появления, а не оставлять их связанными.

Например, если x = (30,15,27,49,15) и y = (12,8,10,9,8), то order(order(x,y, na.last=T)) выведет z = (4,1, 3,5,2) вместо z = (4,1,3,5,1) или другого z (например, (3,1,2,4,1)), который соответствует шагу 2.

Я не мог избежать этого. Как я могу продолжить?

Ответы [ 3 ]

2 голосов
/ 14 февраля 2020

tl; dr: Я думаю, что версия 1 лучше. Версии 2 и 3 были ранними идеями, которые не так хороши, но я оставляю их здесь на случай, если они кому-нибудь пригодятся.


К сожалению rank не дает возможности разорвать связи, используя секунду вектор (полезная возможность, которую позволяют order и sort do ).

Версия 1

Но, library(data.table) обеспечивает frank(), который прекрасно выполняет свою работу.

x = c(30,15,27,49,15) 
y = c(12,11,10,9,8) 
frank(list(x,y), ties.method = "min")
# [1] 4 2 3 5 1

x = c(30,15,27,49,15) 
y = c(12,8,10,9,8)
frank(list(x,y), ties.method = "min")
# [1] 4 1 3 5 1

Обратите внимание, что frank также предоставляет еще одну опцию для ties.method = "dense" что может быть лучше для некоторых целей, поскольку не пропускает ранги (то есть, когда двум значениям присваивается ранг 1, следующий по величине получает ранг 2, а не 3) - см. пример ниже

frank(list(x,y), ties.method = "dense")
[1] 3 1 2 4 1

Версия 2

Если вы хотите придерживаться базы R, одним из простых обходных путей будет ранжирование x * K + y, где K - любое число, достаточно большое, что добавление наибольшего y к любому x*K не может измениться. порядок:

ranky = function(x,y) {
  K = 1 +  max(y) / min(diff(sort(unique(x))))
  rank(x*K + y, ties.method = 'min')
}

ranky(c(30,15,27,49,15), c(12,11,10,9,8) )
# [1] 4 2 3 5 1    
ranky(c(30,15,27,49,15), c(12,8,10,9,8))
# [1] 4 1 3 5 1

Версия 3

Также в базе R вы можете вставить вместе строковые представления фиксированной ширины каждого и затем ранжировать объединенный символьный вектор.

rank(paste(
      formatC(x, width = 15, flag = "0"), 
      formatC(y, width = 15, flag = "0")), 
     ties.method = 'min')
1 голос
/ 14 февраля 2020

Опция, использующая data.table:

library(data.table)
f <- function(x, y) {
    data.table(x, y)[order(x, y), r := .I][, r := min(r), .(x, y)]$r
}

f(c(30,15,27,49,15), c(12,11,10,9,8))
#[1] 4 2 3 5 1

f(c(30,15,27,49,15), c(12,8,10,9,8))
#[1] 4 1 3 5 1

Или какая должна быть более быстрая версия:

f <- function(x, y) {
    DT <- setindex(data.table(x, y), x, y)[order(x, y), r := .I]

    if (uniqueN(data.table(x, y))==DT[, .N]) 
        DT$r
    else 
        DT[,r := min(r), .(x, y)]$r
}
0 голосов
/ 14 февраля 2020

Вы можете написать функцию для этого:

my_order <- function(x,y){
  a <- rank(x,ties.method = "first")
  b <- `class<-`(names(which(table(x)>1)),class(x))
  c(apply(outer(x,b,'=='),2,function(m)a[m]<<-a[m][rank(y[m])]))
  a
}

Причина использования функции apply заключается в том, что мы можем иметь более одного повторного значения:

x = c(30,15,27,49,15) ;
y = c(12,8,10,9,8) 
my_order(x,y)
[1] 4 1 3 5 1

my_order(c(2,1,1,2),c(6,4,2,6))
[1] 3 2 1 3

сравнить с

order(order(c(2,1,1,2),c(6,4,2,6)))
[1] 3 2 1 4
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...