numeração por grupos

Suponha que tenhamos o seguinte database:

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 . . 

E eu gostaria de tê-lo com números atribuídos em cada grupo, neste caso, por ID, como:

 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 . . 

Eu poderia fazer isso facilmente usando um loop. Algo como isso funcionaria:

 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 } 

Minha pergunta é, existe alguma function ou maneira mais elegante de fazer isso além de usar um loop?

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()) 

Se você quer um one-liner, algo como

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

Você poderia usar apenas rle e 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 fato, acho que a sequence é destinada exatamente a esse 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 

Existem provavelmente melhores maneiras, mas pode-se usar tapply nos IDs e lançar uma function que retorne uma sequência.

 # 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 

o que resulta em

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

Aqui está outra solução

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

Eu comparei seus anwsers e IShouldBuyABoat é o mais promissor. Descobri que a function ave poderia ser aplicada mesmo se o dataset não fosse classificado de acordo com a variável de agrupamento.

Vamos considerar o dataset:

 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 anwser e aplicado ao meu exemplo:

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

Proposição e 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 

O código IShouldBuyABoat fornece uma resposta correta:

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