tidyr: pivot_longer y pivot_wider
¿Qué es “tidy data”?
Una tabla está en formato tidy cuando cumple tres reglas (Wickham, 2014):
- Cada variable tiene su propia columna.
- Cada observación tiene su propia fila.
- Cada valor tiene su propia celda.
Suena trivial. No lo es. La mayoría de los datos del mundo real vienen en formato wide: una variable repartida en varias columnas. Por ejemplo:
pais | ventas_2022 | ventas_2023 | ventas_2024
---------|-------------|-------------|------------
España | 1200 | 1400 | 1600
Francia | 1800 | 2100 | 2400
Para humanos, esto se lee bien. Para análisis con dplyr + ggplot2, no. La variable real es año, pero está embebida en los nombres de columna. Hay que extraerla.
La forma tidy del mismo dato:
pais | año | ventas
---------|------|-------
España | 2022 | 1200
España | 2023 | 1400
España | 2024 | 1600
Francia | 2022 | 1800
Francia | 2023 | 2100
Francia | 2024 | 2400
Ahora cada variable es una columna. group_by(pais), group_by(año), ggplot(aes(x = año, y = ventas, color = pais)), todo trivial.
La regla práctica: wide para humanos, long para análisis.
pivot_longer(): de wide a long
Es el verbo que más usarás. Lo que hace: toma N columnas y las convierte en 2 columnas (nombre + valor) con N veces más filas.
library(tidyr)
ventas_wide |>
pivot_longer(
cols = starts_with("ventas_"),
names_to = "año",
values_to = "ventas"
)Argumentos:
cols: qué columnas pivotar. Acepta selectores tiposelect()(starts_with,where, etc.).names_to: nombre de la nueva columna que contendrá los nombres originales.values_to: nombre de la nueva columna que contendrá los valores.
Si los nombres originales contenían info que quieres separar (por ejemplo ventas_2022_q1), usa names_pattern o names_sep:
ventas |>
pivot_longer(
cols = starts_with("ventas_"),
names_to = c("año", "trimestre"),
names_pattern = "ventas_(\\d{4})_q(\\d)",
values_to = "valor"
)Aquí cada nombre tipo ventas_2022_q3 se parsea en año = "2022" y trimestre = "3". Es uno de los superpoderes que tidyr ofrece y casi nadie usa.
Para casos con un separador fijo, la alternativa más simple:
ventas |>
pivot_longer(
cols = starts_with("ventas_"),
names_to = c("año", "trimestre"),
names_sep = "_",
values_to = "valor"
)pivot_wider(): de long a wide
El inverso. Útil para presentación, tablas resumen, exports a Excel.
ventas_long |>
pivot_wider(
names_from = año,
values_from = ventas
)Argumentos:
names_from: de qué columna salen los nombres de las nuevas columnas.values_from: de qué columna salen los valores.
Cuando una combinación (identificador, name) se repite, pivot_wider se queja y deja una lista en la celda. Para resumir al pivotar, pasa values_fn:
ventas_long |>
pivot_wider(
names_from = año,
values_from = ventas,
values_fn = sum
)Con values_fn = sum, si hay varias filas para la misma combinación, las suma. Patrón típico: agregar y pivotar en una sola operación.
El flujo típico
El patrón más común en análisis real es:
datos_wide |>
pivot_longer(cols = starts_with("año_"), names_to = "año", values_to = "valor") |>
mutate(año = as.integer(año)) |>
filter(año >= 2020) |>
group_by(pais, año) |>
summarise(total = sum(valor, na.rm = TRUE), .groups = "drop") |>
pivot_wider(names_from = año, values_from = total)Léelo: “alargar para analizar, agregar, ensanchar para presentar”. Esa es la cadencia. Long es la forma de trabajo. Wide es la forma de salida.
Más allá de pivots: separate_wider_* y unite()
A veces el problema no es wide vs long sino dentro de una sola columna:
# Columna "nombre_completo" con "Apellido, Nombre"
contactos |>
separate_wider_delim(
cols = nombre_completo,
delim = ", ",
names = c("apellido", "nombre")
)Y el inverso, unite():
contactos |>
unite("nombre_completo", apellido, nombre, sep = ", ")separate_wider_delim (y sus primos _position, _regex) reemplazan al antiguo separate(), que sigue funcionando pero se está soft-deprecating.
Trampas habituales
- Pivotar todo el data frame. Si pasas
cols = everything()por reflejo, te llevas las columnas identificadoras al pivot. Especifica solo las columnas que realmente son la variable repartida. pivot_wider()que genera columnas con nombres no sintácticos. Siañotiene valores como"2024 (provisional)",pivot_widercrea una columna con ese nombre exacto. Para acceder después tienes que usar backticks. Limpia los valores antes de pivotar si vas a operar con ellos por nombre.- Olvidar
values_fnenpivot_wider. Si hay duplicados de la clave conjunta,pivot_widerlos embebe como listas (con un warning fácil de ignorar). Si esperabas valores planos, el bug pasa silencioso. Casi siempre quieresvalues_fn = sum,mean,first, etc. pivot_longerconcols = -id. Funciona y es muy idiomático (“todo menos id”), pero si añades una columna identificadora nueva al dataset, se te pivota sin querer. Lista explícita > negación cuando la composición es delicada.
En la siguiente entrega
Has aprendido a reorganizar tablas entre formatos. Queda un dominio que casi todo dataset real toca y que en R es notoriamente espinoso: las fechas. lubridate es el paquete que las hace tratables, pero hay tres conceptos distintos (duration, period, interval) que conviene entender bien. Es lo siguiente.