Dummifique la columna de caracteres y encuentre valores únicos

Tengo un dataframe con la siguiente estructura

test <- data.frame(col = c('a; ff; cc; rr;', 'rr; a; cc; e;')) 

Ahora quiero crear un dataframe a partir de este que contiene una columna con nombre para cada uno de los valores únicos en el dataframe de prueba. Un valor único es un valor que termina con el ‘;’ personaje y comenzando con un espacio, sin incluir el espacio. Luego, para cada una de las filas de la columna, deseo completar las columnas ficticias con un 1 o un 0. Como se indica a continuación

 data.frame(a = c(1,1), ff = c(1,0), cc = c(1,1), rr = c(1,0), e = c(0,1)) a ff cc rr e 1 1 1 1 1 0 2 1 0 1 1 1 

Intenté crear un df usando bucles for y los valores únicos en la columna, pero se está volviendo complicado. Tengo un vector disponible que contiene los valores únicos de la columna. El problema es cómo crear unos y ceros. Intenté alguna función mutate_all() con grep() pero esto no funcionó.

Utilizaría splitstackshape y mtabulate de los paquetes de qdapTools para obtener esto como un trazador de líneas, es decir,

 library(splitstackshape) library(qdapTools) mtabulate(as.data.frame(t(cSplit(test, 'col', sep = ';', 'wide')))) # a cc ff rr e #V1 1 1 1 1 0 #V2 1 1 0 1 1 

También puede ser splitstackshape como @A5C1D2H2I1M1N2O1R2T1 menciona en los comentarios,

 cSplit_e(test, "col", ";", mode = "binary", type = "character", fill = 0) 

Aquí hay una posible implementación de data.table . Primero dividimos las filas en columnas, fundimos en una sola columna y la extendimos mientras contamos los eventos para cada fila

 library(data.table) test2 <- setDT(test)[, tstrsplit(col, "; |;")] dcast(melt(test2, measure = names(test2)), rowid(variable) ~ value, length) # variable a cc e ff rr # 1: 1 1 1 0 1 1 # 2: 2 1 1 1 0 1 

Podemos hacer esto con tidyverse

 library(tidyverse) rownames_to_column(test, 'grp') %>% separate_rows(col) %>% filter(col!="") %>% count( grp, col) %>% spread(col, n, fill = 0) %>% ungroup() %>% select(-grp) # A tibble: 2 × 5 # a cc e ff rr #*      #1 1 1 0 1 1 #2 1 1 1 0 1 

Aquí hay una solución base R. Primero quita el espacio. Obtenga toda la combinación única. Dividir el dataframe real y luego verificar la presencia de él en los cols que tendrá todo el combo. Luego obtienes una matriz lógica que se puede convertir fácilmente en numérica.

 test=as.data.frame(apply(test,2,function(x)gsub('\\s+', '',x))) cols=unique(unlist(strsplit(as.character(test$col), split = ';'))) yy=strsplit(as.character(test$col), split = ';') z=as.data.frame(do.call.rbind(lapply(yy, function(x) cols %in% x))) names(z)=cols z=as.data.frame(lapply(z, as.integer)) 

Otro enfoque con tidytext y tidyverse

 library(tidyverse) library(tidytext) #for unnest_tokens() df <- test %>% unnest_tokens(word, col) %>% rownames_to_column(var="row") %>% mutate(row = floor(parse_number(row)), val = 1) %>% spread(word, val, fill = 0) %>% select(-row) df # a cc e ff rr #1 1 1 0 1 1 #2 1 1 1 0 1 

Aquí hay un enfoque de base R:

 x <- strsplit(as.character(test$col), ";\\s?") # split the strings lvl <- unique(unlist(x)) # get unique elements x <- lapply(x, factor, levels = lvl) # convert to factor t(sapply(x, table)) # count elements and transpose # a ff cc rr e #[1,] 1 1 1 1 0 #[2,] 1 0 1 1 1 

Otra solución simple sin paquetes adicionales:

 x = c('a; ff; cc; rr;', 'rr; a; cc; e;') G = lapply(strsplit(x,';'), trimws) dict = sort(unique(unlist(G))) do.call(rbind, lapply(G, function(g) 1*sapply(dict, function(d) d %in% g)))