Módulo 3 — Visualización de Datos en R

Gráficos en R base e introducción a ggplot2

enlace web: https://rpubs.com/daniloceschin/Rbioinfo-modulo3

Asignatura: Bioinformática
Carrera: Licenciatura en Genética
Institución: IUCBC – CIMETSA, Córdoba, Argentina
Docente: Dr. Danilo G. Ceschin
Año: 2026


Objetivo del módulo: aprender a generar y personalizar gráficos en R, primero con las herramientas de R base y luego con ggplot2. Al final del módulo vas a poder elegir el tipo de gráfico adecuado para cada tipo de dato y producir figuras con contexto biológico real.


Parte 1 — Gráficos en R base

R tiene un sistema gráfico integrado que no requiere ningún paquete adicional. Es menos elegante que ggplot2, pero es inmediato, flexible y suficiente para exploración y análisis rápido.

Datos de práctica

Vamos a usar un conjunto de datos simulados que representan genes con distintos atributos. Lo creamos una sola vez y lo usamos en toda la Parte 1.

set.seed(42)
n_genes <- 80

genes_df <- data.frame(
  gen        = paste0("Gen_", sprintf("%02d", 1:n_genes)),
  longitud   = round(rnorm(n_genes, mean = 2500, sd = 900)),   # pb
  gc_content = round(runif(n_genes, min = 0.35, max = 0.70), 3),
  expresion  = round(2^rnorm(n_genes, mean = 7, sd = 2), 1),   # CPM
  condicion  = factor(rep(c("Control","KO"), each = n_genes/2),
                      levels = c("Control","KO")),
  cromosoma  = factor(sample(paste0("chr", c(1:5,"X")),
                             n_genes, replace = TRUE)),
  stringsAsFactors = FALSE
)

# Vista rápida
head(genes_df)
str(genes_df)

La función plot() — el punto de entrada

plot() es la función gráfica más general de R. Detecta automáticamente el tipo de dato que recibe y elige el gráfico más apropiado.

# El gráfico más simple posible: x vs y
plot(genes_df$longitud, genes_df$gc_content)

Funciona, pero es bastante austero. Los argumentos principales para mejorar la presentación son:

Argumento Qué controla Ejemplo
main Título main = "Mi gráfico"
xlab, ylab Etiquetas de ejes xlab = "Longitud (pb)"
col Color de los puntos col = "steelblue"
pch Forma de los puntos pch = 16 (círculo sólido)
cex Tamaño de los puntos cex = 0.8
xlim, ylim Límites de los ejes xlim = c(0, 5000)
type Tipo de gráfico type = "p" (puntos), "l" (líneas), "b" (ambos)
plot(
  x    = genes_df$longitud,
  y    = genes_df$gc_content,
  main = "Longitud génica vs Contenido GC",
  xlab = "Longitud del gen (pb)",
  ylab = "Contenido GC",
  col  = "steelblue",
  pch  = 16,          # círculo sólido
  cex  = 0.8          # puntos un poco más pequeños
)

Colorear puntos por grupo

Una técnica muy útil: asignar colores distintos según una variable categórica. Usamos el factor condicion para ello.

# Definir paleta de colores por condición
colores <- c(Control = "steelblue", KO = "firebrick")

plot(
  x    = genes_df$longitud,
  y    = genes_df$gc_content,
  main = "Longitud génica vs Contenido GC\npor condición experimental",
  xlab = "Longitud del gen (pb)",
  ylab = "Contenido GC",
  col  = colores[genes_df$condicion],   # asigna color según el factor
  pch  = 16,
  cex  = 0.9
)

# Agregar leyenda
legend(
  x      = "topright",
  legend = names(colores),
  col    = colores,
  pch    = 16,
  title  = "Condición"
)

💡 Nota: colores[genes_df$condicion] usa el vector de condiciones como índice del vector de colores. Cuando el valor es "Control" toma "steelblue", cuando es "KO" toma "firebrick". Es el mismo mecanismo de subsetting por nombre que vimos en el Módulo 2.

