Capítulo21 Factores
Fecha de la última revisión
## [1] "2026-09-09"
El tema proviene de los siguientes sitios. Aunque los ejemplos son distintos
English: https://r4ds.had.co.nz/factors.html
Español: https://es.r4ds.hadley.nz/15-factors.html
21.1 Temas
- Qué es un factor y en qué se diferencia de una variable de texto
- Crear factores:
factor(),parse_factor(),fct_inorder() - Ver y cambiar los niveles:
levels(),fct_relevel() - Reordenar para graficar:
fct_reorder(),fct_infreq(),fct_rev() - Agrupar categorías:
fct_recode(),fct_collapse(),fct_lump_n() - Las constantes que ya trae R:
letters,month.name,pi
21.2 ¿Qué es un factor?
Un factor es una variable categórica con una lista de valores permitidos, que se llaman niveles, y con un orden entre ellos. Por dentro R guarda números enteros (1, 2, 3) y una tabla que dice qué texto corresponde a cada número.
La pregunta razonable es por qué molestarse, si el texto ya funciona. Hay dos razones, y las dos aparecen en este capítulo:
- El orden. El texto se ordena alfabéticamente, siempre. Con los meses del año eso significa que abril va antes que enero, lo cual es absurdo y sin embargo es lo que hace cualquier gráfica si la variable es texto.
- Los valores permitidos. Con un factor, R sabe cuáles categorías existen aunque en los datos no aparezca ninguna. Si en tu muestra no hubo ningún individuo de la categoría “juvenil”, con texto esa categoría desaparece de la tabla y de la gráfica; con factor sigue ahí, con un cero, que es la información correcta.
Regla práctica: deja el texto como texto mientras estés limpiando datos, y conviértelo en factor cuando llegues a graficar o a modelar. Convertir demasiado pronto es la causa de la mitad de los enredos con factores.
21.3 El problema, en cuatro meses
El ejemplo que sigue tiene cuatro meses y cuatro valores. Fíjate desde ahora en
que month es texto (chr) y x es un número entero (int): esa diferencia
va a decidir cómo sale la gráfica.
## [1] "December" "January" "March" "April"
## chr [1:4] "December" "January" "March" "April"
## int [1:4] 1 2 3 4
Unir las dos listas. Ojo con cbind(): junta las columnas, pero como una de
ellas es texto, convierte todo a texto, incluidos los números. Es un caso
clásico y una razón más para preferir tibble() o data.frame().
## month valores x
## 1 December 7 1
## 2 January 3 2
## 3 March 2 3
## 4 April 10 4
21.4 ¿Qué ocurrió en el gráfico?

