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
| channel | status | orders |
|---|---|---|
| app | cancelled | 265 |
| app | completed | 3885 |
| marketplace | cancelled | 108 |
| marketplace | completed | 1597 |
| web | cancelled | 353 |
| web | completed | 5367 |
| channel | cancelled | completed |
|---|---|---|
| app | 265 | 3885 |
| marketplace | 108 | 1597 |
| web | 353 | 5367 |
- 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. index='channel'dice qué se queda como filas. Los valores dechannelse conservan y cada uno se escribe una sola vez.columns='status'convierte los valores destatusen encabezados de columna.cancelledycompleteddejan de ser celdas y pasan a ser la parte superior de la cuadrícula.- 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.
- Los mismos seis números, tres filas en vez de seis. Una persona compara canales de un vistazo. Un
groupbyo 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
-
orderslarga: una fila por pedido -
index='channel'pasa a ser las etiquetas de fila -
columns='status'pasa a ser los encabezados de columna -
values='order_id', aggfunc='count'rellena cada celda - La cuadrícula las celdas vacías son NaN
pivot_tableparte de una tabla larga con una fila por observación, comoorders.indexycolumnseligen qué dos columnas definen los ejes de la cuadrícula.valuesyaggfuncdicen qué va en cada celda. Varias filas pueden caer en la misma celda, yaggfunces cómo se combinan: un conteo, una suma o una media.- Una combinación que nunca ocurre deja una celda vacía,
NaN.fill_value=0escribe 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
| channel | y2023 | y2024 | y2025 |
|---|---|---|---|
| app | 819 | 1366 | 2135 |
| marketplace | 464 | 601 | 702 |
| web | 1888 | 2079 | 1946 |
| channel | year | orders |
|---|---|---|
| app | y2023 | 819 |
| app | y2024 | 1366 |
| app | y2025 | 2135 |
| marketplace | y2023 | 464 |
| marketplace | y2024 | 601 |
| marketplace | y2025 | 702 |
| web | y2023 | 1888 |
| web | y2024 | 2079 |
| web | y2025 | 1946 |
- Una hoja de un colega: una columna por año. Se lee bien, pero no puedes hacer
groupby('year')porqueyearno es una columna, son tres. melt(id_vars='channel', var_name='year', value_name='orders')dejachannelcomo está y apila las tres columnas de año en dos: el encabezado antiguo va ayear, la celda va aorders.- Cada fila de la hoja ancha se despliega en tres filas. Entran nueve números, salen nueve números: solo cambió la forma.
- Ahora
yeares una columna. Puedes agrupar por ella, unir con ella o pasarla a un gráfico como el ejex.
Ú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.
No del todo. pivot_table va en el sentido contrario, de larga a ancha. Necesitaría una tabla larga como entrada y haría la hoja aún más ancha.Prueba otra respuesta.
Correcto. melt convierte las 36 columnas de mes en una columna month y una columna de valor. Después funcionan tanto groupby('month') como un gráfico de líneas.
No del todo. groupby necesita una columna por la que agrupar. Aquí 'mes' es el nombre de 36 columnas distintas, no los valores de una.Prueba otra respuesta.
No del todo. crosstab cuenta o reparte combinaciones de dos columnas. No mueve encabezados de columna a una columna.Prueba otra respuesta.
Lección hecha: ya está marcada en el mapa.