2012-04-01 11 views
14

Todos:¿Cómo particionar al clasificar en una columna en particular?

tengo una trama de datos como el follow.I sabe que puedo hacer una orden de importancia mundial como esto:

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 

Pero cómo puedo fijar un orden de rango para un ID particular, en lugar de un orden de rango global. ¿Cómo puedo hacer esto? En T-SQL, podemos hacer esto con la siguiente sintaxis:

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

¿Alguna idea?

Respuesta

4

A mi manera, pero es probable que sea mejor. Nunca usó rango, ni siquiera lo sabía. Gracias, puede ser útil.

#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] 

Rendimiento:

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:

Si no se preocupan por preservar el orden original de los datos entonces funciona con menos código:

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

Gracias, Tyler. – RobinMin

13

Muchas opciones.

Usando ddply del plyr paquete:

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 si el rendimiento es un problema (es decir, datos muy grandes) utilizando el paquete de 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 en todo su detalle sangriento una solución de base R usando splitlapplydo.call y rbind:

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

Gracias usted, Joran. – RobinMin

+2

Buena respuesta, como de costumbre. Para obtener el mejor rendimiento de data.table, es mejor evitar '.SD', cuando pueda. Esto debería ser más rápido para los grandes data.tables (¡que es donde es más probable que use el paquete en primer lugar!): 'DT <- data.table (dt, key = c (" ID "," Value ")); DT [, list (Value, Order = seq_len (.N)), by = ID] ' –

+0

He intentado implementar su solución de data.table pero el rango es solo 1 por cada fila. He usado tu código casi palabra por palabra solo cambiando los nombres de las variables. ¿Tendría una idea de un posible error que podría estar cometiendo? Sé que no puedes ver el código, así que es una pregunta difícil, pero no quería hacer una pregunta repetida. – Kory

6

Aquí hay un par de enfoques:

ave Esto toma cada conjunto de números de Valor que tienen el mismo ID y aplica rango por separado para cada uno de tales conjuntos. No se usan paquetes

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 

Tenga en cuenta que la solución anterior mantiene el orden de fila original. Podría ser ordenado después si eso fuera deseado.

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" 
') 

Esta solución reordena las filas. Se supone que está bien, ya que su solución de ejemplo lo hizo (pero si no agrega una columna de número de secuencia a dt y agrega una orden apropiada por cláusula para volver a ordenar el resultado en el orden numérico de secuencia).

+0

Sé que esto fue hace mucho tiempo, pero ¿podría elaborar sobre su primer método? Parece que me da un rango de uno por cada entrada en mi mesa. Solo tengo la columna que quiero agrupar por segundo y la columna que quiero clasificar en el primer argumento como la que tiene aquí. – Kory

+0

He agregado algunas explicaciones y resultados. –

0

Puede usar el paquete data.table.

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

dando el resultado deseado:

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 
Cuestiones relacionadas