Predicción de resultados de fútbol

Un modelo de aprendizaje automático en Python que utiliza la biblioteca Scikit-learn para predecir los resultados de los partidos de fútbol en la Premier League rusa (RPL).

Entrada


Me inspiró para escribir este artículo el artículo Aprendizaje automático: predicción del vencimiento del EPL 2018 . Nuestro modelo de aprendizaje automático se entrenará en las estadísticas de partidos de la Premier League rusa (RPL) a partir de la temporada 2015/2016 para predecir los resultados de los próximos juegos. Datos tomados del sitio web de estadísticas de fútbol wyscout.com.
El código y los datos están disponibles en github .

Datos


Conectamos las bibliotecas necesarias:

import pandas as pd import numpy as np import collections 

Los datos del partido están en github .

 data = pd.read_csv("RPL.csv", encoding = 'cp1251', delimiter=';') data.head() 

imagen
¿Qué significa xG y PPDA?
xG (objetivos esperados) es un modelo de objetivos esperados. Se basa en el indicador de tiros a puerta, en base al cual podemos estimar cuántos goles realmente tuvo que marcar el equipo si tomamos en cuenta todos los tiros que aplicó. Obtenga más información sobre xG.
PPDA (Pases permitidos por acción defensiva) es un indicador de estadísticas de fútbol que te permite determinar la intensidad de la presión en un partido. Cuanto menor sea el valor de PPDA, mayor será la intensidad del juego en defensa. Aprenda más sobre PPDA
PPDA = número de pases realizados por el equipo atacante / número de acciones en defensa


Vamos a predecir los resultados de los partidos para la segunda parte de la temporada 2018/2019 (es decir, los partidos jugados en 2019). La lista de equipos que juegan esta temporada (sin incluir Arsenal, Orenburg, Dynamo, Krylia Sovetov y Yenisei, porque no tienen estadísticas de temporadas pasadas o hay pocas estadísticas sobre ellas):

 RPL_2018_2019 = pd.read_csv('Team Name 2018 2019.csv', encoding = 'cp1251') teamList = RPL_2018_2019['Team Name'].tolist() teamList 

imagen

Eliminamos partidos con equipos que no participan en la temporada 2018/2019:

 deleteTeam = [x for x in pd.unique(data['']) if x not in teamList] for name in deleteTeam: data = data[data[''] != name] data = data[data[''] != name] data = data.reset_index(drop=True) 

Función que devuelve las estadísticas del equipo para una temporada:

 def GetSeasonTeamStat(team, season): goalScored = 0 #  goalAllowed = 0 #  gameWin = 0 # gameDraw = 0 # gameLost = 0 # totalScore = 0 #   matches = 0 #   xG = 0 #  shot = 0 # shotOnTarget = 0 #   cross = 0 # accurateCross = 0 #  totalHandle = 0 #  averageHandle = 0 #     Pass = 0 # accuratePass = 0 #  PPDA = 0 #    for i in range(len(data)): if (((data[''][i] == season) and (data[''][i] == team) and (data[''][i] == 2)) or ((data[''][i] == season-1) and (data[''][i] == team) and (data[''][i] == 1))): matches += 1 goalScored += data[''][i] goalAllowed += data[''][i] if (data[''][i] > data[''][i]): totalScore += 3 gameWin += 1 elif (data[''][i] < data[''][i]): gameLost +=1 else: totalScore += 1 gameDraw += 1 xG += data['xG'][i] shot += data[''][i] shotOnTarget += data['  '][i] Pass += data[''][i] accuratePass += data[' '][i] totalHandle += data[''][i] cross += data[''][i] accurateCross += data[' '][i] PPDA += data['PPDA'][i] averageHandle = round(totalHandle/matches, 3) #      return [gameWin, gameDraw, gameLost, goalScored, goalAllowed, totalScore, round(xG, 3), round(PPDA, 3), shot, shotOnTarget, Pass, accuratePass, cross, accurateCross, round(averageHandle, 3)] 

Ejemplo de uso de funciones:

 GetSeasonTeamStat("", 2018) #    2017/2018 

imagen

Por conveniencia, podemos agregar el código:

 returnNames = ["", "", "", "\n ", " ", "\n ", "\nxG ( )", "PPDA ( )", "\n", "  ", "\n", " ", "\n", " ", "\n (   )"] for i, n in zip(returnNames, GetSeasonTeamStat("", 2018)): print(i, n) 

imagen

¿Por qué nuestras estadísticas son diferentes de las estadísticas reales?
Estadísticas reales de Spartak en la temporada 2017/2018:

imagen

Las estadísticas son diferentes porque Tomamos en cuenta los partidos de equipos que no juegan en el RPL en la temporada 2018/2019. Es decir, no tenemos en cuenta los partidos de Spartak - SKA, Spartak - Tosno, etc.