Agregar una línea de tendencia

plot(
  x    = genes_df$longitud,
  y    = genes_df$gc_content,
  main = "Longitud génica vs Contenido GC",
  xlab = "Longitud del gen (pb)",
  ylab = "Contenido GC",
  col  = "steelblue",
  pch  = 16, cex = 0.8
)

# Línea de regresión
abline(lm(gc_content ~ longitud, data = genes_df),
       col = "firebrick", lwd = 2, lty = 2)   # lty=2: línea punteada

💡 Nota: abline() agrega una línea sobre un gráfico existente. lm() ajusta un modelo lineal y abline() extrae automáticamente la pendiente e intercepto para dibujar la recta.


Histogramas con hist()

El histograma muestra la distribución de una variable continua. Ideal para explorar si los datos siguen una distribución normal, tienen sesgo, o presentan valores atípicos.

hist(
  x      = genes_df$longitud,
  main   = "Distribución de longitudes génicas",
  xlab   = "Longitud (pb)",
  ylab   = "Frecuencia",
  col    = "steelblue",
  border = "white",      # color del borde de las barras
  breaks = 15            # número aproximado de barras
)

Agregar curva de densidad

# freq=FALSE: eje Y como densidad (en lugar de frecuencia absoluta)
hist(
  genes_df$longitud,
  main   = "Distribución de longitudes génicas",
  xlab   = "Longitud (pb)",
  ylab   = "Densidad",
  col    = "lightsteelblue",
  border = "white",
  breaks = 15,
  freq   = FALSE          # necesario para superponer la curva de densidad
)

# Superponer curva de densidad suavizada
lines(density(genes_df$longitud), col = "firebrick", lwd = 2)

Comparar distribuciones de dos grupos

# par(mfrow) divide la ventana gráfica en filas × columnas
par(mfrow = c(1, 2))   # 1 fila, 2 columnas

hist(genes_df$longitud[genes_df$condicion == "Control"],
     main   = "Control",
     xlab   = "Longitud (pb)",
     col    = "steelblue",
     border = "white",
     breaks = 10,
     xlim   = c(0, 6000))   # mismo eje x en ambos para comparar

hist(genes_df$longitud[genes_df$condicion == "KO"],
     main   = "KO",
     xlab   = "Longitud (pb)",
     col    = "firebrick",
     border = "white",
     breaks = 10,
     xlim   = c(0, 6000))

par(mfrow = c(1, 1))   # restablecer la ventana gráfica a 1 panel

⚠️ Importante: siempre restablecer par(mfrow = c(1,1)) al terminar. Si no lo hacés, el siguiente gráfico que generes seguirá dividido en paneles.


Boxplots con boxplot()

El boxplot (diagrama de caja) es ideal para comparar distribuciones entre grupos. Muestra mediana, cuartiles y valores atípicos de un vistazo.

Anatomía de un boxplot:

 ─── línea superior del bigote: máximo (sin outliers)
 ┐  borde superior de la caja:  Q3 (tercer cuartil, 75%)
 │  línea dentro de la caja:    mediana (Q2, 50%)
 ┘  borde inferior de la caja:  Q1 (primer cuartil, 25%)
 ─── línea inferior del bigote: mínimo (sin outliers)
 ○   puntos fuera de los bigotes: valores atípicos (outliers)
boxplot(
  expresion ~ condicion,
  data     = genes_df,
  main     = "Expresión génica por condición",
  xlab     = "Condición",
  ylab     = "Expresión (CPM)",
  col      = c("steelblue", "firebrick"),
  border   = "gray30",
  notch    = FALSE       # notch=TRUE agrega muesca en la mediana
)

💡 Nota: la fórmula expresion ~ condicion se lee: “expresión en función de condición”. Es la misma notación que se usa en modelos estadísticos como lm() o t.test(). El ~ separa variable respuesta (izquierda) de variable explicativa (derecha).

