forcatsEn el trabajo con variables categóricas es importante poder manipular las etiquetas de las categorías o niveles de la variable. A propóstio de esto, en esta sesión aprenderemos a:
Para la mayoría de actividades usaremos comandos de forcats un paquete del tidyverse especializado en manipular factores.
En este recurso usaremos el tidyverse, principalmente el comando mutate (del paquete dplyr), algunos comandos del paquete forcats y el paquete ggplot2 para gráficar. También, de este mismo grupo de paquetes, aplicaremos el operador %>% (“pipe”) (del paquete magrittr) para encadenar comandos. Para activar los paquetes del tidyverse utilice el código:
library(tidyverse) # activando paquetes del tidyverse (ggplot2, forcats, dplyr, magrittr, etc.)
%>%)Este operador permite pasar el objeto que esta a su izquierda como primer argumento del comando que está a su derecha, y con esto, favorece la escritura de código donde se deben usar varios comandos, uno detras de otro, para transformar un objeto.
El operador %>% pasa el objeto a su izquierda como primer argumento del comando a su derecha. Es decir, el código:
f(x,y)
es equivalente a
x %>% f(y)
forcatsLa mayoría de comandos del paquete forcats inician con el prefijo fct_, todos ellos diseñados para realizar operaciones de rutina con variables categóricas o factores. Algunos comandos son:
| Comando | Descripción |
|---|---|
fct_relevel
|
Permite reordenar los niveles de un factor, a mano, usando diferentes alernativas |
fct_recode
|
Permite cambiar las etiquetas o reagrupar los niveles del factor |
fct_rev
|
Invierte el orden de los niveles de un factor |
fct_infreq
|
Ordena los niveles de un factor de acuerdo a su frecuencia en el vector |
fct_inorder
|
Ordena los niveles de un factor de acuerdo al orden en el que aparezcan en el vector |
fct_reorder
|
Ordena los niveles de un factor de acuerdo a valores de una variable numérica resumida por funciones como la media o mediana |
Descargue una hoja de referencia del paquete forcats aquí. También puede consultar la ayuda de cada comando usando el código: ?fct_reorder, ?fct_relevel, etc.
El archivo tratSimul.csv contiene los datos de un estudio donde participaron 246 sujetos. En el ensayo se comparó un tratamiento para mejorar los sintomas de cierta enfermedad contra un placebo. La respuesta o desenlace fue una variable categórica ordinal llamada Improved (“mejora”). Se cuenta, además, con la edad y el género de los sujetos. Importe los datos con el siguiente código:
dat <- read.csv(file = "tratSimul.csv") # se importan los datos
str(dat) # se imprime su estructura
'data.frame': 246 obs. of 5 variables:
$ id : int 44 55 62 170 145 113 149 68 30 179 ...
$ Treatment: chr "Placebo" "Placebo" "Placebo" "Placebo" ...
$ Sex : chr "Male" "Male" "Male" "Male" ...
$ Age : int 29 34 32 30 27 30 28 30 28 28 ...
$ Improved : chr "None" "None" "Medium" "Low" ...
Una tabla de contingencia plana de estos datos es la siguiente:
ftable(Improved ~ Sex + Treatment, data = dat)
Improved High Low Medium None
Sex Treatment
Female Placebo 0 19 4 38
Treated 23 7 31 4
Male Placebo 1 10 4 44
Treated 23 10 22 6
Un resumen de la edad es el siguiente:
summary(dat$Age)
Min. 1st Qu. Median Mean 3rd Qu. Max.
23.00 32.00 50.50 50.06 68.75 77.00
Ejercicio: En una copia del data.frame dat, cree o agregue una nueva variable llamada AgeF que tenga la edad (Age) convertida en una variable categórica con tres niveles. Cuando imprima el str de la nueva tabla debe salir algo como lo siguiente:
str(dat1) # estructura de la nueva tabla
'data.frame': 246 obs. of 6 variables:
$ id : int 44 55 62 170 145 113 149 68 30 179 ...
$ Treatment: chr "Placebo" "Placebo" "Placebo" "Placebo" ...
$ Sex : chr "Male" "Male" "Male" "Male" ...
$ Age : int 29 34 32 30 27 30 28 30 28 28 ...
$ Improved : chr "None" "None" "Medium" "Low" ...
$ AgeF : Factor w/ 3 levels "Jov","Med","Viej": 1 1 1 1 1 1 1 1 1 1 ...
Use el comando cut (dentro del comando mutate) para realizar la categorización y simultaneamente agregar la nueva variable. Asigne etiquetas pertinentes a los niveles de AgeF. Finalice escribiendo código R que le permita verificar la transformación.
Código solución
# Se crea una nueva tabla (dat1) con una variable nueva (AgeF)
dat1 <- dat %>%
mutate(
AgeF = cut(Age, br = c(20,40,60,80), labels = c("Jov","Med","Viej"))
)
str(dat1) # estructura de la nueva tabla
# Verificando la categorizacion:
head(dat1, 3) # 1eras. tres filas de la nueva tabla
# Resumen por grupo:
dat1 %>% # con la nueva tabla
group_by(AgeF) %>% # agrupo por AgeF
summarise( # y resumo para calcular ...
n = n(), # la cantidad de filas (sujetos),
Age_min = min(Age), # la edad min y,
Age_max = max(Age) # la edad max por grupo
)
Cuando R crea un factor, a no ser de que se indique otra cosa, los niveles del factor quedan ordenados de menor a mayor alfabeticamente. El orden de los niveles de un factor se puede consultar usando el comando levels. Por ejemplo, para la respuesta Improved, el orden actual de sus niveles es:
dat %>% # desde dat ...
pull(Improved) %>% # se extrae Improved
factor() %>% # se convierte en factor
levels() # se consultan los niveles del factor y su orden
[1] "High" "Low" "Medium" "None"
Ejercicio: En la copia de la tabla creada para el ejercicio anterior, actualice la columna Improved cambiando el orden de los niveles de esta variable de tal forma que el nuevo orden sea: None, Low, Medium y High. Para esto utilice el comando fct_relevel en conjunto con mutate. Finalice escribiendo código que verifique la transfromación.
Código solución
# Se actualiza la columna Improved cambiando el orden sus niveles.
dat1 <- dat1 %>%
mutate(
Improved = fct_relevel(Improved, "None", "Low", "Medium")
)
str(dat1) # estructura de la nueva tabla
# Se verifica el cambio
levels(dat1$Improved)
# Por ejemplo, en una nueva tabla de contingencia
# los niveles ya salen ordenados:
xtabs(~ Treatment + Improved, data = dat1) %>%
prop.table(margin = 1) %>%
round(3)
Ejercicio: En la misma copia de la tabla creada en el primer ejercicio, cree nuevas variables en las cuales se cambie las etiquetas de las categorías de las variables Treatment, Sex e Improved por unas en español. Para esto utilice el comando fct_recode en conjunto con mutate. Finalice escribiendo código que verifique la transfromación.
Código solución
# Se crean nuevas variables, versiones en espanol, de las variables Improved, Sex y Treatment.
# Al usar el comando fct_recode la clave esta en la sintaxis: etiqueta_nueva = "etiqueta_actual"
dat1 <- dat1 %>%
mutate(
Mejora = fct_recode(Improved, Ninguna = "None", Baja = "Low",
Media = "Medium", Alta = "High"),
trat = fct_recode(Treatment, Placebo = "Placebo", Tratado = "Treated"),
genero = fct_recode(Sex, Hombre = "Male", Mujer = "Female")
)
str(dat1) # estructura de la nueva tabla
# Se verifica el cambio
levels(dat1$Improved)
# Se realizan tablas de contingencia para verificar el cambio
xtabs(~ Treatment + trat, data = dat1)
xtabs(~ Improved + Mejora, data = dat1)
xtabs(~ Sex + genero, data = dat1)
Aquí nos referimos a cambiar el agrupamiento de los niveles de un factor generando nuevos (menos) niveles.
Ejercicio: En la misma copia de la tabla creada desde el primer ejercicio, agregue una nueva variable llamada Mejora3 en la cual los niveles Media y Alta conformen un sólo nivel etiquetado como Marcada. Utilice para esto, de nuevo, el comando fct_recode dentro del comando mutate. Termine escribiendo código R que permita verificar el cambio.
Código solución
# A partir de la variable Mejora, se crea nueva variable uniendo dos categorias
# (re-categorizando). Al usar el comando fct_recode, la clave esta en usar
# la misma (nueva) etiqueta para todos los niveles que se quieren juntar.
dat1 <- dat1 %>%
mutate(
Mejora3 = fct_recode(Mejora, Marcada = "Media", Marcada = "Alta")
)
str(dat1) # estructura de la nueva tabla
# Se verifica el cambio
levels(dat1$Mejora3)
# Se realizan tablas de contingencia para verificar el cambio
xtabs(~ Mejora, data = dat1)
xtabs(~ Mejora + Mejora3, data = dat1)
forcats al generar gráficosConsidere el siguiente gráfico que muestra la frecuencia de sujetos en cada categoría de la respuesta (Mejora) de acuerdo al tratamiento, el género y la edad.
Ejercicio:
De acuerdo al gráfico ¿concluiría que el tratamiento si parece estar asociado con una mejora en los síntomas de la enfermedad? De existir un efecto del tratamiento, este sería el mismo en todos los grupos de edad?
Escriba código R que produzca el gráfico. Para esto, use xtabs, prop.table y as.data.frame para generar un data.frame con las frecuencias relativas condicionadas a cada grupo. Luego use ggplot con geom_col (o uno similar) para realizar el gráfico.
Código solución
# Creacion de data.frame con datos agrupados en formato largo
# y generacion de grafico de barras apiladas con ggplot2
dat1 %>%
xtabs(~ trat + Mejora + AgeF + genero, data = .) %>%
prop.table(margin = c(1,3,4)) %>%
as.data.frame() %>%
mutate(
Mejora = fct_rev(Mejora) # se revierte el orden de los niveles para que queden
# mejor en el grafico de acuerdo a la escala de color
) %>%
ggplot(aes(x = AgeF, y = Freq*100, fill = Mejora)) + geom_col() +
facet_grid(genero ~ trat) +
scale_fill_brewer(direction = -1, palette = "YlGn") +
labs(x = "Edad", y = "Frecuencia (%)", fill = "Mejora de\nsíntomas")
En otro ejemplo, considere el siguiente gráfico que muestra la misma información que el anterior, tan sólo que las etiquetas de la edad en el eje X están modificadas.
Ejercicio: Escriba código R que produzca el gráfico.
Código solución
# Creacion de data.frame con datos agrupados en formato largo
# y generacion de grafico de barras apiladas con ggplot2.
# Igual que el codigo del ejericio anterior, solo se agrega una linea para modificar
# AgeF con el comando fct_recode. La expresion "\n" genera un salto de linea.
dat1 %>%
xtabs(~ trat + Mejora + AgeF + genero, data = .) %>%
prop.table(margin = c(1,3,4)) %>%
as.data.frame() %>%
mutate(
Mejora = fct_rev(Mejora),
AgeF = fct_recode(AgeF, "Joven\n20-40" = "Jov",
"Media\n40-60" = "Med",
"Alta\n60-80" = "Viej")
) %>%
ggplot(aes(x = AgeF, y = Freq*100, fill = Mejora)) + geom_col() +
facet_grid(genero ~ trat) +
scale_fill_brewer(direction = -1, palette = "YlGn") +
labs(x = "Edad", y = "Frecuencia (%)", fill = "Mejora de\nsíntomas")
Nota: En el código de los dos gráficos anteriores, usamos los comandos
xtabs,prop.tableyas.data.framepara calcular las frecuencias relativas o proporciones de las categorías de la variable respuesta. No obstante, el paquete ggplot2 tiene el comandogeom_bar, que con su argumentoposition, permiten hacer el mismo gráfico desde un data.frame con los datos crudos (donde cada fila es un sujeto). Para mayor detalle ir a esta sección.
Desde los mismos datos en el archivo tratSimul.csv y revisados en la sección anterior escriba código R que realice el siguiente gráfico:
Este gráfico fue construido con todos los datos (246 sujetos). La etiqueta Placebo se cambio por Control y la etiqueta Tratado se cambio por Aspirina. Las etiquetas de la edad también fueron modificadas como se observa en el gráfico. Note además que el género ya no aparece.
Considere el data.frame DAAG::ais que tiene datos sobre variables morfométricas y de la sangre de una muestra de 202 atletas en 10 deportes.
library(DAAG) # se carga paquete DAAG
str(ais) # se revisa la estructura del data.frame "ais"
'data.frame': 202 obs. of 13 variables:
$ rcc : num 3.96 4.41 4.14 4.11 4.45 4.1 4.31 4.42 4.3 4.51 ...
$ wcc : num 7.5 8.3 5 5.3 6.8 4.4 5.3 5.7 8.9 4.4 ...
$ hc : num 37.5 38.2 36.4 37.3 41.5 37.4 39.6 39.9 41.1 41.6 ...
$ hg : num 12.3 12.7 11.6 12.6 14 12.5 12.8 13.2 13.5 12.7 ...
$ ferr : num 60 68 21 69 29 42 73 44 41 44 ...
$ bmi : num 20.6 20.7 21.9 21.9 19 ...
$ ssf : num 109.1 102.8 104.6 126.4 80.3 ...
$ pcBfat: num 19.8 21.3 19.9 23.7 17.6 ...
$ lbm : num 63.3 58.5 55.4 57.2 53.2 ...
$ ht : num 196 190 178 185 185 ...
$ wt : num 78.9 74.4 69.1 74.9 64.6 63.7 75.2 62.3 66.5 62.9 ...
$ sex : Factor w/ 2 levels "f","m": 1 1 1 1 1 1 1 1 1 1 ...
$ sport : Factor w/ 10 levels "B_Ball","Field",..: 1 1 1 1 1 1 1 1 1 1 ...
Suponga que se quiere hacer un diagrama de barras del factor sport para mostrar la frecuencia de cada deporte, una opción sería el siguiente código:
# Codigo para realizar diagrama de barras de frecuencias del factor 'sport'
ais %>%
xtabs(~ sport, data = .) %>%
as.data.frame() %>%
ggplot(aes(x = Freq, y = sport)) + geom_col(fill = "skyblue")
No obstante, el gráfico queda mucho mejor si los deportes se muestran ordenados por su frecuencia. El comando fct_infreq realiza esta ordenación automáticamente:
# Codigo para realizar diagrama de barras de frecuencias del factor 'sport'
# antes se reordenan los niveles del factor 'sport' por la frecuencia
ais %>%
mutate(
sport = fct_infreq(sport)
) %>%
xtabs(~ sport, data = .) %>%
as.data.frame() %>%
ggplot(aes(x = Freq, y = sport)) + geom_col(fill = "skyblue")
Nota: El comando
count(paquete dplyr) produce los conteos igual quextabspero directamente en undata.framecomo datos agrupados en formato largo. Es decir, con este comando nos ahorramos la aplicación deas.data.frame. El código sería el siguiente:
# Codigo para generar el mismo grafico de arriba sólo aque ahora usamos
# el comando 'count' para generar el data.frame con los conteos.
ais %>%
mutate(
sport = fct_infreq(sport)
) %>%
count(sport) %>%
ggplot(aes(x = n, y = sport)) + geom_col(fill = "skyblue")
Considere este gráfico donde se realizan boxplot de la hg (hemoglobina) por deporte:
ggplot(ais, aes(x = hg, y = sport)) +
geom_boxplot(fill = "skyblue")
El gráfico puede mejorar si los deportes se ordenan por su nivel de hemoglobina. Para hacer esto, usamos el comando fct_reorder, el cual permite ordenar por alguna medida de resumen, tal como la mediana o la media, calculada a cierta variable numérica por grupo:
ggplot(ais, aes(x = hg, y = fct_reorder(sport, hg, median))) +
geom_boxplot(fill = "skyblue") +
geom_vline(xintercept = median(ais$hg), size = 0.5, linetype = "dashed") +
labs(y = "Sport")
geom_bar y position_fill para calcular frecuencias condicionalesArriba, en esta sección, usamos los comandos xtabs, prop.table, y as.data.frame para realizar el cálculo de las frecuencias relativas condicionales de las categorías de la variable respuesta para despues hacer el gráfico con el comando geom_col de ggplot2.
Nos podemos ahorrar el cálculo de estas frecuencias relativas si, en lugar de usar el comando geom_col, empleamos el comando geom_bar, el cual automáticamente realiza el conteo (que haría xtabs). Además, si utilizamos el argumento position = position_fill(), se calculan las frecuencias condicionales respectivas. A continuación el código completo:
# Version de un mismo grafico de arriba, pero usando geom_bar y position_fill
# para dejar que el mismo ggplot2 calcule las proporciones
# condicionales.
ggplot(dat1, aes(x = AgeF, fill = fct_rev(Mejora)) ) +
geom_bar(position = position_fill()) +
facet_grid(genero ~ trat) +
scale_fill_brewer(direction = -1, palette = "YlGn") +
scale_y_continuous(labels = scales::label_percent(suffix = "") ) +
labs(x = "Edad", y = "Frecuencia (%)", fill = "Mejora de\nsíntomas")
Observe que los datos deben estar en formato crudo, donde cada fila es un sujeto. Además, ya no es necesario mapear el eje y a una columna de frecuencia, pues el comando geom_bar automáticamente cálcula la frecuencia y realiza ese mapeo.