Los meses salieron en orden alfabético: April, December, January, March. No es un
error de ggplot(), es lo único que puede hacer con una variable de texto:
ordenarla como se ordenan las palabras. Para que salgan en el orden del calendario
hay que decirle a R cuál es ese orden, y eso es exactamente lo que hace un factor.
Ordenar los meses correctamente, usando month.name.
Hay variables ya descrita en R, para facilitar el uso de constantes
- LETTERS: Mayúscula
- letters: minúscula
- month.abb: abreviación de meses en Inglés
- month.name: nombre de meses en Inglés
- pi = el valor de pi
See this website for more details
https://datacornering.com/how-to-get-the-month-name-from-the-number-in-r/
## [1] "A" "B" "C" "D" "E" "F" "G" "H" "I" "J" "K" "L" "M" "N" "O" "P" "Q" "R" "S"
## [20] "T" "U" "V" "W" "X" "Y" "Z"
## [1] "a" "b" "c" "d" "e" "f" "g" "h" "i" "j" "k" "l" "m" "n" "o" "p" "q" "r" "s"
## [20] "t" "u" "v" "w" "x" "y" "z"
## [1] "Jan" "Feb" "Mar" "Apr" "May" "Jun" "Jul" "Aug" "Sep" "Oct" "Nov" "Dec"
## [1] "January" "February" "March" "April" "May" "June"
## [7] "July" "August" "September" "October" "November" "December"
## [1] 3.141593
El bloque siguiente es la receta completa, y vale la pena leerlo en tres pasos.
Primero la gráfica mal, en orden alfabético. Después una línea de verificación
que casi nadie escribe y que evita muchos problemas: all(df$Months %in% month.name) comprueba que todos los meses estén bien escritos antes de
convertirlos. Y por último factor(..., levels = month.name), que impone el
orden correcto, y la gráfica buena.
df <- data.frame(Months=c("January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November","December"),
Attacks=c(49, 70, 49, 44, 53, 35, 33, 36, 47, 40, 44, 32))
ggplot(df, aes(Months, Attacks))+
geom_point()
### validar el supuesto que tengo información escrita correctamente para cada mes de año en el dataframe
all(df$Months %in% month.name)## [1] TRUE
### convert to a factor, order defined by `month.name`.
df$Months <- factor(df$Months, levels=month.name)
ggplot(data=df, aes(x=Months, y=Attacks)) +
geom_point()
21.4.1 factor() y parse_factor()
Con factor(x, levels = ...) asignamos los niveles en el orden de la lista que
demos. Y aquí aparece la diferencia importante entre las dos funciones que crean
factores.
factor() (base R) y parse_factor() (readr): crear un factor.
- Las dos convierten un vector de texto en factor, con los niveles y el orden que les digas en
levels. - La diferencia: si un valor de los datos no está en la lista de niveles,
factor()lo convierte enNAen silencio, yparse_factor()produce una advertencia que lo nombra. - Cuál usar:
parse_factor()mientras estés revisando datos, porque te enseña las erratas.factor()cuando ya sabes que los datos están limpios.
En el bloque de abajo, x1 trae un mes inventado, "Dii", que no está en la
lista de niveles. Compara lo que hace cada función con él: las dos producen un
NA, pero solo una te avisa.
x1= c("Dic", "Ene", "Mar", "Dii") # Nota que esta lista tengo un mes que se llama "Dii"
niveles_meses=c("Ene", "Mar", "Dic")
y1 <-factor(x1, levels = niveles_meses)
y1## [1] Dic Ene Mar <NA>
## Levels: Ene Mar Dic
## [1] Dic Ene Mar <NA>
## attr(,"problems")
## # A tibble: 1 × 4
## row col expected actual
## <int> <int> <chr> <chr>
## 1 4 NA value in level set Dii
## Levels: Ene Mar Dic
Ese NA silencioso de factor() es de los errores más caros del curso. Si en una
columna de 5000 filas hay cincuenta escritas con una errata, factor() las
convierte en cincuenta faltantes y el análisis sigue como si nada. Compruébalo
siempre después de convertir: sum(is.na(mi_factor)).
Si quiere que los valores aparezcan en el orden que aparecen en la base de datos use unique(variable)
df_est=data.frame(Nombre_est=c("Monica", "Isabela", "Juan", "Robo", "Pepito"),
Nota_Final=c(99, 27, 89, 91, 27))
df_est## Nombre_est Nota_Final
## 1 Monica 99
## 2 Isabela 27
## 3 Juan 89
## 4 Robo 91
## 5 Pepito 27

df_est$fct1=factor(df_est$Nombre_est, levels = unique(df_est$Nombre_est))
ggplot(df_est, aes(fct1, Nota_Final))+
geom_point()
fct_inorder() del paquete forcats hace lo mismo que ese unique() pero
directamente sobre el factor, y se lee mejor dentro de una cadena de pipes:
## [1] Dic Ene Mar Dii
## Levels: Dic Ene Mar Dii
levels() es la función que hay que correr siempre que un factor se porte raro:
enseña la lista de niveles y, sobre todo, el orden en que están.
## [1] "Dic" "Ene" "Mar" "Dii"
21.5 El paquete forcats
- library(forcats)
forcats es el paquete del tidyverse dedicado a los factores (el nombre es un
anagrama de factors). Todas sus funciones empiezan por fct_, reciben el
factor como primer argumento y devuelven otro factor, así que encajan
directamente dentro de mutate() y de aes().
Los factores vienen del lenguaje S y en su momento fueron una buena idea por
una razón que hoy suena rara: guardar enteros en vez de texto ahorraba memoria,
que en los años ochenta era escasa. Durante décadas R convirtió automáticamente
todo el texto en factores al leer un archivo, lo que causó incontables dolores de
cabeza. En 2020, con R 4.0.0, ese comportamiento por fin cambió:
stringsAsFactors = FALSE pasó a ser el valor por defecto. forcats, de
Hadley Wickham (2016), había resuelto antes la otra mitad del problema: cómo
manipular los niveles sin pelear.
factor() y las funciones fct_* (forcats): manejar variables categóricas.
factor(x, levels = ...): define una variable categórica y el orden de sus niveles.fct_reorder(f, y): reordena los niveles según otra variable (ideal para gráficos).- Otras:
fct_infreq()(por frecuencia),fct_rev()(invertir),fct_relevel()(mover niveles a mano).
- sort()
- factor()
- parse_factor()
- unique()
- fact_inorder()
- levels()
21.5.2 Una figura pequeña a la derecha, con el texto alrededor

