Categorizar variables en R

Manipulación de datos con R Transformación de datos
Categoriza datos en R con la función cut

La función cut permite cortar datos en grupos y especificar ‘etiquetas de corte’, por lo que es muy útil para crear un factor a partir de una variable continua. En este tutorial aprenderás cómo usar la función cut en R y, por lo tanto, cómo categorizar variables en R.

La función cut() para categorizar variables

En ocasiones resulta útil categorizar los valores de una variable continua en diferentes grupos. Para ese propósito puedes usar la función cut de R. En el siguiente bloque de código mostramos la sintaxis de la función y la descripción simplificada de los argumentos.

cut(num_vector,              # Vector de entrada (numérico)
    breaks,                  # Número o vector con los cortes
    labels = NULL,           # Etiquetas para cada grupo
    include.lowest = FALSE,  # Si se incluye el valor más pequeño o no en el primer intervalo
    right = TRUE,            # Si el intervalo derecho está cerrado (y el izquierdo abierto) o viceversa
    dig.lab = 3,             # Número de dígitos de los grupos si labels = NULL
    ordered_result = FALSE,  # Si se debería ordenar el resultado (TRUE) del factor o no (FALSE)
    …)                       # Argumentos adicionales

Para más información sobre la función base para categorizar variables en R escribe help(cut) o ?cut en la consola de R o RStudio.

El argumento breaks

El argumento breaks permite cortar los datos en subgrupos y, por lo tanto, clasificarlos. Considera el siguiente vector:

x <- -5:5

Por un lado, puedes establecer el argumento breaks como un número entero, creando tantos intervalos (niveles) como los especificados. Estos intervalos serán todos de la misma longitud.

cut(x, breaks = 2)
(-5.01,0] (-5.01,0] (-5.01,0] (-5.01,0] (-5.01,0]
(-5.01,0] (0,5.01] (0,5.01]  (0,5.01]  (0,5.01]  (0,5.01] 
Levels: (-5.01,0] (0,5.01]

Por otro lado, también puedes especificar los intervalos que prefieras.

cut(x, breaks = c(-6, 2, 5))
(-6,2] (-6,2] (-6,2] (-6,2] (-6,2] (-6,2] (-6,2] (-6,2] (2,5]  (2,5] 
(2,5] 
Levels: (-6,2] (2,5]

Cabe destacar que si los intervalos tienen decimales, puedes modificar el número de decimales con el argumento dig.lab y decidir si ordenar o no los resultados con el argumento ordered_result.

El argumento labels

También puedes cambiar los niveles del factor con el argumento labels.

x <- c(12, 1, 25, 12, 65, 2, 6, 17)

cut(x, breaks = c(0, 3, 12, 15, 20, 80),
    labels = c("Primero", "Segundo", "Tercero", "Cuarto", "Quinto"))

# Equivalente a
c <- cut(x, breaks = c(0, 3, 12, 15, 20, 80))
levels(c) <- c("Primero", "Segundo", "Tercero", "Cuarto", "Quinto")
Segundo  Primero  Quinto  Segundo Quinto  Primero  Segundo  Cuarto 
Levels: Primero Segundo Tercero Cuarto Quinto

Incluír el valor más pequeño

El argumento include.lowest especifica si el lado izquierdo del primer intervalo es abierto o cerrado. Por defecto se establece en FALSE (abierto).

x <- 15:25

cut(x, breaks = c(15, 20, 25), include.lowest = FALSE)
<NA> (15,20] (15,20] (15,20] (15,20]
(15,20] (20,25] (20,25] (20,25] (20,25] (20,25]
Levels: (15,20] (20,25]

En este caso, el valor más bajo (15), no se incluye en el intervalo (el intervalo izquierdo está abierto), por lo que el valor se clasifica como NA, porque el número 15 no pertenece a ninguno de los intervalos. Sin embargo, si estableces include.lowest como TRUE se incluirá el valor, ya que se cerrará el lado izquierdo del primer intervalo.

cut(x, breaks = c(15, 20, 25), include.lowest = TRUE)
[15,20] [15,20] [15,20] [15,20] [15,20] 
[15,20] (20,25] (20,25] (20,25] (20,25] (20,25]
Levels: [15,20] (20,25]

El argumento right

Considera el caso en el que que quieres clasificar algunos datos (\(x\)) en las siguientes categorías:

  • Bajo, si \(x \in\) [0, 150).
  • Medio, si \(x \in\) [150, 200).
  • Alto, si \(x \in\) [200, $ $).

Por defecto, el argumento right está establecido como TRUE, por lo que los intervalos están abiertos a la izquierda y cerrados a la derecha (x, y].

x <- c(75, 150, 160, 151, 216, 149)

categorias <- cut(x, breaks = c(0, 150, 200, Inf),
                  labels = c("bajo", "medio", "alto"))

data.frame(x, categorias)

