Python · Capítulo 9 · Lección de conceptos · 7 min de lectura

Tablas largas para calcular, cuadrículas anchas para leer

Los mismos números pueden estar en una tabla larga, una fila por observación, o en una cuadrícula ancha, una columna por categoría. Reformar es pasar de una a otra, sin añadir ni perder nada por el camino.

La idea

Cualquier tabla de números puede tener dos formas. En una tabla larga, cada fila es una observación, y las cosas que la describen (canal, estado, año) tienen cada una su columna. En una tabla ancha, algunas de esas descripciones han pasado a los encabezados de columna, así que una fila contiene varias observaciones una al lado de otra.

Ninguna es mejor. Son para lectores distintos. La larga es para calcular: groupby, merge, query y los gráficos quieren apuntar a un nombre de columna, y una tabla larga tiene una columna por cada cosa por la que podrías agrupar. La ancha es para leer: una cuadrícula con los canales a un lado y los estados arriba pone la comparación delante de tus ojos.

Reformar es pasar de una a otra. No añade información ni quita ninguna. Los mismos números quedan en sitios distintos, así que cambian el número de filas y de columnas mientras el total se mantiene.

Figura 1

Los mismos seis números en dos formas: pedidos por canal y estado

Larga: resultado de groupby
channelstatusorders
appcancelled265
appcompleted3885
marketplacecancelled108
marketplacecompleted1597
webcancelled353
webcompleted5367
Ancha: cuadrícula de pivot_table
channelcancelledcompleted
app2653885
marketplace1081597
web3535367

Paso 1 de 5 Un groupby(['channel', 'status']).size() da una tabla larga: una fila por combinación, con el número en su propia columna. A los ordenadores les gusta esta forma.

  1. Un groupby(['channel', 'status']).size() da una tabla larga: una fila por combinación, con el número en su propia columna. A los ordenadores les gusta esta forma.
  2. index='channel' dice qué se queda como filas. Los valores de channel se conservan y cada uno se escribe una sola vez.
  3. columns='status' convierte los valores de status en encabezados de columna. cancelled y completed dejan de ser celdas y pasan a ser la parte superior de la cuadrícula.
  4. Cada fila larga cae en la celda donde se cruzan su etiqueta de fila y su etiqueta de columna. Los 265 pedidos cancelados y 3,885 completados de la app quedan uno al lado del otro.
  5. Los mismos seis números, tres filas en vez de seis. Una persona compara canales de un vistazo. Un groupby o un gráfico siguen queriendo la forma larga.

Por qué existe

Un groupby por dos columnas te da 21 combinaciones, una debajo de otra. Es la respuesta correcta y una cosa terrible para poner delante de un director financiero. Mientras tanto, la hoja que te manda Marketing tiene una columna por canal, y no puedes agrupar por "canal" porque no es una columna.

Los mismos datos pasan de mano en mano entre personas que necesitan formas distintas. Los analistas calculan en largo, los directivos leen en ancho, y una exportación de otra herramienta puede llegar en cualquiera de las dos. Conocer las dos formas, y los movimientos de una línea entre ellas, te ahorra copiar números a mano.

Hay una prueba práctica para saber qué forma tienes. Pregunta si un valor por el que quieres agrupar, filtrar o colorear está en una columna o en un encabezado. Si está en un encabezado, la tabla es ancha en ese aspecto, y reformas antes de hacer cualquier otra cosa. Cuando la respuesta es un nombre de columna, la mayor parte del código de pandas se escribe solo.

Cómo funciona por dentro

De largo a ancho: elige tres cosas

Para pasar a ancho respondes tres preguntas. ¿Qué columna pasa a ser las filas? ¿Los valores de qué columna pasan a ser los encabezados de columna? ¿Qué va en cada celda? pivot_table(index=..., columns=..., values=..., aggfunc=...) son esas respuestas en orden.

Figura 2

Cómo leer una llamada a pivot_table

  1. orders larga: una fila por pedido
  2. index='channel' pasa a ser las etiquetas de fila
  3. columns='status' pasa a ser los encabezados de columna
  4. values='order_id', aggfunc='count' rellena cada celda
  5. La cuadrícula las celdas vacías son NaN

Paso 1 de 4 pivot_table parte de una tabla larga con una fila por observación, como orders.

  1. pivot_table parte de una tabla larga con una fila por observación, como orders.
  2. index y columns eligen qué dos columnas definen los ejes de la cuadrícula.
  3. values y aggfunc dicen qué va en cada celda. Varias filas pueden caer en la misma celda, y aggfunc es cómo se combinan: un conteo, una suma o una media.
  4. Una combinación que nunca ocurre deja una celda vacía, NaN. fill_value=0 escribe 0 cuando el cero es la respuesta verdadera.

