Cut Cut Cut

¿Por qué cut3?
Programación
R
R package
Análisis de datos
Dmisc
Autor/a

Daniel E. de la Rosa

Fecha de publicación

11 de julio de 2023

library(Dmisc)

# Datos sintéticos para los ejemplos.
set.seed(123)
df <- data.frame(
  sex = rep(c('M', 'F'), each = 500),
  age = c(sample(20:60, 500, TRUE), sample(30:70, 500, TRUE))
)

Al trabajar con datos, es frecuente encontrarse con la necesidad de analizar variables numéricas. En ciertos casos, resulta suficiente emplear medidas estadísticas básicas como la media, la suma, el valor mínimo y máximo, y otras similares, especialmente cuando se desea examinar la relación de estas variables con variables categóricas.

En la situación en la que se requiera comparar dos variables numéricas, podemos recurrir a métricas como la correlación, la regresión, la covarianza, entre otras, para establecer una relación entre ambas. Sin embargo, a menudo surge la necesidad de convertir estas variables numéricas en categóricas para capturar y resaltar las diferencias entre distintos grupos demográficos o poblacionales.

Para este propósito, el lenguaje de programación estadística R proporciona la función cut. Según su documentación, cut es una función diseñada para convertir una variable numérica en un factor1.

Además, existen bibliotecas de terceros que expanden las capacidades de la función cut, añadiendo más funcionalidades. Un ejemplo de esto es la función cut3 del paquete Dmisc. A continuación, examinaremos cómo funcionan estas funciones y compararemos la utilidad y efectividad de cut3 con otras alternativas disponibles.

Nota

La primera y fundamental diferencia de cut3, con el resto de las funciones diseñadas para estos fines, radica en que la primera es una función diseñada para trabajar sobre un data.frame, mientras que las otras están diseñadas para trabajar con el vector numérico de interés.

En términos de ventajas, esto puede significar una mayor flexibilidad y eficiencia en algunos casos. En lugar de tener que aislar un vector numérico y trabajar con él individualmente, cut3 permite al usuario operar directamente en el data.frame completo. Esto puede ser particularmente útil en situaciones donde se necesita manipular o analizar múltiples columnas simultáneamente, como se verá más adelante.

Sin embargo, esta funcionalidad también presenta algunas desventajas. La principal es que cut3 sobrescribe la variable en cuestión dentro del data.frame, y no es intuitiva la forma de asignarla en una nueva variable.2

Cortes

El más importante argumento de estas funciones, después de los datos, es breaks. Este argumento consiste en una indicación de como se construyen los intervalos en la variable categórica resultante.

Un único número

Cuando el argumento breaks consiste de un único número (entero y mayor o igual a 2)3. Este se interpreta como el número de cortes que se deben realizar en la variable numérica cuando se convierte en categórica.

df2 <- df
df2$age <- cut(df2$age, breaks = 4)
table(df2)
   age
sex (19.9,32.5] (32.5,45] (45,57.5] (57.5,70]
  F          31       171       144       154
  M         155       159       150        36

En este caso, salvo un leve diferencia en términos de la sintaxis. El mismo resultado se pudiera lograr utilizando cut3.

df3 <- Dmisc::cut3(
  df, 
  var_name = 'age', 
  breaks = 4
)
table(df3)
   age
sex (19.9,32.5] (32.5,45] (45,57.5] (57.5,70]
  F          31       171       144       154
  M         155       159       150        36

Un vector numérico

Cuando el argumento breaks se pasa como un vector numérico, los valores contenidos en dicho vector se interpretan como los puntos de corte para la construcción de los intervalos en los que se va a dividir la variable en cuestión.

df2 <- df
df2$age <- cut(
  df2$age, 
  breaks = c(0, 20, 40, 60, 80, 100)
)
table(df2)
   age
sex (0,20] (20,40] (40,60] (60,80] (80,100]
  F      0     137     245     118        0
  M      5     235     260       0        0

Nuevamente, lo mismo se puede conseguir utilizando la función cut3().

df3 <- df
df3 <- cut3(
  df3, 
  var_name = "age", 
  breaks = c(0, 20, 40, 60, 80, 100)
)
table(df3)
   age
sex (0,20] (20,40] (40,60] (60,80] (80,100]
  F      0     137     245     118        0
  M      5     235     260       0        0

Es importante destacar que este vector de valores debe contener un valor inicial menor que el mínimo de la variable y un último valor mayor o igual al máximo de la variable. Esto se debe a la forma en que se construyen los intervalos.

Observa en la etiqueta de la variable creada (0,20] que el límite inferior del intervalo está abierto (, lo que significa que solo se incluyen valores mayores que 0. Puedes cambiar este comportamiento y hacer que el primer rango sea cerrado utilizando el argumento include.lowest. Mientras que el límite superior está cerrado ], lo que significa que se incluyen valores menores o iguales a 20.

