Numérotation des lignes dans les groupes d’un bloc de données

Travailler avec un bloc de données similaire à celui-ci:

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 

J’essaie d’append une colonne avec numérotation dans chaque groupe. Le faire de cette façon n’utilise évidemment pas les pouvoirs 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 

Quel serait un bon moyen de faire cela?

Utilisez ave , ddply , dplyr ou data.table :

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

ou:

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

ou:

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

ou (le plus efficace en mémoire, comme il l'affecte par référence dans DT ):

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

Pour rendre cette question r-faq plus complète, une alternative de base R avec sequence et rle :

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

qui donne le résultat escompté:

 > 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 est une variable de facteur, vous devez d'abord l'envelopper comme as.character :

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

Voici une option utilisant une boucle for par groupes plutôt que par lignes (comme OP)

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

Je voudrais append une variante de data.table utilisant la fonction rank() qui offre la possibilité supplémentaire de modifier l’ordre et la rend donc un peu plus flexible que la solution seq_len() et est assez similaire aux fonctions row_number en SGBDR.

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