Convierta columnas data.frame de factores a caracteres

Tengo un dataframe. Vamos a llamarlo 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 dataframe (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

Extrañamente puedo ver las columnas de bob a mano y hacer

 bob$phenotype <- as.character(bob$phenotype) 

que funciona bien Y, después de escribir algo, 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 factores en un data.frame con columnas de caracteres sin tener que pasar manualmente por cada columna?

Pregunta de bonificación: ¿por qué funciona el enfoque manual?

Siguiendo a Matt y Dirk. Si desea recrear su dataframe existente sin cambiar la opción global, puede volver a crearlo con una instrucción apply:

 bob < - data.frame(lapply(bob, as.character), stringsAsFactors=FALSE) 

Esto convertirá todas las variables en "carácter" de clase, si solo quiere convertir factores, consulte la solución de Marek a continuación .

Como señala @hadley, 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 utilizando as.data.frame con el argumento stringsAsFactors = FALSE .

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 

Package purrr de RStudio ofrece otra alternativa:

 library(purrr) library(dplyr) bob %>% map_if(is.factor, as.character) %>% as_data_frame -> bob 

(recuerde que es un paquete nuevo)

La opción global

stringsAsFactors: la configuración predeterminada para los argumentos de data.frame y read.table.

puede ser algo que desee configurar como FALSE en sus archivos de inicio (por ejemplo, ~ / .Rprofile). Por favor vea help(options) .

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 la aplicación para lograr esto. Lo cual no implica en absoluto que las soluciones de aplicación no funcionen bien.

Los factores están estructurados 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] abad Levels: abd > 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 levels() devuelve una matriz de caracteres. Puede usar este hecho para convertir de manera fácil y compacta factores a 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 as.numeric() 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 

Si desea una nueva bobc dataframe 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 quieres convertirlo de nuevo, puedes crear un vector lógico de qué columnas son factores y usarlo para aplicar el factor de forma selectiva.

 f < - sapply(bobf, class) == "factor" bobc[,f] <- lapply(bobc[,f], factor) 

Normalmente hago esta función como parte 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) } 

Otra forma es convertirlo usando aplicar

 bob2 < - apply(bob,2,as.character) 

Y uno mejor (el anterior es de clase 'matriz')

 bob2 < - as.data.frame(as.matrix(bob),stringsAsFactors=F) 

Actualización: Aquí hay un ejemplo de algo que no funciona. Pensé que sí, pero creo que la opción stringsAsFactors solo funciona en cadenas de caracteres: deja los factores en paz.

Prueba esto:

 bob2 < - data.frame(bob, stringsAsFactors = FALSE) 

En términos generales, cada vez que tenga problemas con factores que deberían ser caracteres, hay una configuración stringsAsFactors en algún lugar para ayudarlo (incluida una configuración global).

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 a todas las plagas de 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 meter los datos en un código como este, podría hacer la parte sapply separado (en realidad, es mucho más fácil hacerlo así), pero entiendes el punto ... No revisé el código '. porque no estoy en casa, ¡así que espero que funcione! =)

Este enfoque, sin embargo, tiene una desventaja ... debes reorganizar columnas después, mientras que con la transform puedes hacer lo que quieras, pero a costa de "escritura de código de estilo peatonal" ...

Entonces ahí ... =)

Al comienzo de su dataframe, incluya stringsAsFactors = FALSE para ignorar todos los malentendidos.

Si usa 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 columnas de factores en su conjunto de datos y quiere convertirlas a 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" 

Esto funciona para mí – finalmente pensé en un trazador de líneas

 df < - as.data.frame(lapply(df,function (y) if(class(y)=="factor" ) as.character(y) else y),stringsAsFactors=F) 

Esta función hace el truco

 df < - stacomirtools::killfactor(df)