El cuarto argumento es lo que lo hace pivot_table y no pivot. Cuando varias filas largas caen en una celda, alguien tiene que combinarlas. aggfunc='sum' suma, 'count' cuenta, 'mean' promedia. El pivot simple no tiene aggfunc y lanza un error cuando dos filas quieren la misma celda, lo cual es útil cuando esperabas exactamente una.

Las celdas que ninguna fila alcanzó son NaN, no cero. Un estado que nunca ocurre en cierto canal no tiene ninguna fila, así que la cuadrícula tiene un hueco. fill_value=0 lo rellena, y es la elección correcta para los conteos, donde no tener filas significa de verdad cero. En una media, una celda vacía significa "sin datos", y rellenarla con 0 diría que la media es cero.

De ancho a largo: melt

melt es el movimiento contrario. id_vars son las columnas que se quedan, var_name es el nombre de la nueva columna que contiene los encabezados antiguos y value_name nombra la columna que contiene las celdas.

Figura 3

melt: un año por columna pasa a ser un año por fila

Ancha: una columna por año
channely2023y2024y2025
app81913662135
marketplace464601702
web188820791946
Larga: después de melt
channelyearorders
appy2023819
appy20241366
appy20252135
marketplacey2023464
marketplacey2024601
marketplacey2025702
weby20231888
weby20242079
weby20251946

Paso 1 de 4 Una hoja de un colega: una columna por año. Se lee bien, pero no puedes hacer groupby('year') porque year no es una columna, son tres.

  1. Una hoja de un colega: una columna por año. Se lee bien, pero no puedes hacer groupby('year') porque year no es una columna, son tres.
  2. melt(id_vars='channel', var_name='year', value_name='orders') deja channel como está y apila las tres columnas de año en dos: el encabezado antiguo va a year, la celda va a orders.
  3. Cada fila de la hoja ancha se despliega en tres filas. Entran nueve números, salen nueve números: solo cambió la forma.
  4. Ahora year es una columna. Puedes agrupar por ella, unir con ella o pasarla a un gráfico como el eje x.

Úsalo siempre que los encabezados sean en realidad valores: años, meses, canales. Una señal clara es que quieres recorrer las columnas en un bucle, o te pillas escribiendo la misma línea cinco veces con un nombre de columna distinto.

Las versiones con índice: unstack y stack

Un groupby con dos claves devuelve una Serie con un índice de dos niveles. unstack() mueve el nivel interior a las columnas, que es un pivot hecho sobre el resultado de un groupby, sin necesidad de aggfunc porque la agrupación ya agregó. stack() hace lo contrario y devuelve las columnas al nivel interior del índice. Es el mismo movimiento que pivot_table y melt, sobre una tabla cuyas etiquetas están en el índice.

Proporciones: crosstab

pd.crosstab(df.channel, df.status) cuenta las combinaciones directamente, sin una columna values. Añade normalize='index' y cada fila suma 1, así que lees la mezcla de estados dentro de cada canal como una proporción. Elegir 'index', 'columns' o 'all' decide de qué son los porcentajes: de la fila, de la columna o del total. Dilo en el título de la tabla, porque "12% de qué" es toda la pregunta.

Otra forma de reformar: explode

Algunas columnas guardan una lista en una celda, como las etiquetas de un producto. explode da a cada elemento de la lista su propia fila y repite las demás columnas, así que crea filas como lo hace un merge. Contar productos después de un explode cuenta etiquetas, no productos. Se aplica el mismo cuidado que con un fan-out.

Errores comunes

"Una tabla dinámica cambia los datos"

Los reorganiza. Suma las celdas de un pivot de conteos y obtienes el número de filas con el que empezaste: los seis números del diagrama suman 11,575 en ambas formas. Si el total de una tabla reformada difiere del original, un filtro, un aggfunc o un dropna quitó algo. Esa comprobación cuesta una línea.

"Ancho es la forma correcta y largo es el desorden"

Largo es la forma que prefieren pandas y la mayoría de herramientas de gráficos. Una tabla con una columna por año es fácil de leer y difícil de analizar: añadir 2026 significa añadir una columna y cambiar el código. Una tabla larga simplemente gana filas. Piensa en lo ancho como una capa de presentación que se hace al final.

"Las celdas vacías de un pivot significan cero"

Significan que ninguna fila llegó a esa celda. En un conteo eso equivale a cero. En una media, una tasa o un precio medio es "desconocido", y fillna(0) arrastraría hacia abajo todo cálculo posterior. Decide en cada tabla si una celda vacía es un cero o un desconocido, y luego rellénala o déjala.

Ponte a prueba

Una pregunta. Al responderla, la lección queda marcada en el mapa; nunca bloquea un nivel.

Una hoja tiene una fila por canal y una columna por mes, 36 columnas de mes. Quieres la media mensual de todos los canales en un gráfico con los meses en el eje x. ¿Qué haces primero?

Elige qué puede ejecutarse en este sitio. Puedes cambiarlo cuando quieras desde "Configurar cookies", al pie de cada página.