Numeración por grupos

Supongamos que tenemos la siguiente base de datos:

ID Shoot hit 1 10 2 1 9 3 1 8 1 2 10 8 2 8 8 2 11 10 2 7 2 3 9 2 4 6 6 4 6 5 . . 

Y me gustaría tenerlo con los números asignados en cada grupo, en este caso por ID, como por ejemplo:

 ID Shoot hit number.in.group 1 10 2 1 1 9 3 2 1 8 1 3 2 10 8 1 2 8 8 2 2 11 10 3 2 7 2 4 3 9 2 1 4 6 6 1 4 6 5 2 . . 

Podría hacerlo fácilmente usando un bucle. Algo como esto funcionaría:

 df$number.in.group = rep(1,nrow(df)) for(i in 2:nrow(df)) if(df$ID[i]==df$ID[i-1]){ df$number.in.group[i] = df$number.in.group[i-1] + 1 } 

Mi pregunta es, ¿hay alguna función o forma más elegante de hacer esto que no sea el uso de un bucle?

Usando dplyr

 dat <- data.frame(ID = rep(1:3, c(2, 3, 5)), val = rnorm(10)) library(dplyr) dat %>% group_by(ID) %>% mutate(number.in.group = 1:n()) 

Si quieres un trazador de líneas, algo así como

 df$number.in.group = unlist(lapply(table(df$ID),seq.int)) 

Podrías simplemente usar rle y sequence :

 dat <- read.table(text = "ID Shoot hit + 1 10 2 + 1 9 3 + 1 8 1 + 2 10 8 + 2 8 8 + 2 11 10 + 2 7 2 + 3 9 2 + 4 6 6 + 4 6 5",sep = "",header = TRUE) > sequence(rle(dat$ID)$lengths) [1] 1 2 3 1 2 3 4 1 1 2 

De hecho, creo que la sequence está destinada exactamente para este propósito.

 > dat$number.in.group <- ave(dat$ID,dat$ID, FUN=seq_along) > dat ID Shoot hit number.in.group 1 1 10 2 1 2 1 9 3 2 3 1 8 1 3 4 2 10 8 1 5 2 8 8 2 6 2 11 10 3 7 2 7 2 4 8 3 9 2 1 9 4 6 6 1 10 4 6 5 2 

Probablemente haya formas mejores, pero se podría utilizar tapply en los ID y agregar una función que devuelva una secuencia.

 # Example data dat <- data.frame(ID = rep(1:3, c(2, 3, 5)), val = rnorm(10)) # Using tapply with a function that returns a sequence dat$number.in.group <- unlist(tapply(dat$ID, dat$ID, function(x){seq(length(x))})) dat 

lo que resulta en

 > dat ID val number.in.group 1 1 -0.454652118 1 2 1 -2.391824247 2 3 2 0.530832021 1 4 2 -1.671043812 2 5 2 -0.045261549 3 6 3 2.311162484 1 7 3 -0.525635803 2 8 3 0.008588811 3 9 3 0.078942033 4 10 3 0.324156111 5 
 df$number.in.group <- unlist(lapply(as.vector(unlist(rle(df$ID)[1])), function(x) 1:x)) 

Aquí hay otra solución

 require(plyr) ddply(dat, .(ID), transform, num_in_grp = seq_along(hit)) 

Comparé tus informes y IShouldBuyABoat es el más prometedor. Encontré que la función ave podría aplicarse incluso si el conjunto de datos no está ordenado de acuerdo con la variable de agrupamiento.

Consideremos el conjunto de datos:

 dane<-data.frame(g1=c(-1,-2,-2,-2,-3,-3,-3,-3,-3), g2=c('reg','pl','reg','woj','woj','reg','woj','woj','woj')) 

Joran awser y aplicado a mi ejemplo:

 > sequence(rle(as.character(dane$g2))$lengths) [1] 1 1 1 1 2 1 1 2 3 

Proposición y resultados de Simon Urbanek:

 > unlist(lapply(table(dane$g2),seq.int)) pl reg1 reg2 reg3 woj1 woj2 woj3 woj4 woj5 1 1 2 3 1 2 3 4 5 

El código IShouldBuyABoat le da la respuesta correcta:

 > as.numeric(ave(as.character(dane$g1),as.character(dane$g1),FUN=seq_along)) [1] 1 1 2 3 1 2 3 4 5