Boxplot con múltiples grupos

boxplot(
  longitud ~ cromosoma,
  data   = genes_df,
  main   = "Longitud génica por cromosoma",
  xlab   = "Cromosoma",
  ylab   = "Longitud (pb)",
  col    = c("steelblue","firebrick","forestgreen",
             "darkorange","mediumpurple","gold3"),
  border = "gray30",
  las    = 1    # las=1: etiquetas del eje X horizontales
)

Gráficos de barras con barplot()

Ideales para mostrar valores resumidos (medias, conteos, proporciones) por categoría. No son adecuados para mostrar distribuciones — para eso son los boxplots o histogramas.

# Conteo de genes por cromosoma
conteo_crom <- table(genes_df$cromosoma)
conteo_crom

barplot(
  height = conteo_crom,
  main   = "Número de genes por cromosoma",
  xlab   = "Cromosoma",
  ylab   = "Número de genes",
  col    = "steelblue",
  border = "white"
)

Barras agrupadas — comparar grupos

# Tabla de frecuencias: cromosoma × condición
tabla_crom_cond <- table(genes_df$cromosoma, genes_df$condicion)

barplot(
  height = t(tabla_crom_cond),    # transponer: condición en filas
  beside = TRUE,                  # beside=TRUE: barras una al lado de otra
  main   = "Genes por cromosoma y condición",
  xlab   = "Cromosoma",
  ylab   = "Número de genes",
  col    = c("steelblue","firebrick"),
  border = "white",
  legend.text = colnames(tabla_crom_cond),
  args.legend = list(x = "topright", title = "Condición")
)

Múltiples gráficos con par(mfrow)

par(mfrow = c(filas, columnas)) divide la ventana gráfica en una cuadrícula. Muy útil para comparar varios gráficos a la vez.

par(mfrow = c(2, 2))   # cuadrícula 2×2

# Gráfico 1: distribución de longitud
hist(genes_df$longitud, main = "Distribución de longitud",
     xlab = "Longitud (pb)", col = "steelblue", border = "white")

# Gráfico 2: distribución de GC content
hist(genes_df$gc_content, main = "Distribución GC content",
     xlab = "GC content", col = "forestgreen", border = "white")

# Gráfico 3: dispersión longitud vs GC
plot(genes_df$longitud, genes_df$gc_content,
     main = "Longitud vs GC content",
     xlab = "Longitud (pb)", ylab = "GC content",
     col = "steelblue", pch = 16, cex = 0.8)

# Gráfico 4: boxplot expresión por condición
boxplot(expresion ~ condicion, data = genes_df,
        main = "Expresión por condición",
        col = c("steelblue","firebrick"), border = "gray30")

par(mfrow = c(1, 1))   # restablecer

Guardar gráficos a archivo

Para guardar un gráfico, abrís un “dispositivo” gráfico antes de generarlo y lo cerrás con dev.off() al terminar.

# ── PNG — para presentaciones y web ─────────────────────────────────────────
png(
  filename = "longitud_vs_gc.png",
  width    = 1800,          # píxeles de ancho
  height   = 1400,          # píxeles de alto
  res      = 300            # resolución (dpi) — 300 es estándar para publicación
)

plot(
  genes_df$longitud, genes_df$gc_content,
  main = "Longitud génica vs Contenido GC",
  xlab = "Longitud (pb)", ylab = "GC content",
  col = "steelblue", pch = 16, cex = 0.8
)

dev.off()   # CRÍTICO: cerrar el dispositivo para que el archivo se escriba

# ── PDF — para publicaciones científicas ─────────────────────────────────────
pdf(
  file   = "longitud_vs_gc.pdf",
  width  = 7,     # pulgadas
  height = 5
)