Función que devolverá estadísticas de todos los equipos para la temporada:

 def GetSeasonAllTeamStat(season): annual = collections.defaultdict(list) for team in teamList: team_vector = GetSeasonTeamStat(team, season) annual[team] = team_vector return annual 

Entrenamiento modelo


Escribiremos una función que devolverá datos de entrenamiento. Ella crea un diccionario con vectores de equipo para todas las estaciones. Para cada juego, la función calcula la diferencia entre los vectores de los equipos para una determinada temporada y la escribe en xTrain. La función establece yTrain en 1 si el equipo local gana, y 0 en caso contrario.

 def GetTrainingData(seasons): totalNumGames = 0 for season in seasons: annual = data[data[''] == season] totalNumGames += len(annual.index) numFeatures = len(GetSeasonTeamStat('', 2016)) #     xTrain = np.zeros(( totalNumGames, numFeatures)) yTrain = np.zeros(( totalNumGames )) indexCounter = 0 for season in seasons: team_vectors = GetSeasonAllTeamStat(season) annual = data[data[''] == season] numGamesInYear = len(annual.index) xTrainAnnual = np.zeros(( numGamesInYear, numFeatures)) yTrainAnnual = np.zeros(( numGamesInYear )) counter = 0 for index, row in annual.iterrows(): team = row[''] t_vector = team_vectors[team] rivals = row[''] r_vector = team_vectors[rivals] diff = [a - b for a, b in zip(t_vector, r_vector)] if len(diff) != 0: xTrainAnnual[counter] = diff if team == row['']: yTrainAnnual[counter] = 1 else: yTrainAnnual[counter] = 0 counter += 1 xTrain[indexCounter:numGamesInYear+indexCounter] = xTrainAnnual yTrain[indexCounter:numGamesInYear+indexCounter] = yTrainAnnual indexCounter += numGamesInYear return xTrain, yTrain 

Aprendemos datos de entrenamiento para todas las estaciones desde 2015/2016 hasta 2018/2019.

 years = range(2016,2019) xTrain, yTrain = GetTrainingData(years) 

Para predecir la probabilidad de ganar, utilizaremos el algoritmo de aprendizaje automático LinearRegression de la biblioteca Scikit-Learn.

 from sklearn.linear_model import LinearRegression model = LinearRegression() model.fit(xTrain, yTrain) 

Escribiremos una función que devolverá pronósticos. Devolverá un valor entre 0 y 1, donde 0 es la pérdida y 1 es la ganancia.

 def createGamePrediction(team1_vector, team2_vector): diff = [[a - b for a, b in zip(team1_vector, team2_vector)]] predictions = model.predict(diff) return predictions 

Resultados


Por ejemplo, veamos las predicciones del algoritmo para el partido Zenit - Spartak

 team1_name = "" team2_name = "" team1_vector = GetSeasonTeamStat(team1_name, 2019) team2_vector = GetSeasonTeamStat(team2_name, 2019) print (',   ' + team1_name + ':', createGamePrediction(team1_vector, team2_vector)) print (',   ' + team2_name + ':', createGamePrediction(team2_vector, team1_vector)) 

imagen

Resulta que en el partido Zenit - Spartak, la probabilidad de victoria para Zenit es del 47% (17/03/2019 Spartak 1-1 Zenit).

Propongo hacer un pronóstico dado lo siguiente:
Hasta 40%: el equipo simplemente no ganará (perderá o empatará)
Del 40% al 60%: alta probabilidad de empate
Desde 60%: el equipo definitivamente no perderá (gane o empate)

Obtenga pronósticos para CSKA contra todos los otros clubes

 for team_name in teamList: team1_name = "" team2_name = team_name if(team1_name != team2_name): team1_vector = GetSeasonTeamStat(team1_name, 2019) team2_vector = GetSeasonTeamStat(team2_name, 2019) print(team1_name, createGamePrediction(team1_vector, team2_vector), " - ", team2_name, createGamePrediction(team2_vector, team1_vector,)) 

imagen

El algoritmo dio un pronóstico correcto para casi todos los partidos que no terminaron en empate. El único pronóstico inexacto: CSKA Moscú - Zenit. La probabilidad de la victoria del CSKA es mayor en 0.001, se podría suponer que los equipos tienen la misma fuerza y ​​jugarán en un empate, pero al final Zenit ganó (3-1).

Conclusión


Nuestro algoritmo es muy primitivo. Solo tiene en cuenta las estadísticas de los partidos (y luego solo 15 parámetros básicos), y el resultado en el fútbol depende de muchos factores. Incluso las condiciones del campo o el clima pueden afectar el resultado del juego.

A continuación, me gustaría aumentar el número de signos, crear una muestra de prueba, probar varios algoritmos, configurar el modelo y obtener los pronósticos más precisos.

Le agradecería que deje sus ideas y comentarios.

Source: https://habr.com/ru/post/456226/


All Articles