Menu

Remodelar datos en R: pivot_wider y pivot_longer (ancho vs largo)

Formatos de datos ancho vs largo y cómo convertir entre ellos: pivot_longer() y pivot_wider() de tidyr, los antiguos nombres spread/gather y reshape() de R base.

Esta página incluye editores ejecutables: edita, ejecuta y ve el resultado al instante.

Ancho vs largo: los mismos datos, dos formas

Toda pregunta de remodelado se reduce a una distinción, así que vamos a verla. Aquí hay un pequeño conjunto de datos — ventas trimestrales de dos ciudades — construido en ambas formas:

Información idéntica, geometría distinta. El formato ancho tiene una fila por ciudad y una columna por trimestre — el trimestre vive en los nombres de columna. El formato largo tiene una fila por observación (ciudad × trimestre), con el trimestre degradado a columna ordinaria y todas las mediciones en una sola columna sales.

Ninguna forma es "la correcta": sirven a amos distintos. El ancho es lo que leen los humanos y lo que exportan las hojas de cálculo: compacto, comparable de un vistazo. El largo es lo que consumen las herramientas, y convertir entre ambos se llama pivotar (o remodelar — es lo mismo).

Por qué el formato largo gana para el análisis

Mira lo que el formato ancho le hizo al concepto "trimestre": trituró una variable en nombres de columna. Nada puede computar con un nombre de columna. En formato largo, quarter vuelve a ser un dato, y todo lo que hay en la caja de herramientas del análisis agrupado se activa:

  • aggregate(sales ~ quarter, ...) o group_by(quarter) — imposible en formato ancho, donde cada trimestre es una columna separada que tratarías a mano (los resúmenes agrupados asumen siempre datos largos).
  • ggplot2 mapea columnas a estéticas: aes(x = quarter, y = sales, color = city) necesita que esas tres columnas existan. Graficar datos anchos es una lucha constante; graficar datos largos es una línea.
  • Añadir Q3 es añadir filas — sin cambio de esquema — mientras que el formato ancho hace crecer una columna nueva de la que todo paso posterior debe enterarse.

La regla práctica: almacena y analiza en largo, presenta en ancho. Por eso las dos conversiones de abajo se usan tanto: los datos llegan anchos desde hojas de cálculo, se alargan para el análisis y se ensanchan de nuevo para la tabla final del informe.

De ancho a largo: pivot_longer()

El paquete tidyr (hermano de dplyr en el tidyverse) es el dueño del remodelado moderno. Sus fragmentos son estáticos aquí — el sandbox solo ejecuta R base. Alargar requiere tres decisiones, un argumento cada una:

library(tidyr)

long <- pivot_longer(wide,
    cols      = c(Q1, Q2),      # which columns to stack
    names_to  = "quarter",      # new column that receives the old NAMES
    values_to = "sales"         # new column that receives the cell VALUES
)

Repasémoslo: cols nombra las columnas que se disuelven (la sintaxis de rango Q1:Q2 y helpers como starts_with("Q") también funcionan — muy útil cuando hay veinte). Cada celda elegida se convierte en una fila; la columna de la que vino aterriza en quarter, el número mismo en sales. Las columnas no listadas en cols (aquí city) se tratan como identificadores y se repiten fila a fila. El resultado es exactamente el data frame long impreso arriba.

De largo a ancho: pivot_wider()

El viaje de vuelta requiere dos decisiones: de dónde salen los nuevos nombres de columna y qué los rellena:

wide <- pivot_wider(long,
    names_from  = quarter,   # distinct values here become new columns
    values_from = sales      # these values fill the cells
)

Cada valor distinto de quarter (Q1, Q2) se convierte en una columna; cada celda se rellena con el valor de sales de la fila que coincide con esa ciudad y ese trimestre. Las columnas restantes (city) actúan como identificadores de fila — una fila de salida por combinación distinta. Una ciudad a la que le falte un trimestre recibe NA en esa celda (el argumento values_fill sustituye otra cosa, p. ej. values_fill = 0 para datos de conteo).

También te encontrarás con la generación anterior en tutoriales antiguos: spread() es pivot_wider() y gather() es pivot_longer() — superadas desde tidyr 1.0, todavía funcionan, no vale la pena aprenderlas más allá de reconocerlas.

