Numerazione di righe all’interno di gruppi in un frame di dati

Lavorare con una cornice dati simile a questa:

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 

Sto cercando di aggiungere una colonna con numerazione all’interno di ciascun gruppo. Facendolo in questo modo ovviamente non si usano i poteri di 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 

Quale sarebbe un buon modo per farlo?

Usa 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 (il più efficiente in termini di memoria, poiché assegna per riferimento all'interno di DT ):

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

Per rendere questa domanda r-faq più completa, un’alternativa di base R con sequence e rle :

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

che dà il risultato voluto:

 > 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 

Se df$cat è una variabile fattore, è necessario prima as.character prima.

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

Ecco un’opzione che usa un ciclo for per gruppi piuttosto per righe (come OP ha fatto)

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

Vorrei aggiungere una variante data.table usando la funzione rank() che fornisce l’ulteriore possibilità di cambiare l’ordine e quindi rende un po ‘più flessibile della soluzione seq_len() ed è piuttosto simile alle funzioni row_number in 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),]