2021

Datos gratuitos: La Euro 2020 con StatsBomb 360

By StatsBomb | noviembre 17, 2021
2021

Datos gratuitos: La Euro 2020 con StatsBomb 360

La semana pasada, agregamos los datos de la última temporada de Lionel Messi en el Barcelona a nuestra base de datos gratuitos. Esta semana, tenemos algo muy especial para la comunidad de analistas e investigadores: datos gratuitos de la Euro 2020, y no solo los datos de evento normales sino también toda la información adicional que proporciona nuestro nuevo producto StatsBomb 360.

¿Qué es StatsBomb 360?

StatsBomb 360 es la próxima revolución en los datos de evento de fútbol. Ya revolucionamos la industria en 2018 con el lanzamiento de StatsBomb Data y la inclusión de una imagen de cada tiro, un freeze frame, que nos proporciona la posición tanto del portero como de los atacantes y defensores en el momento del tiro. Con StatsBomb 360, recogemos un freeze frame para todos los eventos en un partido, mostrando la posición de todos los jugadores en la pantalla. Esta información adicional abre muchas posibilidades de análisis, cosas como:

  • Pases que rompen líneas
  • Recepciones entre líneas, al pie, o al espacio
  • Distancia respecto a los defensores
  • Líneas de pase abiertas… ¡y cerradas!
  • Defensive Island Events (DIEs) - cuando equipos crean situaciones de 1v1 donde los defensores están sin coberturas/ayudas cercanas
  • Formación defensiva en cada evento

Esto solo es la punta del iceberg. Nuestros clientes ya han empezado a analizar y sacar provecho de la esta nueva información y nosotros mismos estamos generando nuevas ideas día a día.

Ahora, vosotros tenéis la oportunidad de investigar los datos y hacer su propio análisis.

Cabe mencionar que si solo queréis aprovechar de los datos de evento normales de la Euro 2020 podéis hacerlo sin problema alguno. Podéis usar nuestra guía vigente sobre el uso de StatsBomb Data en R, utilizar nuestra herramienta de Python, statsbombpy, para acceder a los datos o buscar en la web una de las varias guías escritas por gente de la comunidad.

Pero aquí, os daremos el código del software de programación R para empezar a trabajar con los datos de StatsBomb 360. Os mostraremos como encontrar los Freeze Frame de StatsBomb 360, combinarlos con los datos de evento normales, utilizarlos para calcular la cantidad de atacantes y defensores en el área en todos los centros de la Euro 2020 y finalmente, hacer un plot de un centro dado con las localizaciones de todos los jugadores en la pantalla.

Antes de nada, hay que actualizar el paquete StatsBombR para tener acceso a las nuevas funciones relacionadas a StatsBomb 360: get.360matchFree y free_allevents_360.

Ya con esto hecho podemos empezar el proceso de importación. Primero, importamos los datos de evento normal de la Euro 2020.

library(tidyverse)
library(StatsBombR)

Comp <- FreeCompetitions() %>%
filter(competition_id==55 & season_id==43)

Matches <- FreeMatches(Comp)

events <- free_allevents(MatchesDF = Matches, Parallel = T)

events = allclean(events)

events = get.opposingteam(events)

Luego, importamos los datos de StatsBomb 360, los combinamos con los datos normales y guardamos este conjunto de datos para uso futuro.

datos360 <- free_allevents_360(MatchesDF = Matches, Parallel = T)

datos360 = datos360 %>% rename(id = event_uuid)

events = events %>% left_join(datos360, by = c("id" = "id"))

events = events %>% rename(match_id = match_id.x) %>% select(-match_id.y)

save(events, file = "Euro_2020_con_360.RData")

Con el conjunto de datos guardado, ahora lo utilizaremos para encontrar los 10 centros de la Euro 2020 en los que habían más atacantes en el área.

Primero, reducimos la cantidad de eventos con los que estamos trabajando para allanar un poco el próximo proceso, filtrando los eventos para incluir solo a los pases.

ffs = events %>%
group_by(team.name) %>%
filter(type.name=="Pass") %>%
select(id, match_id, team.name, OpposingTeam, player.name, type.name, minute, second, location.x, location.y, pass.end_location.x, pass.end_location.y, pass.type.name, pass.cross, freeze_frame)

Luego, desplegamos los freeze frame para tener las localizaciones de los jugadores en cada uno de ellos en el mismo dataframe que los eventos a las que se relacionan.

ffs = ffs %>% unnest(freeze_frame) %>%
mutate(ff_location.x = (map(location, 1)), ff_location.y = (map(location, 2))) %>%
select(-location) %>%
mutate(ff_location.x = as.numeric(ifelse(ff_location.x == "NULL", NA, ff_location.x)), ff_location.y = as.numeric(ifelse(ff_location.y == "NULL", NA, ff_location.y)))

Finalmente, filtramos otra vez para incluir solo a los centros en juego dinámico que acabaron en el área de penalti y calculamos la cantidad de atacantes y defensores en el área en cada uno de ellos. Luego, ordenamos los centros por la cantidad de atacantes en el área y hacemos una lista de los 10 centros en los que habían más atacantes en el área.

centros = ffs %>%
filter(pass.end_location.x>102 & pass.end_location.y>18 & pass.end_location.y<62) %>%
filter(is.na(pass.type.name) | pass.type.name=="Recovery" | pass.type.name=="Interception")%>%
filter(pass.cross==TRUE) %>%
filter(keeper==FALSE) %>%
group_by(team.name, OpposingTeam, id) %>%
summarise(atacantes = sum(teammate==TRUE & actor==FALSE & ff_location.x>102 & ff_location.y>18 & ff_location.y<62, na.rm = TRUE), defensores = sum(teammate==FALSE & ff_location.x>102 & ff_location.y>18 & ff_location.y<62, na.rm = TRUE), ata_v_def = atacantes-defensores) %>%
ungroup() %>%
arrange(desc(atacantes)) %>%
slice(1:10)