plot(
  genes_df$longitud, genes_df$gc_content,
  main = "Longitud génica vs Contenido GC",
  xlab = "Longitud (pb)", ylab = "GC content",
  col = "steelblue", pch = 16
)

dev.off()

⚠️ Importante: si olvidás llamar a dev.off(), el archivo queda abierto e incompleto, y los siguientes gráficos que generes se seguirán escribiendo en ese archivo en lugar de mostrarse en RStudio. Si eso ocurre, ejecutá dev.off() varias veces hasta que la consola diga "null device" o 1.


Parte 2 — Introducción a ggplot2

La gramática de los gráficos

ggplot2 funciona de forma completamente diferente a R base. En lugar de una función única que hace todo, construís un gráfico por capas, combinando componentes con el operador +.

La idea central es la gramática de gráficos (Grammar of Graphics): todo gráfico se puede describir como la combinación de:

Componente Función Qué hace
Datos ggplot(data) Define el data frame de origen
Aesthetics aes() Mapea variables a propiedades visuales (x, y, color, tamaño…)
Geometría geom_*() Define el tipo de gráfico (puntos, barras, cajas…)
Escalas scale_*() Controla ejes, colores, tamaños
Tema theme_*() Controla la apariencia general
Facetas facet_*() Divide el gráfico en paneles por grupo
# Instalar ggplot2 (solo la primera vez)
install.packages("ggplot2")

# Cargar en cada sesión
library(ggplot2)

Gráfico de dispersión con geom_point()

Rehacemos el primer gráfico de R base y comparamos:

# Versión mínima
ggplot(data = genes_df, aes(x = longitud, y = gc_content)) +
  geom_point()

Inmediatamente más prolijo que R base, sin configurar nada. Ahora lo personalizamos:

ggplot(genes_df, aes(x = longitud, y = gc_content, color = condicion)) +
  geom_point(size = 2, alpha = 0.7) +         # alpha: transparencia (0-1)
  scale_color_manual(values = c(Control = "steelblue", KO = "firebrick")) +
  labs(
    title  = "Longitud génica vs Contenido GC",
    x      = "Longitud del gen (pb)",
    y      = "Contenido GC",
    color  = "Condición"
  ) +
  theme_bw()    # tema con fondo blanco y grilla gris

💡 Diferencia clave con R base: en ggplot2 el color por grupo se define dentro de aes() como color = condicion. ggplot2 se encarga automáticamente de asignar colores y generar la leyenda. En R base había que hacerlo manualmente con colores[genes_df$condicion] y luego agregar la leyenda con legend().

Agregar línea de tendencia

ggplot(genes_df, aes(x = longitud, y = gc_content)) +
  geom_point(color = "steelblue", size = 2, alpha = 0.7) +
  geom_smooth(method = "lm", formula = y ~ x,
              color = "firebrick", se = TRUE) +    # se=TRUE: banda de confianza
  labs(
    title = "Longitud génica vs Contenido GC",
    x     = "Longitud del gen (pb)",
    y     = "Contenido GC"
  ) +
  theme_bw()

Histograma con geom_histogram()

ggplot(genes_df, aes(x = longitud)) +
  geom_histogram(bins = 15, fill = "steelblue", color = "white") +
  labs(
    title = "Distribución de longitudes génicas",
    x     = "Longitud (pb)",
    y     = "Frecuencia"
  ) +
  theme_bw()

Histograma con densidad superpuesta

ggplot(genes_df, aes(x = longitud)) +
  geom_histogram(aes(y = after_stat(density)),   # eje Y como densidad
                 bins = 15, fill = "lightsteelblue", color = "white") +
  geom_density(color = "firebrick", linewidth = 1) +
  labs(
    title = "Distribución de longitudes génicas",
    x     = "Longitud (pb)",
    y     = "Densidad"
  ) +
  theme_bw()

Histogramas por grupo con facet_wrap()

facet_wrap() es una de las características más potentes de ggplot2: divide automáticamente el gráfico en paneles según una variable categórica.

