Liga Deportiva Universitaria demuestra una vez más su potencial internacional pasando a la final de la Copa Sudamericana 2023. En este post, reviso la trayectoria de LDU en campeonatos internacionales, extrayendo datos de diferentes fuentes con R. Imagen generada con inteligencia artificial.
Después de casi 14 años desde su última final en un campeonato internacional, Liga Deportiva Universitaria de Quito (LDU) selló su pase a la final de la Copa Sudamericana (el segundo campeonato más importante del fútbol de clubes en Sudamérica) derrotando al Defensa y Justicia argentino por goleada. El triunfo de Liga no viene fácil: aunque se ganó el apodo de Rey de Copas por los cuatro títulos internacionales ganados durante 2007-2010 (incluyendo la Copa Libertadores, el título más importante de América, nunca antes ganado por otro equipo ecuatoriano), al equipo le ha costado restaurar su gloria desde entonces.
El 28 de Octubre del 2023 Liga promete desempolvar el trono en Ponceano enfrentandose en Montevideo, Uruguay ante Fortaleza, equipo recientemente ascendido a la Serie A Brasileirao. En homenaje al equipo y en la misma línea de los otros artículos de este blog, en este post reviso la trayectoria de Liga en campeonatos internacionales de fútbol, aprovechando la oportunidad para explorar las posibilidades de extracción de datos de fútbol con R.
Show the code
# Loop para extraer informacion-copa-sudamericanalibertadores_matches_raw <-load_match_comp_results('Copa Libertadores de América')sudamericana_matches_raw <-load_match_comp_results('Copa Sudamericana')# Preparar datos de Copa Libertadoreslibertadores_matches <- libertadores_matches_raw %>% janitor::clean_names() %>%transmute(competition_name =iconv(competition_name, from ="UTF-8", to ="latin1"),year = season_end_year, round,round_number =case_when( round =='First stage'~1, round %in%c('Second stage', 'Group stage') ~2, round =='Knockout round play-offs'~3, round =='Round of 16'~5, round =='Quarter-finals'~7, round =='Semi-finals'~9, round %in%c('Final', 'Finals') ~11 ), day, date, time,home_team_country =str_sub(home, start =-2) %>%str_to_upper(),home_team_name =str_sub(home, end =-4), home_goals,away_team_country =str_sub(away, start =1, end =2) %>%str_to_upper(),away_team_name =str_sub(away, start =4, end =-1), away_goals,result =case_when( home_goals > away_goals ~'home_win', home_goals < away_goals ~'away_win', home_goals == away_goals ~NA),match_winner =case_when( home_goals > away_goals ~ home_team_name, home_goals < away_goals ~ away_team_name, home_goals == away_goals ~'none-draw'),leg =case_when( notes %>%str_like('%Leg 1%') ~'leg_1', notes %>%str_like('%Leg 2%') ~'leg_2',TRUE~NA ),penalty_kicks =if_else(notes %>%str_like('%penalties%'), TRUE, FALSE),extra_time =if_else(notes %>%str_like('%extra time%'), TRUE, FALSE), venue, attendance, referee, notes)# Preparar datos de Copa Sudamericanasudamericana_matches <- sudamericana_matches_raw %>% janitor::clean_names() %>%transmute(competition_name,year = season_end_year, round,round_number =case_when( round =='First stage'~1, round %in%c('Second stage', 'Group stage') ~2, round =='Knockout round play-offs'~3, round =='Round of 16'~5, round =='Quarter-finals'~7, round =='Semi-finals'~9, round %in%c('Final', 'Finals') ~11 ), day, date, time,home_team_country =str_sub(home, start =-2) %>%str_to_upper(),home_team_name =str_sub(home, end =-4), home_goals,away_team_country =str_sub(away, start =1, end =2) %>%str_to_upper(),away_team_name =str_sub(away, start =4, end =-1), away_goals,result =case_when( home_goals > away_goals ~'home_win', home_goals < away_goals ~'away_win', home_goals == away_goals ~NA),match_winner =case_when( home_goals > away_goals ~ home_team_name, home_goals < away_goals ~ away_team_name, home_goals == away_goals ~'none-draw'),leg =case_when( notes %>%str_like('%Leg 1%') ~'leg_1', notes %>%str_like('%Leg 2%') ~'leg_2',TRUE~NA ),penalty_kicks =if_else(notes %>%str_like('%penalties%'), TRUE, FALSE),extra_time =if_else(notes %>%str_like('%extra time%'), TRUE, FALSE), venue, attendance, referee, notes)# Filtrar para datos de LDU en Copa Libertadores solamentelibertadores_matches_ldu <- libertadores_matches %>%filter(home_team_name =='LDU de Quito'| away_team_name =='LDU de Quito')# Obtener la ultima etapa alcanzada para cada año de LDUldu_stage_reached_libertadores <- libertadores_matches_ldu %>%group_by(year) %>%summarise(stage_reached_libertadores =max(round_number, na.rm = T))# Filtrar para datos de LDU en Copa Sudamericana solamentesudamericana_matches_ldu <- sudamericana_matches %>%filter(home_team_name =='LDU de Quito'| away_team_name =='LDU de Quito')# Obtener la ultima etapa alcanzada para cada año de LDUldu_stage_reached_sudamericana <- sudamericana_matches_ldu %>%group_by(year) %>%summarise(stage_reached_sudamericana =max(round_number, na.rm = T))# Crear data frame para realizar grafico de resultados LDU en campeonatos internacionales international_results_ldu <- ldu_stage_reached_libertadores %>%complete(year =seq(2014, 2023)) %>%replace_na(list(stage_reached_libertadores =0.1)) %>%mutate(tournament ='Libertadores') %>%rename(stage_reached = stage_reached_libertadores) %>%bind_rows( ldu_stage_reached_sudamericana %>%complete(year =seq(2014, 2023)) %>%replace_na(list(stage_reached_sudamericana =0.1)) %>%mutate(tournament ='Sudamericana') %>%rename(stage_reached = stage_reached_sudamericana) ) %>%mutate(label =case_when(.default ='', stage_reached %in%c(1,2) ~'Grupos', stage_reached ==3~'Repechaje', stage_reached ==5~'Octavos de final', stage_reached ==7~'Cuartos de final', stage_reached ==9~'Semifinal', stage_reached ==11~'Final'))
Existen varias referencias que describen la implementación de métodos de football analytics, sin embargo el problema aparece cuando se quiere implementar los métodos para el Ecuador. De forma decepcionante (aunque quizás no sorprendente), no existe una autoridad que provea datos oficiales de la LigaPro Ecuador. Existen algunas páginas web que proveen datos básicos, con especial atención al esfuerzo de @mifutbolecuador, sin embargo, no es posible encontrar una fuente que permita descargas. Curiosamente, sucede lo mismo para los campeonatos internacionales CONMEBOL: aunque existe más información disponible, es complicado llegar a una base de datos limpia y descargable. Esto quizás se debe al potencial uso monetario de estos datos dada la existencia de la industria de las apuestas deportivas (especialmente importante en Ecuador).
Ventajosamente, existen esfuerzos internacionales que recientemente han empezado a recopilar información sobre equipos ecuatorianos. Uno es FBref.com, un sitio web que provee datos de fútbol de todo el mundo. Otro sitio web similar es Transfermarkt. La información de ambos sitios web es de libre acceso, aunque no es posible descargar los datos directamente desde la página web. Sin embargo, su existencia que permiten una técnica común de recolección de datos: web scraping. El scraping puede ser un problema por varias razones, ya que requiere entender las estructuras de datos de la página web para construir un códgio de scraping robusto. Ventajosamente, R ofrece un atajo excelente: la librería worldfootballR de @JaseZiv, que permite hacer scraping de forma fácil automática. Arriba utilicé esta librería para acceder a datos pre-procesados de FBref para todos los partidos de Copa Libertadores1 y Sudamericana2 desde 2014 hasta 20233.
Show the code
# Grafico de resultados LDU en campeonatos internacionalescolors <-c('Libertadores'='#C7B143', 'Sudamericana'='#20409A')international_results_ldu %>%filter(year >2014) %>%ggplot(aes(year, stage_reached, fill = tournament)) +geom_col(width =0.8, position =position_dodge(width =0.8)) +scale_fill_manual(values = colors) +scale_x_continuous(breaks =seq(2014, 2023, 1)) +annotate('text', x =2015.2, y =3.4, label ='Octavos', size =12, color ='white', angle =90) +annotate('text', x =2015.8, y =1, label ='Grupos', size =12, color ='white', angle =90) +annotate('text', x =2017.2, y =3.4, label ='Octavos', size =12, color ='white', angle =90) +annotate('text', x =2018.2, y =3.4, label ='Octavos', size =12, color ='white', angle =90) +annotate('text', x =2018.8, y =6, label ='Cuartos', size =12, color ='white', angle =90) +annotate('text', x =2019.8, y =3.4, label ='Octavos', size =12, color ='white', angle =90) +annotate('text', x =2020.8, y =1, label ='Grupos', size =12, color ='white', angle =90) +annotate('text', x =2021.2, y =6, label ='Cuartos', size =12, color ='white', angle =90) +annotate('text', x =2022.2, y =1, label ='Grupos', size =12, color ='white', angle =90) +annotate('text', x =2023.2, y =10, label ='Final', size =12, color ='white', angle =90) +labs(x ='',y ='',title ='Resultados de copas internacionales para Liga de Quito',subtitle ='Copa Libertadores y Copa Sudamericana, 2015-2023',fill ='Campeonato',caption ='Nota: Valores sin etiqueta se presentan cuando LDU no participó en el campeonato.')+ theme_daniel+theme(axis.line.y =element_blank(),axis.text.y =element_blank(),legend.position =c(0.08, 0.88))
Naturalmente, a Liga le ha costado más terminar en etapas avanzadas de Copa Libertadores que de Copa Sudamericana. El salto a la final de la Copa Sudamericana se da de forma abrupta: en 2022 no logra pasar de la etapa de grupos, pero en 2023 arrasa y llega a la final en Montevideo para enfrentarse a Fortaleza de Brasil, un equipo relativamente nuevo en la escena internacional al haber recientemente ascendido a la primera división de Brasil.
Una de las complicaciones de los datos (tanto en FBref como en Transfermarkt) es que el gráfico solamente muestra una escena parcial de la historia de LDU en campeonatos internacionales. Lo más lejos que se puede observar es el resultado de Sudamericana en 2015, en donde Liga cayó ante River Plate tras un recordado penal errado por Jonathan Álvez, con Liga como anfitrión. No es posible observar los datos de la mítica Liga de los 2000, que ganó la Copa Libertadores y Sudamericana en 2008 y 2009 además de dos Recopas4 en 2009 y 2010. Tampoco es posible observar el periodo posterior a 2010, en donde Liga de Quito no logró clasificar a ningún torneo internacional hasta 2015.
Show the code
# Extraer datos de partidos de Sudamericana para obtener Top Rivales de Liga en Sudamericanasudamericana_matches_ldu <- sudamericana_matches %>%filter(home_team_name =='LDU de Quito'| away_team_name =='LDU de Quito') %>%transmute(year, round, date,ldu_home_or_away =case_when( home_team_name =='LDU de Quito'~'Home', away_team_name=='LDU de Quito'~'Away'),opponent =case_when( home_team_name =='LDU de Quito'~ away_team_name, away_team_name =='LDU de Quito'~ home_team_name),opponent_country =case_when( home_team_name =='LDU de Quito'~ away_team_country, away_team_name =='LDU de Quito'~ home_team_country),ldu_goals =case_when( home_team_name =='LDU de Quito'~ home_goals, away_team_name =='LDU de Quito'~ away_goals),opponent_goals =case_when( home_team_name =='LDU de Quito'~ away_goals, away_team_name =='LDU de Quito'~ home_goals),ldu_result =case_when( ldu_goals > opponent_goals ~'Win', ldu_goals < opponent_goals ~'Loss', ldu_goals == opponent_goals ~'Draw'),tournament ='Sudamericana', )# Hacer lo mismo para Libertadoreslibertadores_matches_ldu <- libertadores_matches %>%filter(home_team_name =='LDU de Quito'| away_team_name =='LDU de Quito') %>%transmute(year, round, date,ldu_home_or_away =case_when( home_team_name =='LDU de Quito'~'Home', away_team_name=='LDU de Quito'~'Away'),opponent =case_when( home_team_name =='LDU de Quito'~ away_team_name, away_team_name =='LDU de Quito'~ home_team_name),opponent_country =case_when( home_team_name =='LDU de Quito'~ away_team_country, away_team_name =='LDU de Quito'~ home_team_country),ldu_goals =case_when( home_team_name =='LDU de Quito'~ home_goals, away_team_name =='LDU de Quito'~ away_goals),opponent_goals =case_when( home_team_name =='LDU de Quito'~ away_goals, away_team_name =='LDU de Quito'~ home_goals),ldu_result =case_when( ldu_goals > opponent_goals ~'Win', ldu_goals < opponent_goals ~'Loss', ldu_goals == opponent_goals ~'Draw'),tournament ='Libertadores')# Unir dataframes y obtener totales de goles a favor y en contra para cada año y por cada torneoldu_match_goals <-bind_rows(sudamericana_matches_ldu, libertadores_matches_ldu) %>%group_by(year, tournament) %>%summarise(ldu_goals =sum(ldu_goals, na.rm =TRUE),opponent_goals =sum(opponent_goals, na.rm =TRUE),ldu_wins =sum(ldu_result =='Win', na.rm =TRUE),ldu_losses =sum(ldu_result =='Loss', na.rm =TRUE),ldu_draws =sum(ldu_result =='Draw', na.rm =TRUE),total_matches =n()) %>%mutate(ldu_goal_difference = ldu_goals - opponent_goals,ldu_win_percentage = ldu_wins / total_matches *100) %>%arrange(year, tournament) %>%ungroup()# Graficar ldu_match_goals %>%select(year, tournament, ldu_goals, opponent_goals) %>%pivot_longer(c(ldu_goals, opponent_goals),names_to ='goals_from',values_to ='goals') %>%mutate(goals_from =case_when( goals_from =='ldu_goals'~'A favor', goals_from =='opponent_goals'~'En contra')) %>%ggplot(aes(x = year, y = goals, fill = goals_from)) +geom_col(position ='dodge') +scale_x_continuous(breaks =seq(2015, 2023, 1)) +scale_fill_manual(values =c('#20409A', '#EE2E24')) +facet_wrap(~tournament) +labs(title ='Goles a favor y en contra de LDU',subtitle ='Torneos internacionales 2015-2023',x ='',y ='Goles',fill ='',caption =str_wrap('Nota: se presentan goles a favor y en contra solo en casos en donde LDU clasificó y participó en al menos en un partido. Datos de FBref.', 120)) + theme_daniel +theme(axis.line.x =element_line(color ='white'),panel.background =element_rect(colour ="white", linewidth=4, fill=NA),axis.text.x =element_text(angle =45, vjust =0.5, hjust=1),axis.ticks =element_line(colour ='white'),legend.position =c(0.9,1.18))
Los datos preprocesados de worldfootballR permiten calcular el número de goles a favor y en contra para cada una de las temporadas de LDU en torneos internacionales. Naturalmente, el equipo ha mejorado su ratio de goles a medida que ha ido pasando a etapas más avanzadas del torneo y como es de esperarse, Liga es más capaz de mantener su arco en cero en la Sudamericana que en la Libertadores. El mejor desempeño de LDU en la Sudamericana se da este año, con 23 goles en 14 partidos, lo que representa un 50% de partidos ganados. Esto, sin embargo, no es el mejor record de LDU en torneos internacionales de los últimos años: en 2020, aunque el equipo solo pasó a octavos de final, logró un 62.5% de partidos ganados en Copa Libertadores. Esto es un claro ejemplo de cómo la estadística simple no debería ser malinterpretada en contextos deportivos como este.
Sin embargo, hay indicadores que rara vez fallan, y uno de ellos es $ la plata $. ¿Cuánto invirtió Liga de Quito en los últimos años para llegar donde está el día de hoy? Esta pregunta podría parecer extremadamente complicada de responder, pero los datos de Transfermarkt, plataforma alemana conocida por su exactitud en cuanto a información de traspasos y valor de jugadores, lo facilita. La función que permite obtener esta información permite solamente observar un año a la vez, por lo que utilizo un forloop (aunque muy temidos en R, los loops son manejables) para obtener la información de los últimos años sin tener que llamar la función una y otra vez.
Show the code
# El campeonato ecuatoriano está distribuido en dos diferentes links de Transfermarkt, es necesario crear un foroop para cada uno: # Vector de años para el loop (2019-2022)years_1 <-2019:2023# Inicializar una lista para guardar los resultados de cada iteracion del loopecuador_finances1 <-list()for (i in years_1){ ecuador_finances1[[which(years_1 == i)]] <-tm_team_transfer_balances('ECU', i, league_url ='https://www.transfermarkt.com/ligapro-serie-a-primera-etapa/startseite/wettbewerb/EL1A') %>%mutate(year = i) # Agregar año para cada iteracion}# El siguiente loop requiere tanto el link como el año, definir los vectores a continuacion# Siguiente link: 2012-2018years_2 <-2012:2018links <-c('https://www.transfermarkt.com/serie-a-primera-etapa/startseite/wettbewerb/ECPE/plus/?saison_id=2011','https://www.transfermarkt.com/serie-a-primera-etapa/startseite/wettbewerb/ECPE/plus/?saison_id=2012','https://www.transfermarkt.com/serie-a-primera-etapa/startseite/wettbewerb/ECPE/plus/?saison_id=2013','https://www.transfermarkt.com/serie-a-primera-etapa/startseite/wettbewerb/ECPE/plus/?saison_id=2014','https://www.transfermarkt.com/serie-a-primera-etapa/startseite/wettbewerb/ECPE/plus/?saison_id=2015','https://www.transfermarkt.com/serie-a-primera-etapa/startseite/wettbewerb/ECPE/plus/?saison_id=2016','https://www.transfermarkt.com/serie-a-primera-etapa/startseite/wettbewerb/ECPE/plus/?saison_id=2017','https://www.transfermarkt.com/serie-a-primera-etapa/startseite/wettbewerb/ECPE/plus/?saison_id=2018')ecuador_finances2 <-list()for (i in1:length(years_2)){ ecuador_finances2[[years_2[i]]] <-tm_team_transfer_balances('ECU', years_2[i], league_url = links[i]) %>%mutate(year = years_2[i]) }# Extraer informacion a un dataframe y limpiar datos:ecuador_finances_full <-bind_rows(ecuador_finances1, ecuador_finances2) # Filtrar para solo LDU, calcular ingresos-egresos y graficar en un grafico de barrasecuador_finances_full %>%filter(squad =='LDU Quito') %>%transmute(year, income = income_euros, expenditure =-expenditure_euros) %>%pivot_longer(c(income, expenditure),names_to ='balance_type',values_to ='values') %>%mutate(balance_type =if_else(balance_type =='income', 'Ingresos', 'Egresos')) %>%ggplot(aes(as.factor(year), values/1000000, fill = balance_type, group = balance_type)) +geom_col(position ='dodge')+scale_fill_manual(values =c('Ingresos'='#20409A', 'Egresos'='#EE2E24')) +scale_y_continuous(labels = scales::comma, breaks =seq(0, 6, by =2)) +geom_hline(yintercept =0, color ='gray40', linewidth =1) +labs(title ='Ingresos y egresos para Liga de Quito',subtitle ='Cifras de traspasos 2012-2022',x ='',y ='Miles de euros',fill ='',caption =str_wrap('Nota: 2023 es un año parcial. No se presentan datos para las temporadas donde no se registran jugadores vendidos. Datos de Transfemarkt.', 115)) + theme_daniel +theme(axis.line.x =element_line(color ='white'),panel.background =element_rect(colour ="white", linewidth=4, fill =NA),axis.text.x =element_text(angle =45, vjust =0.5, hjust=1),axis.ticks =element_line(colour ='white'),legend.position =c(0.85,1.1))
En 2022 LDU tuvo la mejor temporada en términos de ingresos por traspasos de su historia, logrando vender jugadores por 7.1 millones de euros5. De forma notable, el club vendió a dos jugadores por altos valores: Rodrigo Aguirre y Nilson Angulo, por 4.75 y 1.90 millones de euros. En 2022 LDU no invirtió dinero en su plantilla, sin embargo, logró traer al equipo a varios agentes libres, situación que no se refleja en el gráfico anterior, pero se puede visualizar en la página web de Transfermarkt. Uno de los jugadores más importantes que llega a LDU como agente libre es Alexander Domínguez, histórico arquero de la selección ecuatoriana y LDU, que regresa al equipo luego de varios años en el exterior. Adicionalmente, las cifras no muestran los valores de contratos temporales, como es el de Renato Ibarra, mediocampista ecuatoriano, quien llega a LDU tras una larga carrera en el fútbol internacional. De todas formas, la inversión en la plantilla de 2021 es la tercera más alta de los últimos 20 años, y los ingresos que LDU ha podido recolectar en 2022 y 2023 potencialmente le permitirán seguir invirtiendo en su plantel existente, así como en la infraestructura del club y las juveniles, que son lo que inicialmente hicieron al club lo que es hoy (y lo que ahora diferencia a Independiente del Valle, también muy exitoso equipo ecuatoriano).
Lejos de ser un ejercicio exhaustivo de análisis, lo que este post intenta hacer es dar una mirada aterrizada a las cifras del equipo en los dos torneos más importantes de la región. Es desafortunado que no exista una fuente de datos centralizada que permita analizar el fútbol ecuatoriano y regional más a fondo, sin embargo, las posibilidades que ofrecen los desarrolladores open source de R, como el paquete utilizado aquí, worldfootballR, son una muestra de que es posible hacer análisis de datos de fútbol de forma más accesible y transparente. Espero que este trabajo sea de utilidad para quiénes se interesen en extraer y analizar datos de fútbol latinoamericano en gran escala, y, ante todo, que sea un buen augurio para la quinta estrella del trono en Ponceano.
Liga te pido ponga 🥚…
Footnotes
La Copa Libertadores es el torneo de clubes más importante de Sudamérica, en donde clasifican los primeros equipos de las divisiones de clubes nacionales de cada país. Es análogo a la Champions League en Europa.↩︎
La Copa Sudamericana es el segundo torneo mas importante del fútbol de clubes sudamericano, en donde clasifican clubes en posiciones intermedias de las divisiones nacionales. Es análogo a la Europa League en Europa.↩︎
En este post, cuando me refiero a un año, realmente me refiero a una temporada. Las temporadas comienzan el año anterior al indicado en las visualizaciones (por ejemplo, la temporada 2021 comienza en 2020 y termina en 2021).↩︎
Minitorneo en donde el campeón Libertadores se enfrenta al de Sudamericana. Análogo a la Super Cup en Europa↩︎
---title: "El trono en Ponceano: Liga de Quito en cifras con R"author: "Daniel Sánchez"date: "2023-10-25"description: "Liga Deportiva Universitaria demuestra una vez más su potencial internacional pasando a la final de la Copa Sudamericana 2023. En este post, reviso la trayectoria de LDU en campeonatos internacionales, extrayendo datos de diferentes fuentes con R. Imagen generada con inteligencia artificial."categories: [spanish, ecuador, football, data viz, R]website: twitter-card: image: dalle.png creator: "@daniel_ec18"csl: ../../econometrics.cslimage: dalle.pngknitr: opts_chunk: message: false warning: false echo: true include: trueformat: html: code-fold: true code-tools: true code-summary: "Show the code" code-overflow: wrap---```{r}#| label: setup# Preliminares# Cargar libreriaslibrary(worldfootballR)library(dplyr)library(stringr)library(tidyr)library(ggplot2)library(ggthemes)library(showtext)# Definir tema basefont_add_google("Questrial", family ="Questrial")showtext_auto()theme_daniel<-theme_hc(style ='darkunica',base_family ='Questrial',base_size =40) +theme(axis.line.y =element_line(colour ='white'),axis.line.x =element_blank(),panel.grid.major.y =element_blank(),panel.grid.major.x =element_blank(),plot.caption =element_text(hjust =0, face ='italic'),plot.title.position ='plot',plot.caption.position ='plot',plot.subtitle =element_text(lineheight =0.5, vjust =0.5),axis.ticks =element_blank(),plot.title =element_text(size =60),axis.text =element_text(colour ='gray'))```Después de casi 14 años desde su última final en un campeonato internacional, Liga Deportiva Universitaria de Quito (LDU) selló su pase a la final de la Copa Sudamericana (el segundo campeonato más importante del fútbol de clubes en Sudamérica) derrotando al Defensa y Justicia argentino por goleada. El triunfo de Liga no viene fácil: aunque se ganó el apodo de *Rey de Copas* por los cuatro títulos internacionales ganados durante 2007-2010 (incluyendo la Copa Libertadores, el título más importante de América, nunca antes ganado por otro equipo ecuatoriano), al equipo le ha costado restaurar su gloria desde entonces.El 28 de Octubre del 2023 Liga promete desempolvar el trono en Ponceano enfrentandose en Montevideo, Uruguay ante Fortaleza, equipo recientemente ascendido a la Serie A Brasileirao. En homenaje al equipo y en la misma línea de los otros artículos de este blog, en este post reviso la trayectoria de Liga en campeonatos internacionales de fútbol, aprovechando la oportunidad para explorar las posibilidades de extracción de datos de fútbol con R.```{r}#| label: extraccion-datos#| cache: true# Loop para extraer informacion-copa-sudamericanalibertadores_matches_raw <-load_match_comp_results('Copa Libertadores de América')sudamericana_matches_raw <-load_match_comp_results('Copa Sudamericana')# Preparar datos de Copa Libertadoreslibertadores_matches <- libertadores_matches_raw %>% janitor::clean_names() %>%transmute(competition_name =iconv(competition_name, from ="UTF-8", to ="latin1"),year = season_end_year, round,round_number =case_when( round =='First stage'~1, round %in%c('Second stage', 'Group stage') ~2, round =='Knockout round play-offs'~3, round =='Round of 16'~5, round =='Quarter-finals'~7, round =='Semi-finals'~9, round %in%c('Final', 'Finals') ~11 ), day, date, time,home_team_country =str_sub(home, start =-2) %>%str_to_upper(),home_team_name =str_sub(home, end =-4), home_goals,away_team_country =str_sub(away, start =1, end =2) %>%str_to_upper(),away_team_name =str_sub(away, start =4, end =-1), away_goals,result =case_when( home_goals > away_goals ~'home_win', home_goals < away_goals ~'away_win', home_goals == away_goals ~NA),match_winner =case_when( home_goals > away_goals ~ home_team_name, home_goals < away_goals ~ away_team_name, home_goals == away_goals ~'none-draw'),leg =case_when( notes %>%str_like('%Leg 1%') ~'leg_1', notes %>%str_like('%Leg 2%') ~'leg_2',TRUE~NA ),penalty_kicks =if_else(notes %>%str_like('%penalties%'), TRUE, FALSE),extra_time =if_else(notes %>%str_like('%extra time%'), TRUE, FALSE), venue, attendance, referee, notes)# Preparar datos de Copa Sudamericanasudamericana_matches <- sudamericana_matches_raw %>% janitor::clean_names() %>%transmute(competition_name,year = season_end_year, round,round_number =case_when( round =='First stage'~1, round %in%c('Second stage', 'Group stage') ~2, round =='Knockout round play-offs'~3, round =='Round of 16'~5, round =='Quarter-finals'~7, round =='Semi-finals'~9, round %in%c('Final', 'Finals') ~11 ), day, date, time,home_team_country =str_sub(home, start =-2) %>%str_to_upper(),home_team_name =str_sub(home, end =-4), home_goals,away_team_country =str_sub(away, start =1, end =2) %>%str_to_upper(),away_team_name =str_sub(away, start =4, end =-1), away_goals,result =case_when( home_goals > away_goals ~'home_win', home_goals < away_goals ~'away_win', home_goals == away_goals ~NA),match_winner =case_when( home_goals > away_goals ~ home_team_name, home_goals < away_goals ~ away_team_name, home_goals == away_goals ~'none-draw'),leg =case_when( notes %>%str_like('%Leg 1%') ~'leg_1', notes %>%str_like('%Leg 2%') ~'leg_2',TRUE~NA ),penalty_kicks =if_else(notes %>%str_like('%penalties%'), TRUE, FALSE),extra_time =if_else(notes %>%str_like('%extra time%'), TRUE, FALSE), venue, attendance, referee, notes)# Filtrar para datos de LDU en Copa Libertadores solamentelibertadores_matches_ldu <- libertadores_matches %>%filter(home_team_name =='LDU de Quito'| away_team_name =='LDU de Quito')# Obtener la ultima etapa alcanzada para cada año de LDUldu_stage_reached_libertadores <- libertadores_matches_ldu %>%group_by(year) %>%summarise(stage_reached_libertadores =max(round_number, na.rm = T))# Filtrar para datos de LDU en Copa Sudamericana solamentesudamericana_matches_ldu <- sudamericana_matches %>%filter(home_team_name =='LDU de Quito'| away_team_name =='LDU de Quito')# Obtener la ultima etapa alcanzada para cada año de LDUldu_stage_reached_sudamericana <- sudamericana_matches_ldu %>%group_by(year) %>%summarise(stage_reached_sudamericana =max(round_number, na.rm = T))# Crear data frame para realizar grafico de resultados LDU en campeonatos internacionales international_results_ldu <- ldu_stage_reached_libertadores %>%complete(year =seq(2014, 2023)) %>%replace_na(list(stage_reached_libertadores =0.1)) %>%mutate(tournament ='Libertadores') %>%rename(stage_reached = stage_reached_libertadores) %>%bind_rows( ldu_stage_reached_sudamericana %>%complete(year =seq(2014, 2023)) %>%replace_na(list(stage_reached_sudamericana =0.1)) %>%mutate(tournament ='Sudamericana') %>%rename(stage_reached = stage_reached_sudamericana) ) %>%mutate(label =case_when(.default ='', stage_reached %in%c(1,2) ~'Grupos', stage_reached ==3~'Repechaje', stage_reached ==5~'Octavos de final', stage_reached ==7~'Cuartos de final', stage_reached ==9~'Semifinal', stage_reached ==11~'Final'))```Existen varias referencias que describen la implementación de métodos de *football analytics*, sin embargo el problema aparece cuando se quiere implementar los métodos para el Ecuador. De forma decepcionante (aunque quizás no sorprendente), **no existe una autoridad que provea datos oficiales de la LigaPro Ecuador**. Existen algunas páginas web que proveen datos básicos, con especial atención al esfuerzo de [\@mifutbolecuador](https://twitter.com/mifutbolecuador), sin embargo, no es posible encontrar una fuente que permita descargas. Curiosamente, sucede lo mismo para los campeonatos internacionales CONMEBOL: aunque existe más información disponible, es complicado llegar a una base de datos limpia y descargable. Esto quizás se debe al potencial uso monetario de estos datos dada la existencia de la industria de las apuestas deportivas (especialmente importante en Ecuador).Ventajosamente, existen esfuerzos internacionales que recientemente han empezado a recopilar información sobre equipos ecuatorianos. Uno es [FBref.com](https://fbref.com/en/about/), un sitio web que provee datos de fútbol de todo el mundo. Otro sitio web similar es [Transfermarkt](https://www.transfermarkt.com/). La información de ambos sitios web es de libre acceso, aunque no es posible descargar los datos directamente desde la página web. Sin embargo, su existencia que permiten una técnica común de recolección de datos: *web scraping*. El scraping puede ser un problema por varias razones, ya que requiere entender las estructuras de datos de la página web para construir un códgio de scraping robusto. Ventajosamente, R ofrece un atajo *excelente*: la librería *worldfootballR* de [\@JaseZiv](https://twitter.com/JaseZiv), que permite hacer scraping de forma fácil automática. Arriba utilicé esta librería para acceder a datos pre-procesados de FBref para todos los partidos de Copa Libertadores[^1] y Sudamericana[^2] desde 2014 hasta 2023[^3].[^1]: La Copa Libertadores es el torneo de clubes más importante de Sudamérica, en donde clasifican los primeros equipos de las divisiones de clubes nacionales de cada país. Es análogo a la Champions League en Europa.[^2]: La Copa Sudamericana es el segundo torneo mas importante del fútbol de clubes sudamericano, en donde clasifican clubes en posiciones intermedias de las divisiones nacionales. Es análogo a la Europa League en Europa.[^3]: En este post, cuando me refiero a un año, realmente me refiero a una *temporada*. Las temporadas comienzan el año anterior al indicado en las visualizaciones (por ejemplo, la temporada 2021 comienza en 2020 y termina en 2021).```{r}#| label: grafico-resultados#| fig-width: 12#| fig-height: 8# Grafico de resultados LDU en campeonatos internacionalescolors <-c('Libertadores'='#C7B143', 'Sudamericana'='#20409A')international_results_ldu %>%filter(year >2014) %>%ggplot(aes(year, stage_reached, fill = tournament)) +geom_col(width =0.8, position =position_dodge(width =0.8)) +scale_fill_manual(values = colors) +scale_x_continuous(breaks =seq(2014, 2023, 1)) +annotate('text', x =2015.2, y =3.4, label ='Octavos', size =12, color ='white', angle =90) +annotate('text', x =2015.8, y =1, label ='Grupos', size =12, color ='white', angle =90) +annotate('text', x =2017.2, y =3.4, label ='Octavos', size =12, color ='white', angle =90) +annotate('text', x =2018.2, y =3.4, label ='Octavos', size =12, color ='white', angle =90) +annotate('text', x =2018.8, y =6, label ='Cuartos', size =12, color ='white', angle =90) +annotate('text', x =2019.8, y =3.4, label ='Octavos', size =12, color ='white', angle =90) +annotate('text', x =2020.8, y =1, label ='Grupos', size =12, color ='white', angle =90) +annotate('text', x =2021.2, y =6, label ='Cuartos', size =12, color ='white', angle =90) +annotate('text', x =2022.2, y =1, label ='Grupos', size =12, color ='white', angle =90) +annotate('text', x =2023.2, y =10, label ='Final', size =12, color ='white', angle =90) +labs(x ='',y ='',title ='Resultados de copas internacionales para Liga de Quito',subtitle ='Copa Libertadores y Copa Sudamericana, 2015-2023',fill ='Campeonato',caption ='Nota: Valores sin etiqueta se presentan cuando LDU no participó en el campeonato.')+ theme_daniel+theme(axis.line.y =element_blank(),axis.text.y =element_blank(),legend.position =c(0.08, 0.88))ggsave('grafico-resultados.png', width =7, height =5, dpi =300)```Naturalmente, a Liga le ha costado más terminar en etapas avanzadas de Copa Libertadores que de Copa Sudamericana. El salto a la final de la Copa Sudamericana se da de forma abrupta: en 2022 no logra pasar de la etapa de grupos, pero en 2023 arrasa y llega a la final en Montevideo para enfrentarse a Fortaleza de Brasil, un equipo relativamente nuevo en la escena internacional al haber recientemente ascendido a la primera división de Brasil.Una de las complicaciones de los datos (tanto en FBref como en Transfermarkt) es que el gráfico solamente muestra una escena parcial de la historia de LDU en campeonatos internacionales. Lo más lejos que se puede observar es el resultado de Sudamericana en 2015, en donde Liga cayó ante River Plate tras un recordado [penal errado](https://www.youtube.com/watch?v=kXfFJ-mEBjk) por Jonathan Álvez, con Liga como anfitrión. No es posible observar los datos de la mítica Liga de los 2000, que ganó la Copa Libertadores y Sudamericana en 2008 y 2009 además de dos Recopas[^4] en 2009 y 2010. Tampoco es posible observar el periodo posterior a 2010, en donde Liga de Quito no logró clasificar a ningún torneo internacional hasta 2015.[^4]: Minitorneo en donde el campeón Libertadores se enfrenta al de Sudamericana. Análogo a la Super Cup en Europa.```{r}#| label: datos-partidos#| fig-width: 12#| fig-height: 8# Extraer datos de partidos de Sudamericana para obtener Top Rivales de Liga en Sudamericanasudamericana_matches_ldu <- sudamericana_matches %>%filter(home_team_name =='LDU de Quito'| away_team_name =='LDU de Quito') %>%transmute(year, round, date,ldu_home_or_away =case_when( home_team_name =='LDU de Quito'~'Home', away_team_name=='LDU de Quito'~'Away'),opponent =case_when( home_team_name =='LDU de Quito'~ away_team_name, away_team_name =='LDU de Quito'~ home_team_name),opponent_country =case_when( home_team_name =='LDU de Quito'~ away_team_country, away_team_name =='LDU de Quito'~ home_team_country),ldu_goals =case_when( home_team_name =='LDU de Quito'~ home_goals, away_team_name =='LDU de Quito'~ away_goals),opponent_goals =case_when( home_team_name =='LDU de Quito'~ away_goals, away_team_name =='LDU de Quito'~ home_goals),ldu_result =case_when( ldu_goals > opponent_goals ~'Win', ldu_goals < opponent_goals ~'Loss', ldu_goals == opponent_goals ~'Draw'),tournament ='Sudamericana', )# Hacer lo mismo para Libertadoreslibertadores_matches_ldu <- libertadores_matches %>%filter(home_team_name =='LDU de Quito'| away_team_name =='LDU de Quito') %>%transmute(year, round, date,ldu_home_or_away =case_when( home_team_name =='LDU de Quito'~'Home', away_team_name=='LDU de Quito'~'Away'),opponent =case_when( home_team_name =='LDU de Quito'~ away_team_name, away_team_name =='LDU de Quito'~ home_team_name),opponent_country =case_when( home_team_name =='LDU de Quito'~ away_team_country, away_team_name =='LDU de Quito'~ home_team_country),ldu_goals =case_when( home_team_name =='LDU de Quito'~ home_goals, away_team_name =='LDU de Quito'~ away_goals),opponent_goals =case_when( home_team_name =='LDU de Quito'~ away_goals, away_team_name =='LDU de Quito'~ home_goals),ldu_result =case_when( ldu_goals > opponent_goals ~'Win', ldu_goals < opponent_goals ~'Loss', ldu_goals == opponent_goals ~'Draw'),tournament ='Libertadores')# Unir dataframes y obtener totales de goles a favor y en contra para cada año y por cada torneoldu_match_goals <-bind_rows(sudamericana_matches_ldu, libertadores_matches_ldu) %>%group_by(year, tournament) %>%summarise(ldu_goals =sum(ldu_goals, na.rm =TRUE),opponent_goals =sum(opponent_goals, na.rm =TRUE),ldu_wins =sum(ldu_result =='Win', na.rm =TRUE),ldu_losses =sum(ldu_result =='Loss', na.rm =TRUE),ldu_draws =sum(ldu_result =='Draw', na.rm =TRUE),total_matches =n()) %>%mutate(ldu_goal_difference = ldu_goals - opponent_goals,ldu_win_percentage = ldu_wins / total_matches *100) %>%arrange(year, tournament) %>%ungroup()# Graficar ldu_match_goals %>%select(year, tournament, ldu_goals, opponent_goals) %>%pivot_longer(c(ldu_goals, opponent_goals),names_to ='goals_from',values_to ='goals') %>%mutate(goals_from =case_when( goals_from =='ldu_goals'~'A favor', goals_from =='opponent_goals'~'En contra')) %>%ggplot(aes(x = year, y = goals, fill = goals_from)) +geom_col(position ='dodge') +scale_x_continuous(breaks =seq(2015, 2023, 1)) +scale_fill_manual(values =c('#20409A', '#EE2E24')) +facet_wrap(~tournament) +labs(title ='Goles a favor y en contra de LDU',subtitle ='Torneos internacionales 2015-2023',x ='',y ='Goles',fill ='',caption =str_wrap('Nota: se presentan goles a favor y en contra solo en casos en donde LDU clasificó y participó en al menos en un partido. Datos de FBref.', 120)) + theme_daniel +theme(axis.line.x =element_line(color ='white'),panel.background =element_rect(colour ="white", linewidth=4, fill=NA),axis.text.x =element_text(angle =45, vjust =0.5, hjust=1),axis.ticks =element_line(colour ='white'),legend.position =c(0.9,1.18))```Los datos preprocesados de *worldfootballR* permiten calcular el número de goles a favor y en contra para cada una de las temporadas de LDU en torneos internacionales. Naturalmente, el equipo ha mejorado su ratio de goles a medida que ha ido pasando a etapas más avanzadas del torneo y como es de esperarse, Liga es más capaz de mantener su arco en cero en la Sudamericana que en la Libertadores. El mejor desempeño de LDU en la Sudamericana se da este año, con 23 goles en 14 partidos, lo que representa un 50% de partidos ganados. Esto, sin embargo, no es el mejor record de LDU en torneos internacionales de los últimos años: en 2020, aunque el equipo solo pasó a octavos de final, logró un 62.5% de partidos ganados en Copa Libertadores. Esto es un claro ejemplo de cómo la estadística simple no debería ser malinterpretada en contextos deportivos como este.Sin embargo, hay indicadores que rara vez fallan, y uno de ellos es \$ *la plata* \$. ¿Cuánto invirtió Liga de Quito en los últimos años para llegar donde está el día de hoy? Esta pregunta podría parecer extremadamente complicada de responder, pero los datos de Transfermarkt, plataforma alemana conocida por su exactitud en cuanto a información de traspasos y valor de jugadores, lo facilita. La función que permite obtener esta información permite solamente observar un año a la vez, por lo que utilizo un `for` *loop* ([aunque muy temidos en R, los loops son manejables](loops.jpg)) para obtener la información de los últimos años sin tener que llamar la función una y otra vez.```{r}#| label: ldu-finances#| cache: true#| fig-width: 12#| fig-height: 8# El campeonato ecuatoriano está distribuido en diferentes links de Transfermarkt, es necesario crear un for loop para cada uno: # Vector de años para el loop (2019-2022)years_1 <-2019:2023# Inicializar una lista para guardar los resultados de cada iteracion del loopecuador_finances1 <-list()for (i in years_1){ ecuador_finances1[[which(years_1 == i)]] <-tm_team_transfer_balances('ECU', i, league_url ='https://www.transfermarkt.com/ligapro-serie-a-primera-etapa/startseite/wettbewerb/EL1A') %>%mutate(year = i) # Agregar año para cada iteracion}# El siguiente loop requiere tanto el link como el año, definir los vectores a continuacion# Siguiente link: 2012-2018years_2 <-2011:2018links <-c('https://www.transfermarkt.com/serie-a-primera-etapa/startseite/wettbewerb/ECPE/plus/?saison_id=2011','https://www.transfermarkt.com/serie-a-primera-etapa/startseite/wettbewerb/ECPE/plus/?saison_id=2012','https://www.transfermarkt.com/serie-a-primera-etapa/startseite/wettbewerb/ECPE/plus/?saison_id=2013','https://www.transfermarkt.com/serie-a-primera-etapa/startseite/wettbewerb/ECPE/plus/?saison_id=2014','https://www.transfermarkt.com/serie-a-primera-etapa/startseite/wettbewerb/ECPE/plus/?saison_id=2015','https://www.transfermarkt.com/serie-a-primera-etapa/startseite/wettbewerb/ECPE/plus/?saison_id=2016','https://www.transfermarkt.com/serie-a-primera-etapa/startseite/wettbewerb/ECPE/plus/?saison_id=2017','https://www.transfermarkt.com/serie-a-primera-etapa/startseite/wettbewerb/ECPE/plus/?saison_id=2018')ecuador_finances2 <-list()for (i in1:length(years_2)){ ecuador_finances2[[years_2[i]]] <-tm_team_transfer_balances('ECU', years_2[i], league_url = links[i]) %>%mutate(year = years_2[i]) }# Extraer informacion a un dataframe y limpiar datos:ecuador_finances_full <-bind_rows(ecuador_finances1, ecuador_finances2) # Filtrar para solo LDU, calcular ingresos-egresos y graficar en un grafico de barrasecuador_finances_full %>%filter(squad =='LDU Quito') %>%transmute(year, income = income_euros, expenditure =-expenditure_euros) %>%pivot_longer(c(income, expenditure),names_to ='balance_type',values_to ='values') %>%mutate(balance_type =if_else(balance_type =='income', 'Ingresos', 'Egresos')) %>%ggplot(aes(as.factor(year), values/1000000, fill = balance_type, group = balance_type)) +geom_col(position ='dodge')+scale_fill_manual(values =c('Ingresos'='#20409A', 'Egresos'='#EE2E24')) +scale_y_continuous(labels = scales::comma, breaks =seq(0, 6, by =2)) +geom_hline(yintercept =0, color ='gray40', linewidth =1) +labs(title ='Ingresos y egresos para Liga de Quito',subtitle ='Cifras de traspasos 2012-2022',x ='',y ='Miles de euros',fill ='',caption =str_wrap('Nota: 2023 es un año parcial. No se presentan datos para las temporadas donde no se registran jugadores vendidos. Datos de Transfemarkt.', 115)) + theme_daniel +theme(axis.line.x =element_line(color ='white'),panel.background =element_rect(colour ="white", linewidth=4, fill =NA),axis.text.x =element_text(angle =45, vjust =0.5, hjust=1),axis.ticks =element_line(colour ='white'),legend.position =c(0.85,1.1))```En 2022 LDU tuvo la mejor temporada en términos de ingresos por traspasos de su historia, logrando vender jugadores por 7.1 millones de euros[^5]. De forma notable, el club vendió a dos jugadores por altos valores: Rodrigo Aguirre y Nilson Angulo, por 4.75 y 1.90 millones de euros. En 2022 LDU no invirtió dinero en su plantilla, sin embargo, logró traer al equipo a varios agentes libres, situación que no se refleja en el gráfico anterior, pero se puede visualizar en la página web de Transfermarkt. Uno de los jugadores más importantes que llega a LDU como agente libre es Alexander Domínguez, histórico arquero de la selección ecuatoriana y LDU, que regresa al equipo luego de varios años en el exterior. Adicionalmente, las cifras no muestran los valores de contratos temporales, como es el de Renato Ibarra, mediocampista ecuatoriano, quien llega a LDU tras una larga carrera en el fútbol internacional. De todas formas, la inversión en la plantilla de 2021 es la tercera más alta de los últimos 20 años, y los ingresos que LDU ha podido recolectar en 2022 y 2023 potencialmente le permitirán seguir invirtiendo en su plantel existente, así como en la infraestructura del club y las juveniles, que son lo que inicialmente hicieron al club lo que es hoy (y lo que ahora diferencia a Independiente del Valle, también muy exitoso equipo ecuatoriano).[^5]: Esta es la moneda nativa de Transfermarkt.Lejos de ser un ejercicio exhaustivo de análisis, lo que este post intenta hacer es dar una mirada aterrizada a las cifras del equipo en los dos torneos más importantes de la región. Es desafortunado que no exista una fuente de datos centralizada que permita analizar el fútbol ecuatoriano y regional más a fondo, sin embargo, las posibilidades que ofrecen los desarrolladores open source de R, como el paquete utilizado aquí, *worldfootballR*, son una muestra de que es posible hacer análisis de datos de fútbol de forma más accesible y transparente. Espero que este trabajo sea de utilidad para quiénes se interesen en extraer y analizar datos de fútbol latinoamericano en gran escala, y, ante todo, que sea un buen augurio para la quinta estrella del trono en Ponceano.> *Liga te pido ponga 🥚...*