Módulo 4: Análisis por grupos - Estudiando al equipo y a los rivales
1. De la jugada al equipo
Hasta ahora, hemos aprendido a ser detectives. Con el filtrado, podemos tomar miles de eventos de un partido y encontrar la jugada exacta que nos interesa: el tiro de un jugador, un pase en una zona concreta, etc. Es una habilidad increíblemente potente.
Sin embargo, el fútbol no es solo una colección de jugadas individuales. Es un deporte de equipo. Para entender realmente la táctica y el rendimiento, necesitamos pasar de analizar la jugada aislada a ver el panorama completo.
Aquí es donde entra en juego la agregación de datos. La idea es simple: en lugar de mirar cada evento uno por uno, vamos a agruparlos para responder preguntas a un nivel superior:
- ¿Qué equipo tuvo más posesión?
- ¿Qué jugador intentó más pases progresivos durante los 90 minutos?
- ¿Cuál fue el promedio de Goles Esperados (xG) por tiro de cada equipo?
Estas son las preguntas que se hacen en una charla técnica o en una reunión de dirección deportiva. En este módulo, aprenderás a usar la herramienta de Pandas que nos permite responderlas: el método .groupby()
.
2. Agrupando datos con .groupby()
La herramienta principal que usaremos para la agregación de datos en Pandas es el método .groupby()
. Su nombre es bastante descriptivo: nos permite agrupar un DataFrame completo basándonos en los valores de una o más columnas.
Para entender cómo funciona, es útil pensar en el proceso que realiza internamente, conocido como “Dividir-Aplicar-Combinar” (Split-Apply-Combine):
- Dividir (Split): Primero,
.groupby()
recorre la columna que le indicamos (por ejemplo, la columna'equipo'
) y divide el DataFrame original en grupos más pequeños. Creará un grupo por cada valor único que encuentre (un grupo para el equipo local, otro para el visitante). - Aplicar (Apply): Luego, a cada uno de estos grupos, le aplicamos una función. Esta función puede ser algo simple como contar el número de filas (
.size()
), sumar los valores de una columna (.sum()
), o calcular el promedio (.mean()
). - Combinar (Combine): Finalmente, Pandas toma los resultados de cada grupo y los combina en un nuevo DataFrame, mucho más pequeño y resumido, que nos muestra el resultado de la operación para cada grupo.
La sintaxis básica para empezar es muy sencilla. Si tenemos nuestro DataFrame de eventos de un partido, llamado match_data
, y queremos agruparlo por equipo, escribiríamos:
grupos_por_equipo = match_data.groupby('h_a')
La variable grupos_por_equipo
ahora contiene un objeto especial de Pandas que tiene los datos separados, listos para que le apliquemos una función. En la siguiente sección, veremos cómo aplicar esas funciones para obtener nuestros primeros resúmenes estadísticos.
3. Comparando el rendimiento de los equipos
Este es nuestro primer análisis agregado. Vamos a tomar todos los eventos del partido y a resumirlos para obtener una visión general de cómo se desempeñó cada equipo. Es el tipo de tabla que verías en un informe post-partido.
En nuestro set de datos, la columna h_a
nos indica si el evento fue realizado por el equipo local (h
de home) o el visitante (a
de away). Usaremos esta columna para agrupar.
En tu Jupyter Notebook, partiendo del match_data
que ya hemos cargado:
# Asumimos que 'match_data' está cargado
# Agrupamos el DataFrame por la columna 'h_a'
grupos_por_equipo = match_data.groupby('h_a')
# Ahora, aplicamos funciones de agregación a estos grupos
# Por ejemplo, contemos cuántos eventos (filas) tiene cada grupo
total_eventos_por_equipo = grupos_por_equipo.size()
print("--- Total de Eventos por Equipo ---")
print(total_eventos_por_equipo)
La salida será una Serie de Pandas muy simple:
--- Total de Eventos por Equipo ---
h_a
a 800
h 950
Name: count, dtype: int64
(Los números son un ejemplo)
Esto ya nos da un primer insight: el equipo local tuvo más participación en el juego. Pero podemos ir mucho más allá. Calculemos la suma de Goles Esperados (xG) para cada equipo, filtrando primero solo por los tiros.
# Primero, filtramos para quedarnos solo con los tiros
tiros = match_data[match_data['result'] == 'Shot']
# Ahora, agrupamos los tiros por equipo y sumamos sus valores de xG
xG_total_por_equipo = tiros.groupby('h_a')['xG'].sum()
print("\n--- xG Total por Equipo ---")
print(xG_total_por_equipo)
Salida:
--- xG Total por Equipo ---
h_a
a 0.89
h 2.15
Name: xG, dtype: float64
Con solo dos líneas de código, hemos obtenido una de las métricas más importantes del análisis de partidos. Esto nos dice que, aunque ambos equipos hubieran empatado a cero, el equipo local generó oportunidades de mucha más calidad.
Esta es la esencia del análisis agregado: pasar de miles de filas de datos crudos a una tabla simple y poderosa que resume la historia del partido. En la siguiente sección, aplicaremos esta misma lógica para evaluar el rendimiento individual de los jugadores.
4. Resumiendo la actuación de un jugador
La misma lógica que usamos para comparar equipos es útil para evaluar el rendimiento de los jugadores a lo largo de un partido. Agrupar por el nombre del jugador nos permite crear una ficha de resumen con sus estadísticas clave, una tarea fundamental en el scouting y el análisis de rendimiento.
Vamos a empezar con una pregunta sencilla: ¿cuántos pases intentó cada jugador del Liverpool en el partido?
# Asumimos que 'match_data' está cargado
# Primero, filtramos los datos para quedarnos solo con los pases del equipo local (Liverpool)
mascara_pases = match_data['result'] == 'Pass'
mascara_liverpool = match_data['h_a'] == 'h'
pases_liverpool = match_data[mascara_pases & mascara_liverpool]
# Ahora, agrupamos este nuevo DataFrame por jugador y contamos los eventos
pases_por_jugador = pases_liverpool.groupby('player').size()
# Ordenamos el resultado para ver quiénes participaron más
pases_por_jugador = pases_por_jugador.sort_values(ascending=False)
print("--- Total de Pases Intentados por Jugador ---")
print(pases_por_jugador.head(10)) # Mostramos el top 10
Salida de ejemplo:
--- Total de Pases Intentados por Jugador ---
player
Trent Alexander-Arnold 110
Virgil van Dijk 95
Alexis Mac Allister 88
...
Esto ya es muy útil, pero ¿qué pasa si queremos ver múltiples estadísticas a la vez? Por ejemplo, para los delanteros, nos interesaría saber cuántos tiros hicieron y cuál fue su xG
total. Para esto, usamos el método .agg()
, que nos permite aplicar diferentes funciones de agregación a diferentes columnas al mismo tiempo.
# Asumimos que el DataFrame 'tiros' del equipo local ya está filtrado
# Agrupamos por jugador y aplicamos múltiples agregaciones
resumen_delanteros = tiros.groupby('player').agg(
total_tiros=('result', 'count'), # Contamos el número de eventos (tiros)
xG_total=('xG', 'sum') # Sumamos el valor de la columna xG
)
# Ordenamos por xG total para ver quién generó más peligro
resumen_delanteros = resumen_delanteros.sort_values(by='xG_total', ascending=False)
print("\n--- Resumen de Tiros y xG por Jugador ---")
print(resumen_delanteros)
Salida dee ejemplo:
--- Resumen de Tiros y xG por Jugador ---
total_tiros xG_total
player
Mohamed Salah 5 0.98
Darwin Núñez 4 0.75
Luis Díaz 2 0.21
Cody Gakpo 2 0.15
Con .agg()
, hemos creado una tabla de resumen de rendimiento ofensivo increíblemente potente. Esta es la base para construir perfiles de jugadores y realizar análisis comparativos.
En la sección final de este módulo, cerraremos con las conclusiones y te daré un avance del siguiente paso en nuestro viaje: cómo combinar diferentes sets de datos para enriquecer aún más nuestros análisis.
5. Conclusiones y siguientes pasos
Has completado un módulo fundamental. La habilidad de agrupar y agregar datos con .groupby()
es lo que separa el análisis superficial del análisis profundo. Hemos pasado de ver el partido jugada por jugada a tener una visión panorámica, como la de un director deportivo.
Ahora puedes tomar miles de eventos y destilarlos en resúmenes claros y concisos que te permiten comparar equipos y evaluar el rendimiento de cada jugador. Has aprendido a responder preguntas complejas que antes eran inaccesibles.
Sin embargo, a menudo, la historia completa no se encuentra en un solo set de datos. Un análisis verdaderamente rico requiere que crucemos información de diferentes fuentes.
En el próximo y último módulo de esta formación intermedia, aprenderemos a hacer exactamente eso. Descubriremos cómo combinar diferentes DataFrames, por ejemplo, cruzando los datos de eventos de un partido con las alineaciones y los minutos jugados por cada futbolista, para enriquecer aún más nuestros análisis y calcular métricas normalizadas, como lo haría un analista profesional.