En este escenario, no todos los valores se clasifican bien.

  x   categorias
  75       bajo
 150       bajo   # <-- Categorizado como bajo
 160      medio
 151      medio 
 216       alto
 149       bajo

Sin embargo, si estableces right = FALSE, los intervalos se cerrarán a la izquierda y se abrirán a la derecha.

categories <- cut(x, breaks = c(0, 150, 200, Inf),
                  labels = c("bajo", "medio", "alto"),
                  right = FALSE)

data.frame(x, categories)

Ahora los datos estarán categorizados correctamente:

  x    categorias
  75       bajo
 150      medio   # <-- Categorizado como medio
 160      medio
 151      medio
 216       alto
 149       bajo

Cambiar los argumentos right e include.lowest puede provocar errores, por lo que recomendamos cambiar los valores del argumento breaks en lugar de los demás.

Ejemplo: ¿Cómo categorizar grupos de edad en R?

Considera que deseas clasificar un vector numérico de edades en las siguientes categorías:

  • 0-14: Niño.
  • 15-24: Joven.
  • 25-64: Adulto.
  • 65 y más: Mayor.
edad <- c(0, 12, 89, 14, 25, 2, 65, 1, 16, 24, 67, 61, 64)

A primera vista, podrías pensar en poner lo siguiente, pero así surgirá un error.

cut(edad, breaks = c(14, 24, 64, Inf),
    labels = c("Niño", "Joven", "Adulto", "Mayor"))

Error in cut.default(edad, breaks = c(14, 24, 64, Inf), labels = c(“Niño”,: lengths of ‘breaks’ and ‘labels’ differ

No obstante, al especificar 4 valores de corte y 4 etiquetas, teniendo en cuenta que los cortes definen intervalos, los intervalos que generas son tres (14-24, 24-64 y 64-Inf). En consecuencia, deberás agregar en este caso el valor más bajo para tener cuatro intervalos en lugar de tres:

cut(edad, breaks = c(0, 14, 24, 64, Inf),
    labels = c("Niño", "Joven", "Adulto", "Mayor"))
NA  Niño  Mayor  Niño  Adulto  Niño  Mayor  Niño  Joven  Joven 
Mayor  Adulto  Adulto
Levels: Niño Joven Adulto Mayor

Sin embargo, la edad más baja (0) se clasificará como NA, ya que el mínimo de los intervalos no se incluye por defecto. Puedes resolver esto cambiando el 0 de los cortes (por ejemplo, poniendo -0.01 en lugar de 0) o estableciendo el argumento include.lowest como TRUE.

cut(edad, breaks = c(-0.01, 14, 24, 64, Inf),
    labels = c("Niño", "Joven", "Adulto", "Mayor"))

# Equivalente a:
cut(edad, breaks = c(0, 14, 24, 64, Inf),
    labels = c("Niño", "Joven", "Adulto", "Mayor"),
    include.lowest = TRUE)
Niño  Niño  Mayor  Niño  Adulto  Niño  Mayor  Niño  Joven  Joven 
Mayor  Adulto  Adulto
Levels: Niño Joven Adulto Mayor

Ejemplo: ¿Cómo categorizar notas de exámenes en R?

Revisaremos ahora otro ejemplo. Las notas de exámenes son variables continuas que se pueden clasificar como suspensas, si la nota es inferior a 5 puntos sobre 10 o aprobadas en otro caso. Generaremos un conjunto de datos muy sencillo para clasificar las calificaciones de los exámenes.

notas <- c(6.1, 5.3, 8.9, 5.0, 8.8, 1.9, 6.6, 7.2, 9.4, 4.9,
           7.1, 3.9, 1.0, 9.3, 9.9, 5.9, 5.1, 8.4, 3.2, 10.0)

En este ejemplo podrías implementar la función como sigue:

notas_categorizadas <- cut(notas, breaks = c(0, 4.9, 10),
                           labels = c("suspenso", "aprobado"))

# Equivalente a:
# notas_categorizadas <- cut(notas, breaks = c(0, 5, 10.1),
#                            labels = c("suspenso", "aprobado"), right = FALSE)

# También puedes especificar los niveles de los factores con la función levels
# levels(notas_categorizadas) <- c("suspenso", "aprobado")

# Generamos un data frame
notas_finales <- data.frame(notas, notas_categorizadas)
head(notas_finales)

Ten en cuenta que en el método alternativo equivalente establecemos right = FALSE, porque si fuese TRUE un 5 sería un suspenso en lugar de un aprobado. Sin embargo, al establecer este argumento como FALSE, el lado derecho del intervalo estará abierto, por lo que un 10 no entraría en ningún intervalo y esa es la razón por la que establecemos el tercer corte en 10.1 en lugar de 10. El resultado final es el siguiente:

    notas        notas_categorizadas
1     6.1             aprobado
2     5.3             aprobado
3     8.9             aprobado
4     5.0             aprobado
5     8.8             aprobado
6     1.9             suspenso