41 · Manipulación: groupby, merge, sort
Split-apply-combine con groupby, joins estilo SQL con merge y ordenación — las tres operaciones que subyacen a casi toda pipeline de feature engineering.
La mayoría de las transformaciones de datos son uno de tres patrones: **split-apply-combine** (agrupar filas por una clave, computar algo dentro de cada grupo, rearmar — esto es `groupby`), **join** (combinar dos tablas por una clave compartida — esto es `merge`), o **sort** (reordenar filas por valor — esto es `sort_values`). Dominar estos tres desbloquea el feature engineering: puedes convertir un registro de transacciones sin procesar en una matriz de features por usuario en menos de diez líneas.
Sin esto:
Sin `groupby`, calcular agregaciones por categoría (valor medio de compra por región, tasa de abandono por segmento de usuario, tasa de clics por campaña publicitaria) requeriría dividir manualmente el DataFrame en subsets, iterar, aplicar y concatenar — veinte líneas para lo que `groupby().mean()` hace en una. Sin `merge`, combinar información de diferentes tablas (clientes + pedidos + productos) requeriría bucles explícitos sobre búsquedas de índice. Estas dos operaciones juntas son el motor del 90% del feature engineering.
El patrón split-apply-combine es el modelo mental detrás de groupby:
- Split — particionar el DataFrame en grupos basados en una columna clave
- Apply — computar algo dentro de cada grupo (sum, mean, count, función personalizada)
- Combine — rearmar los resultados por grupo en un nuevo DataFrame
df.groupby('region')['revenue'].sum() hace los tres pasos en una expresión. El resultado es una Series con los valores únicos de 'region' como índice.
Para agregaciones de múltiples columnas, .agg() acepta un dict que mapea nombres de columna a funciones de agregación:
df.groupby('region').agg({'revenue': ['mean', 'std', 'count']})
Esto devuelve un DataFrame con un MultiIndex en las columnas — el nivel exterior es el nombre de la columna, el nivel interior es el nombre de la función de agregación.
Merge implementa joins relacionales. Los cuatro modos how replican SQL:
inner— conservar solo las filas cuya clave aparece en ambas tablasleft— conservar todas las filas de la tabla izquierda;NaNpara filas derechas sin coincidenciaright— conservar todas las filas de la tabla derecha;NaNpara filas izquierdas sin coincidenciaouter— conservar todas las filas de ambas tablas;NaNdonde no haya coincidencia
El valor por defecto es inner. Siempre verifica .shape antes y después de un merge.
Python (in browser)
`.groupby('region')['revenue'].sum()` es el split-apply-combine de tres pasos en una línea. Encadenar `.sort_values(ascending=False)` te da inmediatamente el ranking — útil para dashboards, tablas de importancia de features y cualquier análisis de 'top N'.
Python runs entirely in your browser via Pyodide (~6 MB on first Run, cached after).
Python (in browser)
`.agg({'col': ['mean', 'std', 'count']})` devuelve un DataFrame con un MultiIndex en las columnas. Para aplanarlo para uso posterior: `agg_result.columns = ['_'.join(c) for c in agg_result.columns]`. En ML, groupby + agg es el motor del feature engineering — `df.groupby('user_id').agg({'purchase': ['sum', 'count', 'mean']})` crea tres features a nivel de usuario desde un registro de transacciones crudo en una sola llamada.
Python runs entirely in your browser via Pyodide (~6 MB on first Run, cached after).
Python (in browser)
Un join `left` conserva cada fila de la tabla izquierda (clientes) y adjunta las filas coincidentes de la derecha (pedidos). Los clientes sin pedidos obtienen `NaN` en las columnas de pedido — exactamente lo que quieres al crear un feature de 'gasto total por cliente' (luego harías `.fillna(0)` en el monto). El inner join descartaría silenciosamente a los clientes sin pedidos.
Python runs entirely in your browser via Pyodide (~6 MB on first Run, cached after).
Elegir el `how` incorrecto es un bug silencioso. `inner` descarta filas silenciosamente; `outer` introduce `NaN` silenciosamente. Siempre verifica con `.shape` antes y después del merge — si el número de filas aumenta inesperadamente, verifica si hay claves duplicadas en una de las tablas (los joins uno-a-muchos multiplican las filas).
¿Qué hace `df.groupby('col').mean()` cuando el DataFrame tiene columnas no numéricas?
- `.groupby('clave').agg(...)` es split-apply-combine: partir por clave, aplicar una agregación dentro de cada grupo, devolver un nuevo DataFrame indexado por los valores únicos de clave.
- `.merge(otro, on='clave', how='left|right|inner|outer')` es un join relacional. Verifica `.shape` antes y después — un crecimiento inesperado de filas significa claves duplicadas (explosión cartesiana).
- `.sort_values('col', ascending=False)` ordena filas. `.sort_index()` ordena por el índice de fila.
- En pandas 2.0+, las agregaciones numéricas sobre un `groupby` omiten silenciosamente las columnas no numéricas. Selecciona las columnas explícitamente antes de agregar.
El feature engineering es mayormente `groupby`. `df.groupby('user_id')['purchase'].sum()` crea un feature de gasto total por usuario en una línea. Los datos de entrenamiento y prueba a menudo viven en tablas separadas unidas con `.merge()`. Los pesos de muestra equilibrados por clase usan `groupby('label').transform('count')`. Las comparaciones de modelos estilo leaderboard usan `groupby('model').agg({'metric': ['mean', 'std']})`.
Si lo quitas: Sin `groupby`, computar agregaciones a nivel de usuario desde un registro de una fila por transacción requeriría escribir bucles Python explícitos — tanto más lentos como más propensos a errores. Sin `merge`, combinar tablas de features con tablas de etiquetas requeriría alineación manual de índices que falla silenciosamente cuando los órdenes de filas difieren. Estas dos operaciones son tan fundamentales que la documentación de pandas las llama 'el pan y la mantequilla de la manipulación de datos'.