insertar - Convertir columnas data.frame de factores a caracteres
data frame en r (13)
Al comienzo de su marco de datos, incluya stringsAsFactors = FALSE
para ignorar todos los malentendidos.
Tengo un marco de datos. Llamémosle bob
:
> head(bob)
phenotype exclusion
GSM399350 3- 4- 8- 25- 44+ 11b- 11c- 19- NK1.1- Gr1- TER119-
GSM399351 3- 4- 8- 25- 44+ 11b- 11c- 19- NK1.1- Gr1- TER119-
GSM399352 3- 4- 8- 25- 44+ 11b- 11c- 19- NK1.1- Gr1- TER119-
GSM399353 3- 4- 8- 25+ 44+ 11b- 11c- 19- NK1.1- Gr1- TER119-
GSM399354 3- 4- 8- 25+ 44+ 11b- 11c- 19- NK1.1- Gr1- TER119-
GSM399355 3- 4- 8- 25+ 44+ 11b- 11c- 19- NK1.1- Gr1- TER119-
Me gustaría concatenar las filas de este marco de datos (esta será otra pregunta). Pero mira:
> class(bob$phenotype)
[1] "factor"
Las columnas de Bob
son factores. Así por ejemplo:
> as.character(head(bob))
[1] "c(3, 3, 3, 6, 6, 6)" "c(3, 3, 3, 3, 3, 3)"
[3] "c(29, 29, 29, 30, 30, 30)"
No empiezo a entender esto, pero supongo que estos son índices en los niveles de los factores de las columnas (de la corte del rey caractaco) de bob
. No es lo que necesito.
Curiosamente puedo atravesar las columnas de bob
con la mano, y hacer
bob$phenotype <- as.character(bob$phenotype)
que funciona bien. Y, después de algunos tecleos, puedo obtener un data.frame cuyas columnas son caracteres en lugar de factores. Entonces mi pregunta es: ¿cómo puedo hacer esto automáticamente? ¿Cómo convierto un data.frame con columnas de factor en un data.frame con columnas de caracteres sin tener que pasar manualmente por cada columna?
Pregunta extra: ¿por qué funciona el enfoque manual?
Esta función hace el truco
df <- stacomirtools::killfactor(df)
Esto funciona para mí - finalmente me di cuenta de una sola línea
df <- as.data.frame(lapply(df,function (y) if(class(y)=="factor" ) as.character(y) else y),stringsAsFactors=F)
La opción global
stringsAsFactors: la configuración predeterminada para los argumentos de data.frame y read.table.
puede ser algo que desee establecer en FALSE
en sus archivos de inicio (por ejemplo, ~ / .Rprofile). Por favor vea help(options)
.
Normalmente hago esta función aparte de todos mis proyectos. Rapido y Facil.
unfactorize <- function(df){
for(i in which(sapply(df, class) == "factor")) df[[i]] = as.character(df[[i]])
return(df)
}
O puedes intentar transform
:
newbob <- transform(bob, phenotype = as.character(phenotype))
Solo asegúrate de poner todos los factores que quieras convertir en personaje.
O puedes hacer algo como esto y matar todas las plagas con un solo golpe:
newbob_char <- as.data.frame(lapply(bob[sapply(bob, is.factor)], as.character), stringsAsFactors = FALSE)
newbob_rest <- bob[!(sapply(bob, is.factor))]
newbob <- cbind(newbob_char, newbob_rest)
No es una buena idea empujar los datos en un código como este, podría hacer la parte de sapply
separado (en realidad, es mucho más fácil hacerlo así), pero entiendes el punto ... No he revisado el código. Porque no estoy en casa, así que espero que funcione! =)
Este enfoque, sin embargo, tiene un inconveniente ... debe reorganizar las columnas después, mientras que con la transform
puede hacer lo que quiera, pero al costo de "escritura de código de estilo peatonal" ...
Así que hay ... =)
Otra forma es convertirlo usando aplicar.
bob2 <- apply(bob,2,as.character)
Y una mejor (la anterior es de clase ''matriz'')
bob2 <- as.data.frame(as.matrix(bob),stringsAsFactors=F)
Para reemplazar solo los factores:
i <- sapply(bob, is.factor)
bob[i] <- lapply(bob[i], as.character)
En el paquete dplyr en la versión 0.5.0 se introdujo la nueva función mutate_if
:
library(dplyr)
bob %>% mutate_if(is.factor, as.character) -> bob
El paquete purrr de RStudio da otra alternativa:
library(purrr)
library(dplyr)
bob %>% map_if(is.factor, as.character) %>% as_data_frame -> bob
(ten en cuenta que es un paquete fresco)
Sé que esta respuesta es un poco tarde, pero si comprende cómo se almacenan los factores, puede evitar el uso de funciones basadas en aplicaciones para lograr esto. Lo que no implica en absoluto que las soluciones de aplicación no funcionen bien.
Los factores se estructuran como índices numéricos vinculados a una lista de "niveles". Esto se puede ver si convierte un factor a numérico. Asi que:
> fact <- as.factor(c("a","b","a","d")
> fact
[1] a b a d
Levels: a b d
> as.numeric(fact)
[1] 1 2 1 3
Los números devueltos en la última línea corresponden a los niveles del factor.
> levels(fact)
[1] "a" "b" "d"
Observe que los levels()
devuelven una matriz de caracteres. Puede utilizar este hecho para convertir de forma fácil y compacta factores en cadenas o números como este:
> fact_character <- levels(fact)[as.numeric(fact)]
> fact_character
[1] "a" "b" "a" "d"
Esto también funciona para valores numéricos, siempre que envuelva su expresión en as.numeric()
.
> num_fact <- factor(c(1,2,3,6,5,4))
> num_fact
[1] 1 2 3 6 5 4
Levels: 1 2 3 4 5 6
> num_num <- as.numeric(levels(num_fact)[as.numeric(num_fact)])
> num_num
[1] 1 2 3 6 5 4
Sólo seguí a Matt y Dirk. Si desea recrear su marco de datos existente sin cambiar la opción global, puede recrearlo con una declaración de aplicación:
bob <- data.frame(lapply(bob, as.character), stringsAsFactors=FALSE)
Esto convertirá todas las variables en "carácter" de clase, si solo desea convertir factores, consulte la solución de Marek a continuación .
Como @hadley señala, lo siguiente es más conciso.
bob[] <- lapply(bob, as.character)
En ambos casos, lapply
produce una lista; sin embargo, debido a las propiedades mágicas de R, el uso de []
en el segundo caso mantiene la clase data.frame del objeto bob
, eliminando así la necesidad de volver a convertir a un data.frame usando as.data.frame
con el argumento stringsAsFactors = FALSE
.
Si desea un nuevo cuadro de datos en bobc
donde cada vector de factor en bobf
se convierte en un vector de caracteres, intente esto:
bobc <- rapply(bobf, as.character, classes="factor", how="replace")
Si luego desea volver a convertirlo, puede crear un vector lógico de qué columnas son factores y utilizarlo para aplicar el factor de forma selectiva.
f <- sapply(bobf, class) == "factor"
bobc[,f] <- lapply(bobc[,f], factor)
Si utilizaría el paquete data.table
para las operaciones en data.frame, entonces el problema no está presente.
library(data.table)
dt = data.table(col1 = c("a","b","c"), col2 = 1:3)
sapply(dt, class)
# col1 col2
#"character" "integer"
Si ya tiene un factor de columnas en su conjunto de datos y desea convertirlas en caracteres, puede hacer lo siguiente.
library(data.table)
dt = data.table(col1 = factor(c("a","b","c")), col2 = 1:3)
sapply(dt, class)
# col1 col2
# "factor" "integer"
upd.cols = sapply(dt, is.factor)
dt[, names(dt)[upd.cols] := lapply(.SD, as.character), .SDcols = upd.cols]
sapply(dt, class)
# col1 col2
#"character" "integer"
Actualización: Aquí hay un ejemplo de algo que no funciona. Pensé que lo haría, pero creo que la opción stringsAsFactors solo funciona con cadenas de caracteres, deja los factores por sí solos.
Prueba esto:
bob2 <- data.frame(bob, stringsAsFactors = FALSE)
En general, siempre que tenga problemas con factores que deberían ser caracteres, hay una configuración de stringsAsFactors
en algún lugar para ayudarlo (incluida una configuración global).