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