El bloque de arriba enseña dos cosas a la vez. La primera es la de siempre:
fct_reorder(estado_civil, n) ordena las barras por su tamaño, y la gráfica pasa
de ser una lista alfabética a contar algo. La segunda es de presentación: con las
opciones out.width y out.extra del bloque, la figura sale a la mitad de ancho
y flotando a la derecha, de manera que el texto la rodea en vez de quedarse
debajo. Es un recurso útil cuando la gráfica es un apoyo del argumento y no el
argumento entero.
Fíjate en el detalle de que fct_reorder() va dentro de mutate(), antes del
ggplot(). También se puede escribir directamente dentro de aes(), y en los
bloques siguientes se hace así; las dos maneras funcionan y conviene reconocer
las dos.
21.6 Haga un re-order por la cantidad de personas por religión
df2=encuesta %>%
count(religion)
df2 %>%
mutate(religion = fct_reorder(religion, n)) %>%
ggplot(aes(x = religion, y = n)) +
geom_col()+
theme(axis.text.x = element_text(angle = 45, vjust = 0.5, hjust=1))
21.7 Modificar el orden de los factores
Usa fct_reorder() para ordenar los niveles de un factor según otra variable (por ejemplo la frecuencia o la media). Los gráficos quedan mucho más fáciles de leer que con el orden alfabético por defecto.
- fct_reorder()
- !is.na( ) remover los “NA”
- fct_infreq()
- fct_rev()
- fct_recode()
- fct_collapse()
- fct_lump()
Conviene separarlas en dos grupos, porque hacen cosas distintas:
Reordenar los niveles (no cambian los datos, solo el orden en que se enseñan).
fct_reorder(f, y): ordena los niveles defsegún la mediana dey. Es la que más se usa: convierte una gráfica de barras alfabética en una gráfica que se lee sola.fct_reorder2(f, x, y): para gráficas de líneas, ordena la leyenda según el valor deyen elxmás grande, es decir, según el orden en que las líneas terminan.fct_infreq(f): ordena por frecuencia, de la categoría más común a la menos común.fct_rev(f): invierte el orden actual.fct_relevel(f, "algo"): mueve a mano uno o varios niveles al principio.
Agrupar o renombrar niveles (estos sí cambian los datos).
fct_recode(f, nuevo = "viejo"): cambia el nombre de un nivel. Si varios niveles reciben el mismo nombre nuevo, quedan fundidos en uno.fct_collapse(f, grupo = c("a", "b", "c")): junta varios niveles en uno, y es más cómodo quefct_recode()cuando son muchos.fct_lump_n(f, n = 5): conserva losnniveles más frecuentes y mete todo lo demás en una categoría “otros”. Útil cuando una variable tiene cuarenta categorías y treinta y cinco tienen dos observaciones cada una.
fct_recode(), fct_collapse() y fct_lump_n() cambian los datos, no solo
su presentación. Agrupar categorías es una decisión de análisis, no de estética:
di siempre en el texto del informe cuáles categorías juntaste y por qué.
Un ejemplo de las tres últimas, con la religión de la encuesta:
encuesta |>
mutate(religion_corta = fct_lump_n(religion, n = 5, other_level = "Otras")) |>
count(religion_corta, sort = TRUE)## # A tibble: 6 × 2
## religion_corta n
## <fct> <int>
## 1 Protestante 10846
## 2 Católica 5124
## 3 Ninguna 3523
## 4 Otras 913
## 5 Cristiana 689
## 6 Judía 388
Y fct_recode(), que cambia el nombre de un nivel. Aquí le cambiamos el nombre al
nivel "Otras" que acabamos de crear nosotros mismos:
encuesta |>
mutate(
religion_corta = fct_lump_n(religion, n = 5, other_level = "Otras"),
religion_corta = fct_recode(religion_corta, "Otras religiones" = "Otras")
) |>
count(religion_corta, sort = TRUE)## # A tibble: 6 × 2
## religion_corta n
## <fct> <int>
## 1 Protestante 10846
## 2 Católica 5124
## 3 Ninguna 3523
## 4 Otras religiones 913
## 5 Cristiana 689
## 6 Judía 388
En fct_recode(), el nombre nuevo va a la izquierda del igual y el viejo a
la derecha, que es al revés de lo que casi todo el mundo escribe la primera vez.
Y si el nivel viejo no existe, no da error: da una advertencia (Unknown levels in f) y devuelve el factor sin tocar. Con warning=FALSE en las opciones del
bloque, esa advertencia no se ve y uno se queda creyendo que renombró algo.
Comprueba siempre con levels() después de renombrar.
Y fct_infreq() con fct_rev(), que juntas son la receta habitual para una
gráfica de barras horizontales bien ordenada:
encuesta |>
mutate(estado_civil = fct_rev(fct_infreq(estado_civil))) |>
ggplot(aes(y = estado_civil)) +
geom_bar()
#model=lm(y~x, df)
#summary(model)
resumen_religion <- encuesta %>%
group_by(religion) %>%
summarise(
edad = mean(edad, na.rm = TRUE),
horas_tv = mean(horas_tv, na.rm = TRUE),
sd_edad = sd(edad, na.rm = TRUE),
n = n()
)
resumen_religion## # A tibble: 15 × 5
## religion edad horas_tv sd_edad n
## <fct> <dbl> <dbl> <dbl> <int>
## 1 Sin respuesta 49.5 2.72 NA 93
## 2 No sabe 35.9 4.62 NA 15
## 3 Inter o no confesional 40.0 2.87 NA 109
## 4 Nativa americana 38.9 3.46 NA 23
## 5 Cristiana 40.1 2.79 NA 689
## 6 Cristiana ortodoxa 50.4 2.42 NA 95
## 7 Musulmana/Islam 37.6 2.44 NA 104
## 8 Otra religión oriental 45.9 1.67 NA 32
## 9 Hinduismo 37.7 1.89 NA 71
## 10 Budismo 44.7 2.38 NA 147
## 11 Otra 41.0 2.73 NA 224
## 12 Ninguna 41.2 2.71 NA 3523
## 13 Judía 52.4 2.52 NA 388
## 14 Católica 46.9 2.96 NA 5124
## 15 Protestante 49.9 3.15 NA 10846

