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)