ggplot(genes_df, aes(x = longitud, fill = condicion)) +
  geom_histogram(bins = 12, color = "white") +
  scale_fill_manual(values = c(Control = "steelblue", KO = "firebrick")) +
  facet_wrap(~ condicion) +        # un panel por condición
  labs(
    title = "Distribución de longitudes por condición",
    x     = "Longitud (pb)",
    y     = "Frecuencia",
    fill  = "Condición"
  ) +
  theme_bw() +
  theme(legend.position = "none")  # leyenda redundante con las etiquetas

Boxplot con geom_boxplot()

ggplot(genes_df, aes(x = condicion, y = expresion, fill = condicion)) +
  geom_boxplot(outlier.color = "gray40", outlier.size = 1.5) +
  scale_fill_manual(values = c(Control = "steelblue", KO = "firebrick")) +
  labs(
    title = "Expresión génica por condición",
    x     = "Condición",
    y     = "Expresión (CPM)"
  ) +
  theme_bw() +
  theme(legend.position = "none")

Boxplot con puntos superpuestos

En ggplot2 es trivial agregar una capa de puntos sobre el boxplot — algo muy tedioso en R base. Esto es buena práctica cuando el número de observaciones es pequeño.

ggplot(genes_df, aes(x = condicion, y = expresion, fill = condicion)) +
  geom_boxplot(outlier.shape = NA,     # ocultar outliers del boxplot...
               alpha = 0.6) +          # porque los mostraremos como puntos
  geom_jitter(width = 0.15, size = 1.5, alpha = 0.5) +
  scale_fill_manual(values = c(Control = "steelblue", KO = "firebrick")) +
  labs(
    title = "Expresión génica por condición",
    x     = "Condición",
    y     = "Expresión (CPM)"
  ) +
  theme_bw() +
  theme(legend.position = "none")

💡 Nota: geom_jitter() agrega un pequeño desplazamiento aleatorio horizontal a los puntos para que no se solapen. width = 0.15 controla cuánto se dispersan lateralmente.


Gráfico de barras con geom_bar() y geom_col()

# geom_bar() cuenta automáticamente las observaciones por categoría
ggplot(genes_df, aes(x = cromosoma, fill = condicion)) +
  geom_bar(position = "dodge") +     # "dodge": barras una al lado de otra
  scale_fill_manual(values = c(Control = "steelblue", KO = "firebrick")) +
  labs(
    title = "Número de genes por cromosoma y condición",
    x     = "Cromosoma",
    y     = "Número de genes",
    fill  = "Condición"
  ) +
  theme_bw()

Temas predefinidos

ggplot2 incluye varios temas para cambiar la apariencia global del gráfico:

# Gráfico base para comparar temas
g_base <- ggplot(genes_df, aes(x = longitud, y = gc_content, color = condicion)) +
  geom_point(size = 2, alpha = 0.7) +
  scale_color_manual(values = c(Control = "steelblue", KO = "firebrick")) +
  labs(x = "Longitud (pb)", y = "GC content", color = "Condición")

g_base + theme_bw()       # fondo blanco con grilla — muy usado en publicaciones
g_base + theme_classic()  # ejes clásicos sin grilla — muy usado en publicaciones
g_base + theme_minimal()  # minimalista — bueno para presentaciones
g_base + theme_dark()     # fondo oscuro — bueno para pantallas

💡 Para publicaciones científicas: theme_classic() y theme_bw() son los más utilizados porque tienen fondos blancos sin ruido visual. theme_minimal() es una buena opción para presentaciones.


Guardar gráficos con ggsave()

En ggplot2 se usa ggsave() en lugar de png() / dev.off(). Es más simple: guardás el último gráfico generado, o uno específico:

# Guardar el último gráfico generado
ggsave(
  filename = "expresion_por_condicion.png",
  width    = 8,       # pulgadas
  height   = 6,
  dpi      = 300      # resolución para publicación
)