Si el vector de valores no cumple con los criterios descritos anteriormente, los valores que no encajen en ningún intervalo se marcarán como NA en la variable resultante. Además, en el caso de la función cut3(), si el usuario establece el argumento .inf en True, los puntos de corte proporcionados se extenderán con -Inf e Inf. Esto significa que cualquier valor se incluirá en la variable resultante, incluso si está fuera del rango de los valores originales. Es una característica útil cuando no se conoce de antemano el valor máximo y mínimo de la variable y se desea incluir todos los valores sin tener que establecer límites específicos.

cut3(
  df, 
  var_name = "age", 
  breaks = c(0, 20, 40, 60, 80, 100)
) # Dejará NAs

cut3(
  df, 
  var_name = "age", 
  breaks = c(20, 40, 60, 80), 
  include.lowest = T
) # Dejará NAs

cut3(
  df, 
  var_name = "age", 
  breaks = c(20, 40, 60, 80), 
  .inf = F
) # No dejará NAs

Finalmente, es esencial que los valores de dicho vector sean únicos. Esto es fundamentalmente relevante cuando los puntos de corte no se asignan manualmente, sino que se utiliza alguna otra estrategia para los fines.

Funciones

Una de las principales novedades de cut3() en comparación con cut() es que el vector de breaks puede ser una función que genera el vector numérico de los cortes. El caso de uso más común para esta funcionalidad es quizás cuando se quieren utilizar cuantiles para dividir la variable.

Para estos propósitos, se debe utilizar el argumento bf_args en el que se especifican argumentos adicionales, los cuales deben pasarse a la función utilizada para construir los puntos de ruptura.

table(
  cut3(
    df, 
    var_name = "age", 
    breaks = quantile
  )
)
   age
sex (20,36] (36,45] (45,55] (55,70]
  F      76     126     126     172
  M     195     114     128      58

En el código de ejemplo anterior, la función cut3() se utiliza para dividir la variable “age” en el data.frame df en grupos o bins basados en cuantiles. Aquí, breaks se establece como quantile, lo que significa que los cuantiles de la distribución de “age” se utilizan para definir los puntos de ruptura.

Una característica interesante de este enfoque es que permite una mayor flexibilidad en la definición de los grupos. Por ejemplo, en lugar de dividir la variable en grupos de igual tamaño, puedes dividirla en grupos basados en la distribución de los datos. Esto puede ser particularmente útil cuando se trabaja con variables que tienen una distribución sesgada o que están altamente concentradas en ciertos rangos.

Además, al permitir que breaks sea una función, cut3() ofrece la posibilidad de generar puntos de ruptura de manera dinámica en función de los datos. Esto puede facilitar la creación de análisis más robustos y adaptables, ya que no es necesario definir los puntos de ruptura de antemano.

Por último, cabe mencionar que el argumento bf_args permite pasar argumentos adicionales a la función de breaks. Esto ofrece aún más flexibilidad, ya que puedes personalizar la función de breaks para adaptarse a tus necesidades específicas. Por ejemplo, podrías cambiar los cuantiles utilizados para dividir la variable, o podrías utilizar una función completamente diferente para generar los puntos de ruptura.

Dmisc::cut3_quantiles

El paquete Dmisc ya incluye la función cut3_quantile, que es una variante de cut3 diseñada específicamente para dividir una variable en cuantiles.

cut3_quantile toma un data.frame, una variable y un conjunto de probabilidades que definen los cuantiles. Si no se especifican las probabilidades, por defecto se utilizan el primer cuartil, la mediana y el tercer cuartil. Luego llama a cut3 con la función quantile de R como argumento para los puntos de ruptura.

table(
  cut3_quantile(
    df, 
    var_name = "age"
  )
)
   age
sex (-Inf,36] (36,45] (45,55] (55, Inf]
  F        76     126     126       172
  M       200     114     128        58

En el ejemplo de código anterior, cut3_quantile divide la variable “age” en el data.frame df en cuartiles.

Por lo tanto, si deseas dividir una variable en cuantiles, puedes utilizar directamente cut3_quantile en lugar de pasar quantile como argumento para breaks en cut3.

Grupos

Otra adición a cut3() consiste en poder definir diferentes cortes para diferentes grupos. Esto es especialmente útil cuando los puntos de ruptura se especifican con funciones.

table(
  cut3(
    df, 
    var_name = "age", 
    breaks = 4, 
    .inf = F
  )
)
   age
sex (19.9,32.5] (32.5,45] (45,57.5] (57.5,70]
  F          31       171       144       154
  M         155       159       150        36
table(
  cut3(
    df, 
    var_name = "age", 
    breaks = 4, 
    .inf = F, 
    groups = "sex"
  )
)
   age
sex (20,30] (30,40] (40,50] (50,60] (60,70]
  F       0     137     122     123     118
  M     134     106     141     119       0

