2012-04-01 9 views
14

All:Come partizionare quando si classifica su una particolare colonna?

Ho un frame di dati come il follow.I so che posso fare una graduatoria globale come questo:

dt <- data.frame(
    ID = c('A1','A2','A4','A2','A1','A4','A3','A2','A1','A3'), 
    Value = c(4,3,1,3,4,6,6,1,8,4) 
); 
> dt 
    ID Value 
1 A1  4 
2 A2  3 
3 A4  1 
4 A2  3 
5 A1  4 
6 A4  6 
7 A3  6 
8 A2  1 
9 A1  8 
10 A3  4 
dt$Order <- rank(dt$Value,ties.method= "first") 
> dt 
    ID Value Order 
1 A1  4  5 
2 A2  3  3 
3 A4  1  1 
4 A2  3  4 
5 A1  4  6 
6 A4  6  8 
7 A3  6  9 
8 A2  1  2 
9 A1  8 10 
10 A3  4  7 

Ma come posso impostare un ordine di classificazione per un particolare ID, invece di un ordine di rango globale. Come posso farcela? In T-SQL, possiamo ottenere questo come la seguente sintassi:

RANK() OVER ([ <partition_by_clause> ] <order_by_clause>) 

Qualche idea?

risposta

4

A modo mio, ma probabilmente è meglio. Non ho mai usato il grado, non lo so nemmeno. Grazie, potrebbe essere utile.

#Your Data 
dt <- data.frame(
    ID = c('A1','A2','A4','A2','A1','A4','A3','A2','A1','A3'), 
    Value = c(4,3,1,3,4,6,6,1,8,4) 
) 
dt$Order <- rank(dt$Value,ties.method= "first") 

#My approach 
dt$id <- 1:nrow(dt) #needed for ordering and putting things back together 
dt <- dt[order(dt$ID),] 
dt$Order.by.group <- unlist(with(dt, tapply(Value, ID, function(x) rank(x, 
    ties.method = "first")))) 
dt[order(dt$id), -4] 

Resa:

ID Value Order Order.by.group 
1 A1  4  5    1 
2 A2  3  3    2 
3 A4  1  1    1 
4 A2  3  4    3 
5 A1  4  6    2 
6 A4  6  8    2 
7 A3  6  9    2 
8 A2  1  2    1 
9 A1  8 10    3 
10 A3  4  7    1 

EDIT:

Se non si cura di preservare l'ordine originale dei dati allora questo funziona con meno codice:

dt <- dt[order(dt$ID),] 
dt$Order.by.group <- unlist(with(dt, tapply(Value, ID, function(x) rank(x, 
    ties.method= "first")))) 

    ID Value Order.by.group 
1 A1  4    1 
5 A1  4    2 
9 A1  8    3 
2 A2  3    2 
4 A2  3    3 
8 A2  1    1 
7 A3  6    2 
10 A3  4    1 
3 A4  1    1 
6 A4  6    2 
+0

Grazie, Tyler. – RobinMin

13

Molte opzioni.

Utilizzando ddply dal plyr pacchetto:

library(plyr) 
ddply(dt,.(ID),transform,Order = rank(Value,ties.method = "first")) 
    ID Value Order 
1 A1  4  1 
2 A1  4  2 
3 A1  8  3 
4 A2  3  2 
5 A2  3  3 
6 A2  1  1 
7 A3  6  2 
8 A3  4  1 
9 A4  1  1 
10 A4  6  2 

O se le prestazioni è un problema (vale a dire molto grandi di dati) utilizzando il pacchetto data.table:

library(data.table) 
DT <- data.table(dt,key = "ID") 
DT[,transform(.SD,Order = rank(Value,ties.method = "first")),by = ID] 
     ID Value Order 
[1,] A1  4  1 
[2,] A1  4  2 
[3,] A1  8  3 
[4,] A2  3  2 
[5,] A2  3  3 
[6,] A2  1  1 
[7,] A4  1  1 
[8,] A4  6  2 
[9,] A3  6  2 
[10,] A3  4  1 

o in tutti i suoi dettagli cruenti una soluzione R di base utilizzando splitlapplydo.call e rbind:

do.call(rbind,lapply(split(dt,dt$ID),transform, 
       Order = rank(Value,ties.method = "first"))) 
+0

voi Grazie, Joran. – RobinMin

+2

Bella risposta, come al solito. Per ottenere le migliori prestazioni da data.table, è meglio evitare '.SD', quando puoi. Questo dovrebbe essere più veloce per i grandi data.tables (che è il posto dove più probabilmente stai usando il pacchetto in primo luogo!): 'DT <- data.table (dt, key = c (" ID "," Value ")); DT [, lista (Valore, Ordine = seq_len (.N)), da = ID] ' –

+0

Ho cercato di implementare la tua soluzione data.table ma il grado è solo 1 per ogni riga. Ho usato il tuo codice quasi parola per parola cambiando solo i nomi delle variabili. Avresti un'idea di un possibile errore che potrei fare? So che non puoi vedere il codice, quindi è una domanda difficile ma non volevo fare una domanda a ripetizione. – Kory

6

Ecco alcuni approcci:

ave Questo prende ogni serie di numeri di valore che hanno lo stesso ID e si applica separatamente a ciascuna di tali serie. Non vengono utilizzati pacchetti.

Rank <- function(x) rank(x, ties.method = "first") 
transform(dt, rank = ave(Value, ID, FUN = Rank)) 

dando:

ID Value rank 
1 A1  4 1 
2 A2  3 2 
3 A4  1 1 
4 A2  3 3 
5 A1  4 2 
6 A4  6 2 
7 A3  6 2 
8 A2  1 1 
9 A1  8 3 
10 A3  4 1 

noti che la soluzione sopra mantiene l'ordine riga originale. Potrebbe essere ordinato in seguito, se lo si desidera.

sqldf con RPostgreSQL

# see FAQ #12 on the sqldf github home page for info on sqldf and PostgreSQL 
# https://cran.r-project.org/web/packages/sqldf/README.html 

library(RPostgreSQL) 
library(sqldf) 

sqldf('select 
      *, 
      rank() over (partition by "ID" order by "Value") rank 
     from "dt" 
') 

Questa soluzione riordina le righe. Si presume che ciò sia corretto poiché la soluzione di esempio lo ha fatto (ma se non si aggiunge una colonna del numero di sequenza a dt e si aggiunge una clausola order by appropriata per riordinare il risultato nell'ordine dei numeri di sequenza).

+0

So che questo è stato da molto tempo fa, ma potresti approfondire il tuo primo metodo? Sembra che mi stia dando una classifica per ogni entrata nel mio tavolo. Ho solo la colonna che voglio raggruppare al secondo e la colonna che voglio classificare nel primo argomento come qui. – Kory

+0

Ho aggiunto alcune spiegazioni e output. –

0

È possibile utilizzare il pacchetto data.table.

setDT(dt) dt[, Order := rank(Value, ties.method = "first"), by = "ID"] dt <- as.data.frame(dt)

dando il risultato desiderato:

ID Value Order 
1 A1  4  1 
2 A2  3  2 
3 A4  1  1 
4 A2  3  3 
5 A1  4  2 
6 A4  6  2 
7 A3  6  2 
8 A2  1  1 
9 A1  8  3 
10 A3  4  1