Creación de gráficas de violín con RStudio

Creation of Violin Plots with RStudio

1 Gráfico de violín

Son gráficos que muestran la densidad y distribución de los datos, que combinan los gráficos de cajas y bigotes con los de densidad. Son muy útiles para el análisis exploratorio de los datos y para representar su comportamiento.

Gráfico de violín

2 Carga de los datos

Para empezar a crear gráficos de este tipo, primero debemos de cargar nuestros datos, en este caso, utilizaremos el conjunto de datos penguins, disponible en el paquete palmerpenguins, elaborado por Horst AM, Hill AP, Gorman KB (2020).

#install.packages("palmerpenguins") #instalar solo si no se tiene el paquete
library(palmerpenguins)
library(ggplot2)
library(dplyr)

3 Objetivo

Crear un gráfico de violín respecto a las especies y peso de los pingüinos cerca de Palmer Station en la Antartida. El gráfico debe de incluir en el eje x, los datos más básicos como máximo, mínimo, promedio, cuartiles, etc.

Artwork by @allison_horst

4 Tipos de datos

STR nos ayuda a conocer la estructura de los datos, basícamente para el ejercicio nos interesa tener:

  • Datos cuantitativos
  • Datos cualitativos
str(penguins)
tibble [344 × 8] (S3: tbl_df/tbl/data.frame)
 $ species          : Factor w/ 3 levels "Adelie","Chinstrap",..: 1 1 1 1 1 1 1 1 1 1 ...
 $ island           : Factor w/ 3 levels "Biscoe","Dream",..: 3 3 3 3 3 3 3 3 3 3 ...
 $ bill_length_mm   : num [1:344] 39.1 39.5 40.3 NA 36.7 39.3 38.9 39.2 34.1 42 ...
 $ bill_depth_mm    : num [1:344] 18.7 17.4 18 NA 19.3 20.6 17.8 19.6 18.1 20.2 ...
 $ flipper_length_mm: int [1:344] 181 186 195 NA 193 190 181 195 193 190 ...
 $ body_mass_g      : int [1:344] 3750 3800 3250 NA 3450 3650 3625 4675 3475 4250 ...
 $ sex              : Factor w/ 2 levels "female","male": 2 1 1 NA 1 2 1 2 NA NA ...
 $ year             : int [1:344] 2007 2007 2007 2007 2007 2007 2007 2007 2007 2007 ...

5 Código - parte uno

Vamos a crear un data frame con las estadísticas básicas de los datos, creando primero una función para calcular la moda de los datos del peso de los pingüinos.

calcular_moda <- function(x) {
  ux <- unique(x)  # Valores únicos
  ux[which.max(tabulate(match(x, ux)))]  # Valor con mayor frecuencia
}

6 Código - parte dos

Creamos el data frame con las estadísticas básicas del peso de los pingüinos, agrupados por su especie.

conteo_sp <- penguins %>%
  group_by(species) %>%
  summarise(
    n = n(),
    suma = sum(body_mass_g, na.rm = TRUE),
    maximo = max(body_mass_g, na.rm = TRUE),
    minimo = min(body_mass_g, na.rm = TRUE),
    promedio = mean(body_mass_g, na.rm = TRUE),
    desviacion_estandar = sd(body_mass_g, na.rm = TRUE),  # Desviación estándar
    intervalo = max(body_mass_g, na.rm = TRUE) - min(body_mass_g, na.rm = TRUE),
    q1 = quantile(body_mass_g, probs = 0.25, na.rm = TRUE),
    q2 = median(body_mass_g, na.rm = TRUE),  # Mediana
    q3 = quantile(body_mass_g, probs = 0.75, na.rm = TRUE),
    q4 = quantile(body_mass_g, probs = 1, na.rm = TRUE),
    moda = calcular_moda(body_mass_g)
  )

7 Estadísticas básicas del peso de los pingüinos vs la especie.

Si no tuvieramos que filtrar por especies, el resultado global sería de esta forma:

8 Código - parte tres