En el ejemplo de código anterior, cut3() se utiliza para dividir la variable “age” en el data.frame tbl en dos grupos, pero los cortes se definen de manera separada para cada valor de la variable “sex”. Esto significa que los grupos generados para “age” serán diferentes para hombres y mujeres, lo cual puede ser muy útil en análisis que requieran tener en cuenta las diferencias entre grupos.

Esta característica de cut3() es muy poderosa, ya que permite adaptar los puntos de ruptura a las características específicas de cada grupo. En muchos casos, la distribución de una variable puede variar significativamente entre diferentes grupos, y usar los mismos puntos de ruptura para todos los grupos podría resultar en una representación inexacta de los datos.

Por ejemplo, imagina que estás analizando la edad de los participantes en un estudio y descubres que la distribución de las edades es muy diferente para hombres y mujeres. Si utilizas los mismos puntos de ruptura para ambos grupos, podrías terminar con bins que contienen muchas mujeres pero pocos hombres, o viceversa. Al permitir definir los puntos de ruptura por separado para cada grupo, cut3() te permite evitar este problema y asegurar una representación más precisa de los datos para cada grupo.

Además, esta función es particularmente útil cuando los puntos de ruptura se especifican con funciones, ya que estas funciones pueden adaptarse a las características específicas de cada grupo. Por ejemplo, podrías utilizar cuantiles para definir los puntos de ruptura, lo que aseguraría que los bins contienen aproximadamente la misma proporción de observaciones para cada grupo, independientemente de las diferencias en las distribuciones de los datos.

Ponderadores

TODO

Etiquetas

Como quizás ya habrás notado en los ejemplos anteriores, las etiquetas de los datos de la variable resultante se construyen utilizando la notación de intervalo correspondiente. Sin embargo, este comportamiento puede ser modificado proporcionando el argumento labels = F. En este caso, se utilizará un número autoincremental simple para nombrar los intervalos.

table(
  cut3(
    df, 
    var_name = "age", 
    breaks = 4, 
    labels = F
  )
)
   age
sex   1   2   3   4
  F  31 171 144 154
  M 155 159 150  36

Además, puedes proporcionar un vector especificando las etiquetas que quieres utilizar en la construcción de la variable. Las etiquetas se asignarán en el orden en que se especifican. Además, el número de etiquetas debe ser exactamente igual al número de bins resultantes.

table(
  cut3(
    df, 
    var_name = "age", 
    breaks = 4, 
    labels = c(
      "1 - 25 años", 
      "21 - 50 años", 
      "51 - 75 años", 
      "76 - 100 años"
    )
  )
)
   age
sex 1 - 25 años 21 - 50 años 51 - 75 años 76 - 100 años
  F          31          171          144           154
  M         155          159          150            36

Esto puede ser útil si deseas personalizar las etiquetas para hacerlas más descriptivas o más fáciles de entender. Por ejemplo, podrías usar etiquetas que describan las características de los individuos en cada grupo, o podrías usar etiquetas que sean más consistentes con la terminología utilizada en tu campo de estudio. Esto puede hacer que tus resultados sean más claros y más fáciles de interpretar, tanto para ti como para otras personas que puedan estar trabajando con tus datos.

Conclusión

La función cut3 del paquete Dmisc en R ofrece ventajas y desventajas. En comparación con funciones como cut, cut3 puede ofrecer mayor flexibilidad y eficiencia al trabajar directamente con data.frames en lugar de solo con vectores numéricos. Esto puede ser especialmente útil cuando se necesita manipular o analizar múltiples columnas a la vez.

Sin embargo, también presenta inconvenientes, como la sobrescritura de la variable original dentro del data.frame, y puede resultar menos intuitiva cuando se necesita asignar el resultado a una nueva variable. En términos de rendimiento, la elección entre cut y cut3 dependerá en gran medida del contexto y de las necesidades específicas de tu análisis.

Finalmente, siempre es recomendable entender las diferencias y peculiaridades de las distintas herramientas disponibles antes de tomar una decisión sobre qué método utilizar para convertir variables numéricas en categóricas.

Referencias

Notas

  1. En R, un factor es una variable de tipo categórica. Es similar a una variable de tipo texto (character), pero que solo toma un conjunto finito de valores (levels).↩︎

  2. Esto se puede lograr con solo crear una variable que sea copia de la original antes de hacer el corte.↩︎

  3. Cuando breaks se especifica de esta forma la variable se divide en segmentos de igual longitud. Para más detalles, véase la documentación en la función cut().↩︎

Reutilización

Cómo citar

BibTeX
@online{e. de la rosa2023,
  author = {E. de la Rosa, Daniel},
  title = {Cut Cut Cut},
  date = {2023-07-11},
  url = {https://adatar.do/blog/Dmisc/cut3.html},
  langid = {es}
}
Por favor, cita este trabajo como:
E. de la Rosa, Daniel. 2023. “Cut Cut Cut.” July 11, 2023. https://adatar.do/blog/Dmisc/cut3.html.