r dummy-variable

Dummify columna de caracteres y encontrar valores únicos



dummy-variable (7)

Esta pregunta ya tiene una respuesta aquí:

Tengo un marco de datos con la siguiente estructura

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

Ahora quiero crear un marco de datos a partir de esto que contenga una columna con nombre para cada uno de los valores únicos en el marco de datos de prueba. Un valor único es un valor terminado por '';'' personaje y comenzando con un espacio, sin incluir el espacio. Luego, para cada una de las filas de la columna, deseo llenar 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 desordenado. Tengo un vector disponible que contiene los valores únicos de la columna. El problema es cómo crear los unos y los ceros. Intenté alguna función mutate_all() con grep() pero esto no funcionó.


Aquí hay un enfoque 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


Aquí hay una posible implementación de data.table . Primero dividimos las filas en columnas, las fundimos en una sola columna y las distribuimos a lo ancho 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


Aquí hay una solución base R. Primero quite el espacio. Obtén toda la combinación única. Divida el marco de datos real y luego verifique su presencia en los cols que tendrán 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))


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


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


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 #* <dbl> <dbl> <dbl> <dbl> <dbl> #1 1 1 0 1 1 #2 1 1 1 0 1


splitstackshape y mtabulate de los paquetes qdapTools para obtener esto como una línea, 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 una forma splitstackshape completa como @ A5C1D2H2I1M1N2O1R2T1 menciona en los comentarios,

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