Caso completo: del CSV bruto a un informe Quarto

r
tidyverse
Un análisis real con tidyverse de principio a fin: lectura, limpieza, transformación, agregación, visualización y publicación. Junta los 11 tutoriales anteriores en un proyecto reproducible.
Autor/a

Tu nombre

El cierre de la ruta

Has completado 11 tutoriales. Cada uno te enseñó una pieza del tidyverse. Esta entrega es distinta: no introduce conceptos nuevos. Toma todo lo aprendido y lo aplica en un análisis real de principio a fin, desde un dataset bruto hasta un informe Quarto publicable.

El dataset que vamos a usar es público y reproducible: nycflights13, el dataset canónico que Hadley Wickham emplea en R for Data Science. Se acompaña con tablas relacionales (vuelos, aerolíneas, aeropuertos, aviones, meteorología), perfectas para mostrar joins reales sin necesidad de descargar nada externo.

El problema

Te dan un encargo: “¿qué aerolíneas son más puntuales saliendo de los aeropuertos de Nueva York durante 2013? ¿Cambia la puntualidad según el mes?”. Tienes que entregar un informe.

Preparación del proyecto

Sigue la estructura del tutorial 2:

analisis-vuelos/
├── analisis-vuelos.Rproj
├── data/                 # datos crudos (no se modifican)
├── scripts/
│   └── 01_analisis.R
└── output/
    └── informe.qmd

Instala los paquetes:

install.packages(c("tidyverse", "lubridate", "nycflights13", "scales"))

nycflights13 trae cinco tablas listas para usar: flights, airlines, airports, planes, weather. No necesitas CSVs externos para este ejercicio.

Paso 1: cargar y explorar

library(tidyverse)
library(lubridate)
library(nycflights13)

# La tabla principal: 336 776 vuelos en 2013
flights

# Aerolíneas (códigos a nombres legibles)
airlines

# Aeropuertos (códigos FAA a nombres)
airports

Inspecciona rápido:

glimpse(flights)

Columnas clave para nuestro análisis:

  • year, month, day, la fecha del vuelo.
  • carrier: código de la aerolínea (dos letras).
  • origin: aeropuerto de origen (JFK, LGA, EWR).
  • dep_delay: retraso de salida en minutos (negativo = anticipo).
  • arr_delay: retraso de llegada en minutos.

Paso 2: limpiar y enriquecer

Descartamos vuelos cancelados (sin dep_delay) y combinamos year/month/day en una fecha real:

vuelos <- flights |>
  filter(!is.na(dep_delay)) |>
  mutate(fecha = make_date(year, month, day)) |>
  select(fecha, month, carrier, origin, dep_delay, arr_delay)

make_date() de lubridate toma year, month, day y devuelve un objeto Date real. Pasamos de manejar tres columnas a una sola fecha estructurada.

Ahora añadimos los nombres reales de aerolíneas y aeropuertos con joins:

vuelos <- vuelos |>
  left_join(airlines, by = "carrier") |>
  left_join(airports, by = c("origin" = "faa")) |>
  select(fecha, month, aerolinea = name.x, aeropuerto = name.y, dep_delay, arr_delay)

Dos cosas para fijarse:

  • by = c("origin" = "faa") empareja columnas con nombres distintos.
  • Tras los dos joins, ambas tablas auxiliares tenían una columna name. dplyr las renombra automáticamente a name.x (de airlines) y name.y (de airports). Las renombramos en el select para que sean legibles aguas abajo.

Paso 3: validar las claves

Antes de agregar, comprueba que los joins no han duplicado filas:

# Las tablas auxiliares deberían tener una fila por clave
airlines |> count(carrier) |> filter(n > 1)   # debe estar vacío
airports |> count(faa)     |> filter(n > 1)   # debe estar vacío

Si esto devuelve filas, alguna tabla auxiliar tiene duplicados y el join los ha propagado. En este caso airlines y airports son tablas de referencia limpias, así que no hay sorpresa. En datos reales no lo asumas: comprueba siempre.

Paso 4: agregar por aerolínea y mes

Calculamos la mediana del retraso de salida y el porcentaje de vuelos a tiempo:

puntualidad <- vuelos |>
  group_by(aerolinea, month) |>
  summarise(
    n_vuelos        = n(),
    mediana_retraso = median(dep_delay, na.rm = TRUE),
    pct_a_tiempo    = mean(dep_delay <= 0, na.rm = TRUE),
    .groups = "drop"
  )

Tres detalles importantes:

  • n() cuenta vuelos por grupo. Útil para descartar aerolíneas con muestra pequeña.
  • median(dep_delay) es más informativa que la media para datos sesgados como retrasos: un solo vuelo de nueve horas distorsiona la media. La mediana no.
  • mean(dep_delay <= 0) calcula el porcentaje de vuelos puntuales, la media de un booleano (TRUE/FALSE) es la proporción de TRUE.

