R: divide la lista no balanceada en la columna data.frame

Supongamos que tiene un dataframe con la siguiente estructura:

df <- data.frame(a=c(1,2,3,4), b=c("job1;job2", "job1a", "job4;job5;job6", "job9;job10;job11")) 

donde la columna b es una lista delimitada por punto y coma (desequilibrada por fila). El data.frame ideal sería:

 id,job,jobNum 1,job1,1 1,job2,2 ... 3,job6,3 4,job9,1 4,job10,2 4,job11,3 

Tengo una solución parcial que toma casi 2 horas (170K filas):

 # Split the column by the semicolon. Results in a list. df$allJobs <- strsplit(df$b, ";", fixed=TRUE) # Function to reshape column that is a list as a data.frame simpleStack <- function(data){ start <- as.data.frame.list(data) names(start) <-c("id", "job") return(start) } # pylr! system.time(df2 <- ddply(df, .(id), simpleStack)) 

Parece ser un problema de tamaño, porque si ejecuto

 system.time(df2 <- ddply(df[1:4000,c("id", "allJobs")], .(id), simpleStack)) 

solo toma 9 segundos. La primera conversión a un conjunto de data.frames con sapply (con una función diferente) es rápida, pero el ‘rbind’ requerido demora aún más.

 #Split by ; as before allJobs <- strsplit(df$b, ";", fixed=TRUE) #Replicate a by the number of jobs in each case n <- sapply(allJobs, length) id <- rep(df$a, times = n) #Turn allJobs into a vector job <- unlist(allJobs) #Retrieve position of each job jobNum <- unlist(lapply(n, seq_len)) #Combine into a data frame df2 <- data.frame(id = id, job = job, jobNum = jobNum) 

cSplit de mi paquete “splitstacksahpe” está diseñado para manejar este tipo de manipulación de datos.

Aquí está en acción en esta pregunta:

 df <- data.frame(a=c(1,2,3,4), b=c("job1;job2", "job1a", "job4;job5;job6", "job9;job10;job11")) # install.packages("splitstackshape") library(splitstackshape) cSplit(df, "b", ";", "long", makeEqual = FALSE) # a b_new # 1: 1 job1 # 2: 1 job2 # 3: 2 job1a # 4: 3 job4 # 5: 3 job5 # 6: 3 job6 # 7: 4 job9 # 8: 4 job10 # 9: 4 job11 

También puede usar strsplit dentro de "dplyr", y luego seguir con unnest desde "tidyr", así:

 library(dplyr) library(tidyr) df %>% mutate(b = strsplit(as.character(b), ";", fixed = TRUE)) %>% unnest(b) # ab # 1 1 job1 # 2 1 job2 # 3 2 job1a # 4 3 job4 # 5 3 job5 # 6 3 job6 # 7 4 job9 # 8 4 job10 # 9 4 job11