¿Cómo concatenar factores sin que se conviertan a un nivel entero?

Me sorprendió ver que R forzará factores en un número al concatenar vectores. Esto sucede incluso cuando los niveles son los mismos. Por ejemplo:

> facs  facs [1] i want to be a factor not an integer Levels: a an be factor i integer not to want > c(facs[1 : 3], facs[4 : 5]) [1] 5 9 8 3 1 

¿Cuál es la manera idiomática de hacer esto en R (en mi caso, estos vectores pueden ser bastante grandes)? Gracias.

De la lista de correo de R :

 unlist(list(facs[1 : 3], facs[4 : 5])) 

Para ‘cbind’ factores, hacer

 data.frame(facs[1 : 3], facs[4 : 5]) 

Una solución alternativa consiste en convertir el factor en un vector de caracteres, luego convertir de nuevo cuando se concatena.

 cfacs <- as.character(facs) x <- c(cfacs[1:3], cfacs[4:5]) # Now choose between factor(x) # and factor(x, levels = levels(facs)) 

Wow, nunca me di cuenta de que eso sucedió. Aquí hay una solución alternativa:

 x <- c(facs[1 : 3], facs[4 : 5]) x <- factor(x, levels=1:nlevels(facs), labels=levels(facs)) x 

Con la salida:

 [1] i want to be a Levels: a an be factor i integer not to want 

Solo funcionará si los dos vectores tienen los mismos niveles que aquí.

Esta es una muy mala R gotcha. En ese sentido, aquí hay uno que simplemente tragó varias horas de mi tiempo.

 x <- factor(c("Yes","Yes","No", "No", "Yes", "No")) y <- c("Yes", x) > y [1] "Yes" "2" "2" "1" "1" "2" "1" > is.factor(y) [1] FALSE 

Me parece que la mejor solución es la de Richie, que coacciona al personaje.

 > y <- c("Yes", as.character(x)) > y [1] "Yes" "Yes" "Yes" "No" "No" "Yes" "No" > y <- as.factor(y) > y [1] Yes Yes Yes No No Yes No Levels: No Yes 

Siempre y cuando tengas los niveles establecidos correctamente, como Richie menciona.

Desde que se hizo esta pregunta, Hadley Wickham ha creado un paquete de forcats con una función fct_c diseñada para problemas como este.

 > library(forcats) > facs <- as.factor(c("i", "want", "to", "be", "a", "factor", "not", "an", "integer")) > fct_c(facs[1:3], facs[4:5]) [1] i want to be a Levels: a an be factor i integer not to want 

Como nota al fct_c , fct_c tampoco se deja engañar por la concatenación de factores que usan codificaciones numéricas discrepantes:

 > x <- as.factor(c('c', 'z')) > x [1] cz Levels: cz > y <- as.factor(c('a', 'b', 'z')) > y [1] abz Levels: abz > c(x, y) [1] 1 2 1 2 3 > fct_c(x, y) [1] czabz Levels: czab > as.numeric(fct_c(x, y)) [1] 1 2 3 4 2 

En función de las otras respuestas que usan conversión a carácter, estoy usando la siguiente función para concatenar factores:

 concat.factor <- function(...){ as.factor(do.call(c, lapply(list(...), as.character))) } 

Puede usar esta función del mismo modo que usaría c .

Aquí hay otra forma de agregar a una variable de factor cuando la configuración es ligeramente diferente:

 facs <- factor(1:3, levels=1:9, labels=c("i", "want", "to", "be", "a", "factor", "not", "an", "integer")) facs # [1] i want to be a factor not an integer # Levels: a an be factor i integer not to want facs[4:6] <- levels(facs)[4:6] facs # [1] i want to be a factor # Levels: i want to be a factor not an integer 

Por esta razón, prefiero trabajar con factores dentro de data.frames:

 df <- data.frame(facs = as.factor( c("i", "want", "to", "be", "a", "factor", "not", "an", "integer") )) 

y lo subconjunto usando subconjunto () o dplyr :: filter () etc. en lugar de índices de fila. Debido a que no tengo criterios de subconjuntos significativos en este caso, usaré head () y tail ():

 df1 <- head(df, 4) df2 <- tail(df, 2) 

Entonces puede manipularlos con bastante facilidad, por ejemplo:

 dfc <- rbind(df1, df2) dfc$facs #[1] i want to be an integer #Levels: a an be factor i integer not to want