## [1] 0
Esta primera gráfica está en orden alfabético inverso, que es el que traía el factor, y por eso los puntos parecen repartidos al azar. Compárala con la siguiente.
21.7.1 Nota el efecto de añadir un “-” antes de edad
fct_reorder(religion, edad) ordena de menor a mayor; con el signo menos delante,
fct_reorder(religion, -edad), el orden se invierte. Es el truco más corto para
darle la vuelta a una gráfica, y funciona solo con variables numéricas. Para
cualquier otro caso está fct_rev().

resumen_religion %>%
mutate(religion = fct_reorder(religion, horas_tv)) %>%
ggplot(aes(horas_tv, religion)) +
geom_point()
21.7.2 fct_reorder() y fct_reorder2()
fct_reorder() sirve cuando el factor va en una posición, es decir, en uno de
los dos ejes: es el caso de todas las gráficas de barras y de puntos de este
capítulo.
fct_reorder2() sirve cuando el factor va en un aspecto que no es posición,
típicamente el color de una gráfica de líneas. Ordena la leyenda según el valor
de y en el x más grande, o sea, según el orden en que las líneas terminan a
la derecha. El resultado es que la leyenda y las líneas coinciden de arriba
abajo, y el lector deja de tener que ir y venir entre las dos.
resumen_ingreso <- encuesta %>%
group_by(ingreso) %>%
summarise(
edad = mean(edad, na.rm = TRUE),
horas_tv = mean(horas_tv, na.rm = TRUE),
n = n()
)
resumen_ingreso## # A tibble: 16 × 4
## ingreso edad horas_tv n
## <fct> <dbl> <dbl> <int>
## 1 Sin respuesta 45.5 2.90 183
## 2 No sabe 45.6 3.41 267
## 3 Se niega a responder 47.6 2.48 975
## 4 25000 o más 44.2 2.23 7363
## 5 20000 - 24999 41.5 2.78 1283
## 6 15000 - 19999 40.0 2.91 1048
## 7 10000 - 14999 41.1 3.02 1168
## 8 8000 - 9999 41.1 3.15 340
## 9 7000 - 7999 38.2 2.65 188
## 10 6000 - 6999 40.3 3.17 215
## 11 5000 - 5999 37.8 3.16 227
## 12 4000 - 4999 38.9 3.15 226
## 13 3000 - 3999 37.8 3.31 276
## 14 1000 - 2999 34.5 3.00 395
## 15 Menos de 1000 40.5 3.36 286
## 16 No aplica 56.1 3.79 7043

21.7.3 Reordenar a mano: fct_relevel()
A veces el orden que uno quiere no sale de ningún cálculo. Un caso muy común: mover al final las categorías que no son categorías de verdad, como “No aplica”, “No sé” o “Sin respuesta”, para que no estorben en el medio de la gráfica.
fct_relevel() es una generalización de stats::relevel() que permite mover
cualquier número de niveles a cualquier posición.

