2011-02-06 9 views
9

Kiedy są więzy w oryginalnych danych, czy istnieje sposób na stworzenie rankingu bez luk w szeregach (kolejne, całkowite wartości rang)? Załóżmy:Jak uzyskać rankingi bez przerw, gdy istnieją więzy między wartościami?

x <- c(10, 10, 10, 5, 5, 20, 20) 
rank(x) 
# [1] 4.0 4.0 4.0 1.5 1.5 6.5 6.5 

W tym przypadku pożądany wynik byłby:

my_rank(x) 
[1] 2 2 2 1 1 3 3 

Grałem ze wszystkimi opcjami ties.method opcji (average, max, min, random), z których żaden zaprojektowane, aby zapewnić pożądany rezultat.

Czy można to osiągnąć za pomocą funkcji rank()?

Odpowiedz

3

Mogę wymyślić szybką funkcję, aby to zrobić. Nie jest optymalny z pętlą for, ale działa :)

x=c(1,1,2,3,4,5,8,8) 

foo <- function(x){ 
    su=sort(unique(x)) 
    for (i in 1:length(su)) x[x==su[i]] = i 
    return(x) 
} 

foo(x) 

[1] 1 1 2 3 4 5 6 6 
+0

To działa cudownie. Dziękuję Ci. Również, bardzo łatwo jest zmienić kierunek sortowania, jeśli potrzebujesz obniżenia rangi! Twoje zdrowie! –

0

Co z numerem sort()?

x <- c(1,1,2,3,4,5) 
sort(x) 

> sort(x) 
[1] 1 1 2 3 4 5 
+0

Jest to prawidłowa zbieg okoliczności. Liczby nie są tak czyste, jak w przykładzie. to znaczy. spróbuj: x <- c (0,5,0,56,0,76,0,23,0,33,0.4) –

+0

@Bandon - Może nie rozumiem ograniczenia twojej potrzeby tutaj ... prawdopodobnie tej części "Nie mogę mieć dwóch elementów w albo koniec zakresu jest większy niż 1 lub maksimum (zakres). " Jakie jest pożądane wyjście z twojego przykładu w powyższym komentarzu? Jeśli to jest bardziej reprezentatywne niż to, co masz w swoim pytaniu, może możesz edytować pytanie, aby to odzwierciedlić? – Chase

+0

przeprosiny, jeśli nie było to jasne. Pytanie dotyczy danych rankingowych i to, co tu zrobiliście, dostarcza pewnego rodzaju danych, które są po prostu tą samą sekwencją liczb, które pochodzą z rozwiązania ich rankingowania, do którego próbuję się dostać. Celem jest zdobycie rang, a nie tylko sortowanie. –

6

W „loopless” sposobem na to jest po prostu traktować wektor jako uporządkowanej czynnika, a następnie przekształcić go numeryczna:

> as.numeric(ordered(c(10,10,10,10, 5,5,5, 10, 10))) 
[1] 2 2 2 2 1 1 1 2 2 
> as.numeric(ordered(c(0.5,0.56,0.76,0.23,0.33,0.4))) 
[1] 4 5 6 1 2 3 
> as.numeric(ordered(c(1,1,2,3,4,5,8,8))) 
[1] 1 1 2 3 4 5 6 6 

Aktualizacja: Innym sposobem, który wydaje się szybciej jest używać findInterval i sort(unique()):

> x <- c(10, 10, 10, 10, 5,5,5, 10, 10) 
> findInterval(x, sort(unique(x))) 
[1] 2 2 2 2 1 1 1 2 2 

> x <- round(abs(rnorm(1000000)*10)) 
> system.time(z <- as.numeric(ordered(x))) 
    user system elapsed 
    0.996 0.025 1.021 
> system.time(z <- findInterval(x, sort(unique(x)))) 
    user system elapsed 
    0.077 0.003 0.080 
1

Inną funkcją, która to robi, ale wydaje się nieefektywne. Nie ma pętli for, ale wątpię, by była bardziej wydajna niż sugestia Sachy!

x=c(1,1,2,3,4,5,8,8) 
fancy.rank <- function(x) { 
    x.unique <- unique(x) 
    d1 <- data.frame(x=x) 
    d2 <- data.frame(x=x.unique, rank(x.unique)) 
    merge(d1, d2, by="x")[,2] 
} 

fancy.rank(x) 

[1] 1 1 2 3 4 5 6 6 
12

Modified crayola solution ale uing match zamiast merge:

x_unique <- unique(x) 
x_ranks <- rank(x_unique) 
x_ranks[match(x,x_unique)] 
+0

Doskonale! Jak się okazuje, wygląda na to, że jest to najszybsze rozwiązanie (benchmarking z rep (x, 100000)).Zasadniczo: Marek> Prasad (poprawiony)> Chase> Prasad (pierwszy)> Crayola (pod względem szybkości) – crayola

+6

Możesz zrobić to wszystko w jednym wierszu: 'match (x, sort (unique (x)))' – hadley

+1

@hadley Jak zawsze masz rację;) Rozumiem to rozwiązanie po wysłaniu, ale czasy były zaskakujące, więc mam aktualizację. – Marek

0

staram się myśleć o innej drodze

x <- c(10,10,10,5,5,20,20) 
as.numeric(as.factor(x)) 
[1] 2 2 2 1 1 3 3 
Powiązane problemy