Numeración de filas dentro de grupos en un dataframe

Trabajando con un dataframe similar a esto:

set.seed(100) df <- data.frame(cat = c(rep("aaa", 5), rep("bbb", 5), rep("ccc", 5)), val = runif(15)) df <- df[order(df$cat, df$val), ] df cat val 1 aaa 0.05638315 2 aaa 0.25767250 3 aaa 0.30776611 4 aaa 0.46854928 5 aaa 0.55232243 6 bbb 0.17026205 7 bbb 0.37032054 8 bbb 0.48377074 9 bbb 0.54655860 10 bbb 0.81240262 11 ccc 0.28035384 12 ccc 0.39848790 13 ccc 0.62499648 14 ccc 0.76255108 15 ccc 0.88216552 

Estoy tratando de agregar una columna con numeración dentro de cada grupo. Hacerlo de esta manera obviamente no está usando los poderes de R:

  df$num <- 1 for (i in 2:(length(df[,1]))) { if (df[i,"cat"]==df[(i-1),"cat"]) { df[i,"num"]<-df[i-1,"num"]+1 } } df cat val num 1 aaa 0.05638315 1 2 aaa 0.25767250 2 3 aaa 0.30776611 3 4 aaa 0.46854928 4 5 aaa 0.55232243 5 6 bbb 0.17026205 1 7 bbb 0.37032054 2 8 bbb 0.48377074 3 9 bbb 0.54655860 4 10 bbb 0.81240262 5 11 ccc 0.28035384 1 12 ccc 0.39848790 2 13 ccc 0.62499648 3 14 ccc 0.76255108 4 15 ccc 0.88216552 5 

¿Cuál sería una buena manera de hacer esto?

Utilice ave , ddply , dplyr o data.table :

 df$num < - ave(df$val, df$cat, FUN = seq_along) 

o:

 library(plyr) ddply(df, .(cat), mutate, id = seq_along(val)) 

o:

 library(dplyr) df %>% group_by(cat) %>% mutate(id = row_number()) 

o (la memoria más eficiente, como se asigna por referencia dentro de DT ):

 library(data.table) DT < - data.table(df) DT[, id := seq_len(.N), by = cat] DT[, id := rowid(cat)] 

Para hacer que esta pregunta de r-faq sea más completa, una alternativa de base R con sequence y rle :

 df$num < - sequence(rle(df$cat)$lengths) 

que da el resultado esperado:

 > df cat val num 4 aaa 0.05638315 1 2 aaa 0.25767250 2 1 aaa 0.30776611 3 5 aaa 0.46854928 4 3 aaa 0.55232243 5 10 bbb 0.17026205 1 8 bbb 0.37032054 2 6 bbb 0.48377074 3 9 bbb 0.54655860 4 7 bbb 0.81240262 5 13 ccc 0.28035384 1 14 ccc 0.39848790 2 11 ccc 0.62499648 3 15 ccc 0.76255108 4 12 ccc 0.88216552 5 

Si df$cat es una variable de factor, primero debe envolverlo como as.character .

 df$num < - sequence(rle(as.character(df$cat))$lengths) 

Aquí hay una opción que usa un ciclo for por grupos más bien por filas (como lo hizo OP)

 for (i in unique(df$cat)) df$num[df$cat == i] < - seq_len(sum(df$cat == i)) 

Me gustaría agregar una variante de data.table utilizando la función rank() , que ofrece la posibilidad adicional de cambiar el orden y lo hace un poco más flexible que la solución seq_len() y es bastante similar a las funciones row_number en RDBMS.

 # Variant with ascending ordering library(data.table) dt < - data.table(df) dt[, .( val , num = rank(val)) , by = list(cat)][order(cat, num),] cat val num 1: aaa 0.05638315 1 2: aaa 0.25767250 2 3: aaa 0.30776611 3 4: aaa 0.46854928 4 5: aaa 0.55232243 5 6: bbb 0.17026205 1 7: bbb 0.37032054 2 8: bbb 0.48377074 3 9: bbb 0.54655860 4 10: bbb 0.81240262 5 11: ccc 0.28035384 1 12: ccc 0.39848790 2 13: ccc 0.62499648 3 14: ccc 0.76255108 4 # Variant with descending ordering dt[, .( val , num = rank(-val)) , by = list(cat)][order(cat, num),]