# Guardar un gráfico guardado en un objeto
mi_grafico <- ggplot(genes_df, aes(x = condicion, y = expresion, fill = condicion)) +
  geom_boxplot() +
  theme_bw()

ggsave("boxplot_expresion.pdf", plot = mi_grafico, width = 6, height = 5)

Parte 3 — Ejemplos con contexto genético

En esta sección aplicamos lo aprendido a situaciones más cercanas a lo que encontrarás en bioinformática real.

Datos de práctica

set.seed(7)
n <- 120

# Datos de expresión génica con 3 condiciones y 2 réplicas cada una
datos_exp <- data.frame(
  gen       = rep(paste0("Gen_", sprintf("%02d", 1:20)), times = 6),
  condicion = factor(rep(rep(c("WT","KO_A","KO_B"), each = 20), times = 2),
                     levels = c("WT","KO_A","KO_B")),
  replica   = factor(rep(c("Rep1","Rep2"), each = 60)),
  expresion = c(
    rnorm(20, mean = 8.0, sd = 1.2),   # WT Rep1
    rnorm(20, mean = 8.0, sd = 1.2),   # WT Rep2
    rnorm(20, mean = 5.5, sd = 1.5),   # KO_A Rep1
    rnorm(20, mean = 5.5, sd = 1.5),   # KO_A Rep2
    rnorm(20, mean = 9.2, sd = 1.0),   # KO_B Rep1
    rnorm(20, mean = 9.2, sd = 1.0)    # KO_B Rep2
  ),
  longitud_kb = round(runif(n, 0.5, 8.0), 2),
  gc_content  = round(runif(n, 0.38, 0.68), 3)
)

# Datos de frecuencias alélicas en 4 poblaciones
snps <- c("rs334","rs1799945","rs4988235","rs7903146","rs1426654")
poblaciones <- c("AFR","EUR","EAS","AMR")

freq_df <- data.frame(
  snp        = rep(snps, times = length(poblaciones)),
  poblacion  = factor(rep(poblaciones, each = length(snps))),
  frecuencia = c(
    0.19, 0.00, 0.07, 0.18, 0.09,   # AFR
    0.00, 0.01, 0.68, 0.30, 0.85,   # EUR
    0.00, 0.00, 0.01, 0.26, 0.71,   # EAS
    0.02, 0.00, 0.45, 0.28, 0.76    # AMR
  )
)

Distribución de longitudes génicas

ggplot(datos_exp[datos_exp$replica == "Rep1", ],
       aes(x = longitud_kb)) +
  geom_histogram(bins = 12, fill = "steelblue", color = "white", alpha = 0.85) +
  geom_density(aes(y = after_stat(count) * 0.6),
               color = "firebrick", linewidth = 1) +
  labs(
    title = "Distribución de longitudes génicas",
    x     = "Longitud del gen (kb)",
    y     = "Frecuencia"
  ) +
  theme_classic()

Comparación de expresión entre condiciones

ggplot(datos_exp, aes(x = condicion, y = expresion, fill = condicion)) +
  geom_boxplot(outlier.shape = NA, alpha = 0.7) +
  geom_jitter(width = 0.15, size = 1.2, alpha = 0.4) +
  scale_fill_manual(values = c(WT = "steelblue",
                                KO_A = "firebrick",
                                KO_B = "forestgreen")) +
  labs(
    title = "Expresión génica por condición experimental",
    x     = "Condición",
    y     = "Expresión (log2 CPM)"
  ) +
  theme_classic() +
  theme(legend.position = "none")

Relación entre GC content y longitud génica