plot_sp <- 
  ggplot(penguins, aes(x = species, y = body_mass_g, fill = species)) + 
  geom_violin(alpha = 0.7) +  # Gráfico de violín
  geom_boxplot(width=0.1, color="black", alpha=0.2) + #Añade la boxplot
  geom_jitter(width = 0.2, color = "white", alpha = 0.5) +  # Añade los puntos
  stat_summary(fun = mean, geom = "point", shape = 16, size = 3, color = "red") + 
  theme_minimal() +   # Tema limpio
  theme(legend.position = "none", 
        axis.text.x = element_text(angle = 0, size = 14, color ="white"),
        axis.text.y = element_text(size = 14, color = "white"),  # Cambiar color de texto del eje Y
    axis.title.x = element_text(size = 14, color = "white"),  # Cambiar color del título del eje X
    axis.title.y = element_text(size = 14, color = "white"),  # Cambiar color del título del eje Y
    plot.title = element_text(size = 18, color = "white"),  # Cambiar color del título
    plot.subtitle = element_text(color = "white"),  # Cambiar color del subtítulo
    plot.caption = element_text(color = "white"),   # Cambiar color de la leyenda (si aplica)
    panel.grid.minor.y = element_line(linewidth = 0.2, color = "lightgray", linetype = "dashed")
  ) + 
  labs(title = "Especies de pingüinos vs su peso (g)",
       x = "Especie", 
       y = "Peso (g)") + 
  scale_x_discrete(labels = function(x) {
    paste("Especie ", x, 
          "\n Conteo=", conteo_sp$n[match(x, conteo_sp$species)], 
          "\n Peso máx=", conteo_sp$maximo[match(x, conteo_sp$species)], 
          "\n Peso mín=", conteo_sp$minimo[match(x, conteo_sp$species)], 
          "\n Promedio=", round(conteo_sp$promedio[match(x, conteo_sp$species)], 2), 
          "\n Q1 = ", conteo_sp$q1[match(x, conteo_sp$species)], ", Q2= ", conteo_sp$q2[match(x, conteo_sp$species)], 
          "\n Q3= ", conteo_sp$q3[match(x, conteo_sp$species)], ", Q4= ", conteo_sp$q4[match(x, conteo_sp$species)],
          "\n SD= ", round(conteo_sp$desviacion_estandar[match(x, conteo_sp$species)],2),
          sep="")
  }) +
  scale_y_continuous(labels = scales::comma_format(),breaks = seq(0, max(penguins$body_mass_g, na.rm = TRUE), by = 500))

ggsave("grafico_violin_sp_transparente.png", plot = plot_sp, width = 10, height = 6, dpi = 300, bg = "transparent")

9 Gráfica especies vs peso

10 Código - parte cuatro

plot_general <- 
  ggplot(penguins, aes(x = general, y = body_mass_g, fill = general)) + 
  geom_violin(alpha = 0.7) +  # Gráfico de violín
  geom_boxplot(width=0.1, color="black", alpha=0.2) + #Añade la boxplot
  geom_jitter(width = 0.2, color = "white", alpha = 0.5) +  # Añade los puntos
  stat_summary(fun = mean, geom = "point", shape = 16, size = 3, color = "red") + 
  theme_minimal() +   # Tema limpio
  theme(legend.position = "none", 
        axis.text.x = element_text(angle = 0, size = 14, color ="white"),
        axis.text.y = element_text(size = 14, color = "white"),  # Cambiar color de texto del eje Y
    axis.title.x = element_blank(),  # no se agrega el nombre del eje x
    axis.title.y = element_text(size = 14, color = "white"),  # Cambiar color del título del eje Y
    plot.title = element_text(size = 18, color = "white"),  # Cambiar color del título
    plot.subtitle = element_text(color = "white"),  # Cambiar color del subtítulo
    plot.caption = element_text(color = "white"),   # Cambiar color de la leyenda (si aplica)
    panel.grid.minor.y = element_line(linewidth = 0.2, color = "lightgray", linetype = "dashed")
  ) + 
  labs(title = "Datos generales de los pingüinos vs su peso (g)",
       y = "Peso (g)") + 
  scale_x_discrete(labels = function(x) {
    paste("Datos generales", #sustituye al nombre de mi eje x
          "\n Conteo=", conteo$n[match(x, conteo$general)], 
          "\n Peso máx=", conteo$maximo[match(x, conteo$general)], 
          "\n Peso mín=", conteo_sp$minimo[match(x, conteo$general)], 
          "\n Promedio=", round(conteo$promedio[match(x, conteo$general)], 2),
          "\n Q1 = ", conteo$q1[match(x, conteo$general)], ", Q2= ", conteo$q2[match(x, conteo$general)], 
          "\n Q3= ", conteo$q3[match(x, conteo$general)], ", Q4= ", conteo$q4[match(x, conteo$general)],
          "\n SD= ", round(conteo$desviacion_estandar[match(x, conteo$general)],2),
          sep="")
  }) +
  scale_y_continuous(labels = scales::comma_format(),breaks = seq(0, max(penguins$body_mass_g, na.rm = TRUE), by = 500))+
  scale_fill_manual(values = c("general" = "#bd93f9"))

ggsave("grafico_violin_transparente.png", plot = plot_general, width = 10, height = 6, dpi = 300, bg = "transparent")

11 Gráfica general