El resultado:

Ahora, haremos un plot de las localizaciones de los jugadores tanto del equipo atacante como del equipo defensivo en uno de estos centros.

Primero, filtramos para incluir solo al centro con más atacantes en el área (Polonia vs. Suecia), cogiendo el id de este centro del dataframe previo, y creamos una nueva columna para nombrar los cuatro tipos de jugador: el que realiza el centro, un compañero de este jugador, un jugador rival o el portero rival.

chart = ffs %>%
filter(id=="57074cd6-5525-49d5-8528-be4bfb329e9b") %>%
mutate(tipo_de_jugador = case_when(actor==TRUE & teammate==TRUE ~ "Activo", teammate==TRUE ~ "Compañero", teammate==FALSE & keeper==FALSE ~ "Rival", keeper==TRUE & teammate==FALSE ~ "Portero"))

De ahí, podemos hacer el plot.

ggplot() +
annotate("rect",xmin = 0, xmax = 120, ymin = 0, ymax = 80, fill = NA, colour = "black", size = 0.6) +
annotate("rect",xmin = 0, xmax = 60, ymin = 0, ymax = 80, fill = NA, colour = "black", size = 0.6) +
annotate("rect",xmin = 18, xmax = 0, ymin = 18, ymax = 62, fill = NA, colour = "black", size = 0.6) +
annotate("rect",xmin = 102, xmax = 120, ymin = 18, ymax = 62, fill = NA, colour = "black", size = 0.6) +
annotate("rect",xmin = 0, xmax = 6, ymin = 30, ymax = 50, fill = NA, colour = "black", size = 0.6) +
annotate("rect",xmin = 120, xmax = 114, ymin = 30, ymax = 50, fill = NA, colour = "black", size = 0.6) +
annotate("rect",xmin = 120, xmax = 120.5, ymin =36, ymax = 44, fill = NA, colour = "black", size = 0.6) +
annotate("rect",xmin = 0, xmax = -0.5, ymin =36, ymax = 44, fill = NA, colour = "black", size = 0.6) +
annotate("segment", x = 60, xend = 60, y = -0.5, yend = 80.5, colour = "black", size = 0.6)+
annotate("segment", x = 0, xend = 0, y = 0, yend = 80, colour = "black", size = 0.6)+
annotate("segment", x = 120, xend = 120, y = 0, yend = 80, colour = "black", size = 0.6)+
theme(rect = element_blank(),
line = element_blank()) +
# add penalty spot right
annotate("point", x = 108 , y = 40, colour = "black", size = 1.05) +
annotate("path", colour = "black", size = 0.6, x=60+10*cos(seq(0,2*pi,length.out=2000)), y=40+10*sin(seq(0,2*pi,length.out=2000)))+
# add centre spot
annotate("point", x = 60 , y = 40, colour = "black", size = 1.05) + annotate("path", x=12+10*cos(seq(-0.3*pi,0.3*pi,length.out=30)), size = 0.6, y=40+10*sin(seq(-0.3*pi,0.3*pi,length.out=30)), col="black") +
annotate("path", x=107.84-10*cos(seq(-0.3*pi,0.3*pi,length.out=30)), size = 0.6, y=40-10*sin(seq(-0.3*pi,0.3*pi,length.out=30)), col="black") +
geom_point(data = chart, aes(x = ff_location.x, y = ff_location.y, fill=tipo_de_jugador), size = 6, alpha = 0.8, shape=21) + #3
theme(axis.text.x=element_blank(),
axis.title.x = element_blank(),
axis.title.y = element_blank(),
plot.caption=element_text(size=13,family="Source Sans Pro", hjust=0.5, vjust=0.5),
plot.subtitle = element_text(size = 18, family="Source Sans Pro", hjust = 0.5),
axis.text.y=element_blank(), legend.position = "top",
legend.title=element_text(size=18,family="Source Sans Pro"),
legend.text=element_text(size=16,family="Source Sans Pro"),
legend.margin = margin(c(20, 10, -65, 50)),
legend.key.size = unit(1.5, "cm"),
plot.title = element_text(margin = margin(r = 10, b = 10), face="bold",size = 24, family="Source Sans Pro", colour = "black", hjust = 0.5),
legend.direction = "horizontal",
axis.ticks=element_blank(),
aspect.ratio = c(65/100),
plot.background = element_rect(fill = "white"),
strip.text.x = element_text(size=13,family="Source Sans Pro")) +
labs(title = "El centro de la Euro 2020 con más atacantes en el área", subtitle = "Przemysław Frankowski, Polonia vs. Suecia, 23/06/2021",
caption = "Creado con los datos gratuitos de StatsBomb\n https://github.com/statsbomb/open-data") +
coord_flip(xlim = c(85, 125))

Con este resultado:

Este código solo representa un punto de partida para investigaciones más profundas. Incluso con un cambio sencillo de los filtros para incluir a diferentes variables, podéis crear algo distinto. Tenemos muchas ganas de ver lo que vosotros haréis con estos datos.

Si necesitáis un poco de inspiración, varios académicos y analistas presentaron sus investigaciones con StatsBomb 360 en la StatsBomb Conference 2021. Podéis ver los videos (en inglés) en nuestro canal de Youtube o leer los artículos de investigación (también en inglés) aquí.