Filtramos aerolíneas con pocos vuelos para evitar ruido:

puntualidad <- puntualidad |>
  filter(n_vuelos >= 100)

Paso 5: pivotar para presentación

Para una tabla resumen orientada a humanos, queremos los meses en columnas:

tabla_resumen <- puntualidad |>
  select(aerolinea, month, pct_a_tiempo) |>
  pivot_wider(
    names_from   = month,
    values_from  = pct_a_tiempo,
    names_prefix = "M"
  )

tabla_resumen

Para análisis y visualización, en cambio, el formato long es mejor, así que dejamos puntualidad en long y solo creamos tabla_resumen para la tabla final. Pivot wider es para presentación, no para trabajo.

Paso 6: visualización

Un preview rápido de ggplot2 (lo veremos a fondo en la Ruta 2):

library(scales)

puntualidad |>
  ggplot(aes(x = month, y = mediana_retraso, color = aerolinea)) +
  geom_line(linewidth = 0.6) +
  geom_point(size = 1.4) +
  scale_x_continuous(breaks = 1:12, labels = month.abb) +
  labs(
    title    = "Mediana de retraso de salida por aerolínea y mes",
    subtitle = "Vuelos desde NYC, 2013",
    x        = NULL,
    y        = "Mediana de retraso (minutos)",
    color    = "Aerolínea"
  ) +
  theme_minimal()

Vas a notar patrones estacionales claros: junio y julio peores (vacaciones + tormentas de verano), otoño mejor. Eso es insight real, no decoración del informe.

Paso 7: el informe en Quarto

El paso final es empaquetar el análisis en un documento Quarto que un humano pueda leer.

Crea output/informe.qmd con esta estructura (los bloques r aquí abajo son ilustrativos. En tu archivo .qmd real serán chunks ejecutables {r} para que Quarto los corra al renderizar):

---
title: "Puntualidad de aerolíneas — NYC 2013"
author: "Tu nombre"
format:
  html:
    toc: true
    code-fold: true
execute:
  echo: false
  warning: false
---

## Resumen ejecutivo

[Tu narrativa aquí: hallazgos principales en 2-3 párrafos]

```r
library(tidyverse)
library(lubridate)
library(nycflights13)
library(scales)

# El pipeline que construiste en los pasos 1-6
```

## Puntualidad por aerolínea y mes

```r
# El ggplot del paso 6
```

## Tabla detallada

```r
# La tabla pivotada del paso 5
```

Renderizas con:

quarto::quarto_render("output/informe.qmd")

O desde RStudio con el botón Render. El resultado es un informe.html autocontenido que puedes enviar por email, subir a GitHub Pages o adjuntar a una presentación.

Lo que has hecho

En este caso completo has aplicado los once tutoriales anteriores en orden:

  1. Instalación + proyecto + pipe (tutoriales 1-3), la base.
  2. readr (tutorial 4), nycflights13 ya viene cargado, pero el patrón es el mismo cuando lees CSVs externos.
  3. filter, select, mutate (tutoriales 5-7), limpiar y enriquecer.
  4. group_by + summarise (tutorial 8), agregación por aerolínea y mes.
  5. joins (tutorial 9), combinar con tablas de aerolíneas y aeropuertos.
  6. pivot_wider (tutorial 10), para la tabla de presentación.
  7. lubridate::make_date (tutorial 11), combinar y/m/d en una fecha real.

Y has cerrado el ciclo con Quarto, un primer contacto con la Ruta 8 (Reproducibilidad con Quarto).

¿Quieres ir más a fondo?

Esta ruta te ha llevado de cero a un análisis completo. El libro Tidyverse para manejo de datos en R desarrolla esta misma materia en 80 páginas, con:

  • Un caso real más largo (ventas multi-tienda, no datos académicos) seguido de principio a fin.
  • Ejercicios resueltos con código completo.
  • Datasets descargables en formatos típicos del mundo real (Excel mal formateado, CSV con encoding raro, mezcla de fechas en formato europeo y anglosajón).
  • Capítulo de troubleshooting con las veinte trampas que más cuestan.

Edición digital (PDF), pago único, actualizaciones de la edición vigente incluidas.


Has terminado la ruta Tidyverse

Felicidades. Si quieres seguir aprendiendo R, las dos rutas que naturalmente continúan son:

  • Visualización con ggplot2: para llevar tus gráficos del quick and dirty a publicables.
  • Reproducibilidad con Quarto: para que tu trabajo sea ejecutable en otra máquina mañana sin pelearte con dependencias.

Ambas están en el listado de Rutas.