ggplot(datos_exp[datos_exp$replica == "Rep1", ],
       aes(x = longitud_kb, y = gc_content, color = condicion)) +
  geom_point(size = 2.5, alpha = 0.8) +
  geom_smooth(method = "lm", formula = y ~ x,
              se = FALSE, linewidth = 0.8) +
  scale_color_manual(values = c(WT = "steelblue",
                                 KO_A = "firebrick",
                                 KO_B = "forestgreen")) +
  labs(
    title  = "Contenido GC en función de la longitud génica",
    x      = "Longitud del gen (kb)",
    y      = "Contenido GC",
    color  = "Condición"
  ) +
  theme_bw()

Frecuencias alélicas en poblaciones

ggplot(freq_df, aes(x = snp, y = frecuencia, fill = poblacion)) +
  geom_col(position = "dodge", color = "white", linewidth = 0.3) +
  scale_fill_manual(values = c(AFR = "#E05A4B",
                                EUR = "steelblue",
                                EAS = "forestgreen",
                                AMR = "darkorange")) +
  scale_y_continuous(limits = c(0, 1), labels = scales::percent) +
  labs(
    title  = "Frecuencias alélicas por SNP y población",
    x      = "SNP",
    y      = "Frecuencia del alelo alternativo",
    fill   = "Población"
  ) +
  theme_bw() +
  theme(axis.text.x = element_text(angle = 30, hjust = 1))

Expresión por condición y réplica con facet_wrap()

ggplot(datos_exp, aes(x = condicion, y = expresion, fill = condicion)) +
  geom_boxplot(outlier.shape = NA, alpha = 0.7) +
  geom_jitter(width = 0.15, size = 1, alpha = 0.4) +
  scale_fill_manual(values = c(WT = "steelblue",
                                KO_A = "firebrick",
                                KO_B = "forestgreen")) +
  facet_wrap(~ replica, labeller = label_both) +
  labs(
    title = "Expresión génica por condición — comparación entre réplicas",
    x     = "Condición",
    y     = "Expresión (log2 CPM)"
  ) +
  theme_bw() +
  theme(legend.position = "none")

💡 Nota: este gráfico con facet_wrap() permite verificar de un vistazo si las dos réplicas biológicas son consistentes entre sí — un control de calidad estándar antes de cualquier análisis de expresión diferencial.


Ejercicios

Ejercicio 1 — R base

Usá el data frame genes_df creado al inicio del módulo.

a) Generá un histograma del gc_content con 12 barras, color "forestgreen" y un título descriptivo.

b) Generá un boxplot que compare la expresión (expresion) entre cromosomas (cromosoma). Asigná un color diferente a cada cromosoma. ¿Hay algún cromosoma con una distribución notablemente diferente?

c) Generá una figura con par(mfrow) que muestre en una cuadrícula 2×2 los siguientes gráficos: - Histograma de longitud - Histograma de expresion - Boxplot de expresion ~ condicion - Dispersión de longitud vs expresion

d) Guardá la figura del inciso c) como archivo PNG de 1800×1400 píxeles a 300 dpi.


Ejercicio 2 — ggplot2

Usá los data frames datos_exp y freq_df creados en la Parte 3.

a) Rehacé el histograma del Ejercicio 1a usando ggplot2. Agregá facet_wrap(~ condicion) para comparar la distribución de gc_content entre condiciones.

b) Generá un gráfico de dispersión de longitud_kb vs expresion coloreado por condicion. Agregá una línea de tendencia con geom_smooth(method = "lm"). Usá theme_classic().

c) Generá un boxplot de expresion por condicion que muestre también los puntos individuales con geom_jitter(). Diferenciá las réplicas usando shape = replica dentro de aes().

d) Con freq_df, generá un gráfico de barras apiladas (position = "stack") que muestre la frecuencia alélica de cada SNP dividida por población. ¿En qué SNP hay mayor diferencia entre poblaciones?

e) Guardá cualquiera de los gráficos anteriores como PDF de 8×6 pulgadas usando ggsave().


Módulo 3 elaborado para Bioinformática — Licenciatura en Genética
IUCBC – CIMETSA · Córdoba, Argentina · 2026
Dr. Danilo G. Ceschin