21.8 Ejercicios del libro R4DS
Hacer los ejercicios de las secciones 15.3.1 y 15.5.1 del libro en español.
21.9 Ejercicios del capítulo
Sobre los ejercicios de este capítulo. Cada ejercicio se entrega con tres
cosas: el script, el resultado (que la tabla o la gráfica se vean) y una
explicación en palabras. Los ejercicios usan millas del paquete datos, no
encuesta, que es el conjunto con el que se enseña aquí. La hoja completa para
entregar está en Ejercicios/Ejercicios_Capitulo_21_factores.Rmd.
21.9.1 Ejercicio 1. Texto contra factor
Datos: millas. Comprueba con class() que clase es texto. Haz una gráfica de
barras. Después conviértela en factor con un orden que tenga sentido para ti (por
ejemplo del vehículo más pequeño al más grande) y repite la gráfica.
Verificación: levels() debe devolver 7 niveles en el orden que tú
escogiste, y la gráfica debe seguir ese orden.
En palabras: ¿en qué orden salían las barras antes? ¿Por qué ese orden no lo escogió nadie?
21.9.2 Ejercicio 2. fct_reorder() para que la gráfica hable
Datos: millas. Haz un diagrama de puntos del consumo mediano en autopista
para cada clase, primero sin ordenar y después con
fct_reorder(clase, autopista).
Verificación: en la versión ordenada, pickup debe quedar en un extremo y
compact o subcompact en el otro.
En palabras: las dos gráficas tienen exactamente los mismos datos. ¿Qué se ve en la segunda que no se veía en la primera? ¿Por qué el orden alfabético es casi siempre el peor de los órdenes posibles?
21.9.3 Ejercicio 3. Ordenar por frecuencia
Datos: millas, variable fabricante. Haz una gráfica de barras horizontales
usando fct_infreq() y fct_rev().
Verificación: la barra más larga tiene que quedar arriba del todo.
En palabras: prueba a quitar fct_rev() y mira qué pasa. Explica por qué en
barras horizontales hace falta y en barras verticales no.
21.9.4 Ejercicio 4. Cuando hay demasiadas categorías
Datos: millas. Averigua cuántos fabricantes distintos hay. Después usa
fct_lump_n() para quedarte con los 5 más frecuentes y meter el resto en una
categoría llamada "Otros".
Verificación: el factor nuevo debe tener exactamente 6 niveles, y la suma de los conteos debe seguir dando 234.
En palabras: ¿cuántos vehículos acabaron en "Otros"? Esta función cambia
los datos, no solo la gráfica. ¿En qué situación sería deshonesto usarla sin
decirlo?
21.9.5 Ejercicio 5. Agrupar a mano
Datos: millas. Con fct_collapse(), agrupa las siete clases en dos categorías
que tengan sentido para ti (por ejemplo vehículos grandes y vehículos pequeños).
Después compara el consumo en autopista entre los dos grupos.
Verificación: el factor nuevo tiene 2 niveles y la suma de los conteos sigue dando 234.
En palabras: justifica tu agrupación. ¿Qué información se perdió al pasar de siete categorías a dos? ¿Habrías llegado a la misma conclusión con las siete?
21.9.6 Ejercicio 6. El nivel que sigue ahí aunque no haya datos
Convierte clase en factor. Filtra fuera todos los 2seater. Después mira
levels(), count(clase) y count(clase, .drop = FALSE).
Verificación: levels() sigue devolviendo 7 niveles, count() devuelve
6 filas, y count(clase, .drop = FALSE) devuelve 7, la última con un
cero. Con fct_drop(), levels() baja a 6.
En palabras: las tres respuestas son distintas y las tres son correctas. Explica cuándo querrías ver el cero y cuándo no. Piensa en una especie que buscaste en el campo y no encontraste.
21.9.7 Ejercicio 7 (BONO). La errata que se convierte en NA
Haz una copia de millas y cámbiale a mano la clase de tres vehículos a
"compacto" (en español, que no es un nivel válido). Conviértela en factor de
dos maneras: con factor(..., levels = ...) usando las siete clases correctas, y
con parse_factor().
Verificación: las dos producen exactamente 3 NA, pero solo una de ellas
te avisa.
En palabras: copia la advertencia de parse_factor(). Si esto pasara en una
columna de 5000 filas de datos de campo, ¿cuál de las dos funciones querrías
haber usado, y qué línea correrías después para estar seguro?
