Cree un número secuencial (contador) para filas dentro de cada grupo de un dataframe

¿Cómo podemos generar números únicos de identificación dentro de cada grupo de un dataframe? Aquí hay algunos datos agrupados por “personid”:

personid date measurement 1 x 23 1 x 32 2 y 21 3 x 23 3 z 23 3 y 23 

Deseo agregar una columna de identificación con un valor único para cada fila dentro de cada subconjunto definido por “personid”, siempre comenzando con 1 . Este es mi resultado deseado:

 personid date measurement id 1 x 23 1 1 x 32 2 2 y 21 1 3 x 23 1 3 z 23 2 3 y 23 3 

Agradezco cualquier ayuda.

La función engañosamente llamada ave() , con el argumento FUN=seq_along , logrará esto muy bien, incluso si su columna personid no está estrictamente ordenada.

 df <- read.table(text = "personid date measurement 1 x 23 1 x 32 2 y 21 3 x 23 3 z 23 3 y 23", header=TRUE) ## First with your data.frame ave(df$personid, df$personid, FUN=seq_along) # [1] 1 2 1 1 2 3 ## Then with another, in which personid is *not* in order df2 <- df[c(2:6, 1),] ave(df2$personid, df2$personid, FUN=seq_along) # [1] 1 1 1 2 3 2 

Algunas alternativas dplyr , que usan funciones de conveniencia row_number y n .

 library(dplyr) df %>% group_by(personid) %>% mutate(id = row_number()) df %>% group_by(personid) %>% mutate(id = 1:n()) df %>% group_by(personid) %>% mutate(id = seq_len(n())) df %>% group_by(personid) %>% mutate(id = seq_along(personid)) 

También puede usar getanID desde el paquete splitstackshape . Tenga en cuenta que el conjunto de datos de entrada se devuelve como una data.table .

 getanID(data = df, id.vars = "personid") # personid date measurement .id # 1: 1 x 23 1 # 2: 1 x 32 2 # 3: 2 y 21 1 # 4: 3 x 23 1 # 5: 3 z 23 2 # 6: 3 y 23 3 

Usando data.table , y suponiendo que desea ordenar por date dentro del subconjunto personid

 library(data.table) DT <- data.table(Data) DT[,id := order(date), by = personid] ## personid date measurement id ## 1: 1 x 23 1 ## 2: 1 x 32 2 ## 3: 2 y 21 1 ## 4: 3 x 23 1 ## 5: 3 z 23 3 ## 6: 3 y 23 2 

Si lo desea, no desea ordenar por date

 DT[, id := 1:.N, by = personid] ## personid date measurement id ## 1: 1 x 23 1 ## 2: 1 x 32 2 ## 3: 2 y 21 1 ## 4: 3 x 23 1 ## 5: 3 z 23 2 ## 6: 3 y 23 3 

Cualquiera de los siguientes también funcionaría

 DT[, id := seq_along(measurement), by = personid] DT[, id := seq_along(date), by = personid] 

Los comandos equivalentes usando plyr

 library(plyr) # ordering by date ddply(Data, .(personid), mutate, id = order(date)) # in original order ddply(Data, .(personid), mutate, id = seq_along(date)) ddply(Data, .(personid), mutate, id = seq_along(measurement)) 

Creo que hay un comando enlatado para esto, pero no puedo recordarlo. Así que aquí hay una forma:

 > test <- sample(letters[1:3],10,replace=TRUE) > cumsum(duplicated(test)) [1] 0 0 1 1 2 3 4 5 6 7 > cumsum(duplicated(test))+1 [1] 1 1 2 2 3 4 5 6 7 8 

Esto funciona porque duplicated devuelve un vector lógico. cumsum vectores numéricos, por lo que el lógico se ve forzado a ser numérico.

Puede almacenar el resultado en su data.frame como una nueva columna si lo desea:

 dat$id <- cumsum(duplicated(test))+1 

Suponiendo que sus datos están en un data.frame llamado Data , esto hará el truco:

 # ensure Data is in the correct order Data <- Data[order(Data$personid),] # tabulate() calculates the number of each personid # sequence() creates a n-length vector for each element in the input, # and concatenates the result Data$id <- sequence(tabulate(Data$personid)) 

Puedes usar sqldf

 df<-read.table(header=T,text="personid date measurement 1 x 23 1 x 32 2 y 21 3 x 23 3 z 23 3 y 23") library(sqldf) sqldf("SELECT a.*, COUNT(*) count FROM df a, df b WHERE a.personid = b.personid AND b.ROWID <= a.ROWID GROUP BY a.ROWID" ) # personid date measurement count #1 1 x 23 1 #2 1 x 32 2 #3 2 y 21 1 #4 3 x 23 1 #5 3 z 23 2 #6 3 y 23 3