Gráfico de dispersión multiserie anotado con R

Un ejemplo real de una fabricación de queso Camembert.

queso
R
PUBLICADO

10 de febrero de 2023

El gráfico muestra la relación entre el índice de humedad en base al extracto magro (HFD) y el pH en un queso de pasta blanda tipo Camembert, diferenciando los datos según la época del año en que se elaboró el queso. Es un ejemplo de cómo las anotaciones sobre el gráfico pueden ayudar a la comprensión de los datos representados.

Los puntos naranjas corresponden a fabricaciones de verano (julio-septiembre) y los azules a fabricaciones de invierno (diciembre-febrero); el resto del año aparece en gris neutro en segundo plano.

Las elipses de confianza al 75% permiten visualizar de un vistazo cómo se agrupan ambas temporadas: en verano el elaborador reduce la HFD para facilitar la conservación del queso con temperatura ambiente más alta, reduciendo el riesgo de proteólisis, mientras que en invierno puede subir HFD, permitiendo una textura más blanda y mayor nivel de proteolisis. Las variaciones de pH son consecuencia de las modificaciones de la tecnología.

Esta variación estacional refleja las diferencias en el manejo de la tecnología, con consecuencias directas sobre la textura y la evolución del queso durante la maduración. El elaborador consigue el objetivo de mejorar el comportamiento del queso en el mercado en momentos de más calor, pero a costa de introducir variaciones excesivas en la textura y el pH.

El script está escrito en R con tidyverse, cowplot y magick. La variable HFD se calcula directamente a partir de los datos de extracto seco total (EST) y materia grasa (MG) registrados en el control de producción, y las imágenes del queso se componen sobre el gráfico como anotaciones visuales para ilustrar el aspecto real de la pasta en cada temporada.

Mostrar código
library(tidyverse)
library(cowplot)
library(magick)

# ── Datos ──────────────────────────────────────────────────────────────────
df <- read_csv2("datos/camembert.csv",
                locale = locale(encoding = "ISO-8859-1")) |>
  mutate(
    fecha    = dmy(fecha),
    mes      = month(fecha),
    estacion = case_when(
      mes %in% 7:9          ~ "Verano",
      mes %in% c(12, 1, 2)  ~ "Invierno",
      TRUE                  ~ "Otras"
    ),
    estacion = factor(estacion, levels = c("Invierno", "Verano", "Otras")),
    hfd = (100-est)/(100-mg)*100
  )

colores <- c("Verano" = "#D2691E", "Invierno" = "#1B3A6B", "Otras" = "grey75")

# ── Gráfico base ───────────────────────────────────────────────────────────
p <- ggplot(df, aes(x = hfd, y = ph, color = estacion)) +
  geom_point(data = filter(df, estacion == "Otras"),
             alpha = 0.30, size = 2) +
  geom_point(data = filter(df, estacion != "Otras"),
             alpha = 0.45, size = 3) +
  stat_ellipse(
    data = filter(df, estacion != "Otras"),
    geom = "path", linewidth = 1.2, level = 0.75,
    show.legend = FALSE
  ) +
  # Flechas hacia las imágenes
  geom_curve(
    data = data.frame(x = 71, y = 4.58, xend = 74.5, yend = 4.58),
    aes(x = x, y = y, xend = xend, yend = yend),
    color = "#D2691E", linewidth = 1,
    arrow = arrow(length = unit(0.25, "cm"), type = "open"),
    curvature = 0.2, inherit.aes = FALSE
  ) +
  geom_curve(
    data = data.frame(x = 72, y = 4.82, xend = 74.5, yend = 4.96),
    aes(x = x, y = y, xend = xend, yend = yend),
    color = "#1B3A6B", linewidth = 1,
    arrow = arrow(length = unit(0.25, "cm"), type = "open"),
    curvature = -0.2, inherit.aes = FALSE
  ) +
  scale_color_manual(values = colores, name = NULL) +
  labs(
    title = "Evolución estacional de los valores de HFD y pH \n de un queso de pasta blanda tipo Camembert",
    x = "HFD",
    y = "pH"
  ) +
  scale_x_continuous(breaks = seq(65, 75, 2), limits = c(65, 80)) +
  scale_y_continuous(breaks = seq(4.4, 5.1, 0.1), limits = c(4.4, 5.1)) +
  theme_bw(base_size = 13) +
  theme(
    plot.title      = element_text(hjust = 0.5),
    legend.position = "none"
  )

# ── Imágenes ───────────────────────────────────────────────────────────────
img_verano   <- magick::image_read("imagenes/Camembert-commons-wikimedia_1.png")
img_invierno <- magick::image_read("imagenes/camembert_2.png")

# ── Composición con cowplot ────────────────────────────────────────────────
# Coordenadas en fracción del canvas total (0-1): x, y = esquina inferior izquierda
ggdraw(p) +
  draw_image(img_verano,
             x = 0.65, y = 0.58, width = 0.24, height = 0.30) +
  draw_label("En verano", x = 0.77, y = 0.16,
             color = "#D2691E", fontface = "bold", size = 12) +
  draw_image(img_invierno,
             x = 0.65, y = 0.18, width = 0.24, height = 0.30) +
  draw_label("En invierno", x = 0.77, y = 0.56,
             color = "#1B3A6B", fontface = "bold", size = 12)