R base tiene reshape(): una advertencia honesta

R base puede hacer esto sin paquetes, mediante reshape() — una función tan famosamente confusa que su propia documentación históricamente se disculpaba por ella. Fue diseñada en torno al vocabulario de estudios longitudinales (idvar, timevar, direction), los nombres de los argumentos encajan torpemente con los datos cotidianos, y todo el mundo los olvida entre un uso y el siguiente. Pero funciona:

Fíjate en los nombres de salida: sales.Q1, sales.Q2 — el nombre de la columna de valores fusionado con cada uno. Sirve en un apuro, en un entorno sin dependencias o cuando te la cruzas en código antiguo. Pero si remodelas datos más de una vez al año, install.packages("tidyr") es la recomendación honesta: esta es la única tarea de manipulación donde la herramienta de R base cuesta genuinamente más que la dependencia que ahorra.

La trampa de las claves duplicadas al ensanchar

Ensanchar asume que cada combinación de identificador + nombre determina un valor. Si Lima tiene dos filas Q1, ¿qué número va en la única celda Q1? pivot_wider() se niega a adivinar: emite una advertencia (values are not uniquely identified) y pone una columna-lista en la celda — un data frame con listas anidadas dentro, que rompe lo siguiente que hagas con él.

Cuando veas esa advertencia, no recurras primero a la vía de escape values_fn — pregúntate por qué existen duplicados. Normalmente los datos están a un grano más fino de lo que pensabas (filas diarias, no trimestrales — así que resume primero y ensancha después) o un join anterior duplicó filas. values_fn = mean (o sum) es el arreglo legítimo solo cuando puedas decir en voz alta bajo qué agregación deben colapsar los duplicados. reshape() es peor aquí: se queda en silencio con el primer duplicado y descarta el resto, con solo una advertencia fácil de pasar por alto.

Lo que te llevas

  • Ancho: variables escondidas en nombres de columna, hecho para leer. Largo: una fila por observación, hecho para el análisis y ggplot2.
  • Almacena y analiza en largo, presenta en ancho.
  • pivot_longer(cols, names_to, values_to) apila columnas en filas; las columnas no listadas se convierten en identificadores que se repiten.
  • pivot_wider(names_from, values_from) reparte filas en columnas; los huecos se rellenan con NA.
  • spread()/gather() son los nombres antiguos; reshape() de base funciona pero es famosamente incómoda.
  • Los pares identificador + nombre duplicados hacen ambiguo el ensanchado — resume primero, no te limites a silenciar la advertencia.

A continuación: leer datos reales desde archivos — read.csv() y sus aristas afiladas.

Preguntas frecuentes

¿Cuál es la diferencia entre datos anchos y largos en R?

Los mismos datos, con formas distintas. El formato ancho reparte una variable entre columnas (una columna por trimestre, por ejemplo): una fila por sujeto, pensado para lectura humana. El formato largo la apila en filas: una fila por observación, con una columna de nombres y una columna de valores, pensado para el análisis agrupado y ggplot2.

¿Cómo convierto datos anchos a largos en R?

pivot_longer(df, cols = Q1:Q4, names_to = "quarter", values_to = "sales") de tidyr apila las columnas elegidas en dos nuevas: los antiguos nombres de columna van a la columna names_to y los valores de las celdas a la columna values_to.

¿Cómo convierto datos largos a anchos en R?

pivot_wider(df, names_from = quarter, values_from = sales) de tidyr reparte las filas en columnas: cada valor distinto de names_from se convierte en una columna, rellenada con las entradas correspondientes de values_from. Las combinaciones ausentes se convierten en NA.

¿Qué reemplazó a spread y gather en R?

pivot_wider() reemplazó a spread() y pivot_longer() reemplazó a gather() en tidyr 1.0 (2019). Las funciones antiguas siguen funcionando — las verás en tutoriales antiguos — pero todo código nuevo debería usar el par pivot_*, que tiene argumentos más claros y más funcionalidades.

Coddy programming languages illustration

Aprende a programar con Coddy

COMENZAR