Predecir cuántos puntos anotará un jugador de la NBA

Para predecir cuántos puntos anotará un jugador de la NBA se necesitan datos históricos del jugador en cuestión y un modelo lo suficiente bueno para realizar esta predicción con una exactitud aceptable. 

Se obtendrán los datos de una temporada para construir varios modelos de predicción. Los modelos construidos se utilizarán para intentar predecir los puntos a anotar en la siguiente temporada. De esta forma, se comprobará si los modelos son de utilidad, porque se podrá ver cuánto difiere la predicción del valor real.

Obtener datos necesarios

Los datos necesarios para la construcción del modelo se han obtenido en base a este artículo, publicado anteriormente, aplicando técnicas de web scraping

En este caso se obtendrán los datos de 2019 y 2020. Los primeros se utilizarán para construir los modelos y los segundos para comprobar el comportamiento de los mismos.

Los datos obtenidos serían los siguientes:

  • Jugador: Nombre del jugador
  • Posición: Posición.
  • Age: Edad del jugador.
  • G: Partidos jugados.
  • GS: Partidos iniciados.
  • MP: Minutos jugados.
  • FG (3P + 2P): Tiros de campo.
  • FGA (3PA + 2PA): Intentos de canasta.
  • FG%: Porcentaje de goles de campo.
  • 3P: canastas de 3 puntos.
  • 3PA: Intentos de canasta de 3 puntos.
  • 3P%: FG% en FGA de 3 puntos (intentos de canasta).
  • 2P: canastas de 2 puntos.
  • 2PA: Intentos de canasta de 2 puntos.
  • 2P%: FG% sobre 2 puntos FGA’s (intentos de canasta).
  • FT: Tiros libres.
  • FTA: Intentos de tiros libres.
  • FT%: Porcentaje de tiros libres.
  • ORB: Rebotes ofensivos.
  • DRB: Rebotes Defensivos.
  • TRB: Rebotes Totales
  • AST: Asistencias.
  • STL: Robos.
  • BLK: Bloqueos.
  • TOV: Pérdidas de balón.
  • PF: Faltas personales.
  • PTS: Puntos.

Curación de los datos

Lo primero que se ha realizado es cruzar los jugadores entre las dos temporadas, de tal forma que haya los mismos jugadores en ambos conjuntos de datos. Este proceso se ha realizado con la siguiente función:

def crossjoin(df1, df2):
    for index, row in df1.iterrows():
        player = df2.loc[df2['Player'] == row['Player']]
        if(not player.empty):
            continue
        else:
            df1.drop(index, inplace=True)
            df2.drop(player.index, inplace=True)
            
    return (df1, df2)
data2020, data2019 = crossjoin(data2020, data2019)
data2019, data2020 = crossjoin(data2019, data2020)

Para poder realizar las siguientes operaciones correctamente ha sido necesario convertir los datos, ya que originalmente erán de tipo object, y se producían errores al calcular las correlaciones. Por ello, se han transformado a tipo numérico de la siguiente forma:

data2019 = data2019[['Player', 'G', 'GS', 'MP', 'FG', 'FGA', 'FG%', '3P',
       '3PA', '3P%', '2P', '2PA', '2P%', 'eFG%', 'FT', 'FTA', 'FT%', 'ORB',
       'DRB', 'TRB', 'AST', 'STL', 'BLK', 'TOV', 'PF', 'PTS']].apply(lambda x: pd.to_numeric(x.astype(str).str.replace(',',''), errors='ignore'))

data2020 = data2020[['Player','G', 'GS', 'MP', 'FG', 'FGA', 'FG%', '3P',
       '3PA', '3P%', '2P', '2PA', '2P%', 'eFG%', 'FT', 'FTA', 'FT%', 'ORB',
       'DRB', 'TRB', 'AST', 'STL', 'BLK', 'TOV', 'PF', 'PTS']].apply(lambda x: pd.to_numeric(x.astype(str).str.replace(',',''), errors='ignore'))

Por último, se ha creado una nueva columna con todos los intentos de tiro sumados en una columna de la siguiente manera:

data2019['TA'] = data2019['FGA'] + data2019['FTA'] 
data2020['TA'] = data2020['FGA'] + data2020['FTA'] 

Estudio de la correlación

La correlación es un tipo de asociación entre dos variables numéricas, con el objetivo de evaluar si existe una relación entre ambas, más info aquí.

La variable que pretendemos predecir es los puntos (PTS), por lo tanto debemos estudiar la correlación de esta variable con las demás. Por ello, se han seleccionado las variables que tienen una correlación mayor a 0.7, después de visionar de forma general todas las correlaciones.

matriz de correlación

En la matriz de correlaciones se puede observar a primera vista una alta correlación con los intentos de tiro. Por ello, esta variable puede ser una candidata a variable predictora.

Antes de comenzar con la construcción de modelos, es conveniente realizar una última conversión de los datos. Los datos de un jugador que ha jugado en varios equipos en la misma temporada están divididos según los equipos en los que haya jugado. A efectos de este caso de estudio nos interesa tener todos los datos de un jugador aglutinados sin importar los equipos en los que haya estado durante la temporada. Por ello, se aglutinan los datos que se encuentren divididos, pero solo de las variables elegidas anteriormente. Por lo tanto, solo habría que realizar una suma simple de la siguiente forma:

corr_columns = list(corr2019[corr2019['PTS'] > 0.7]['PTS'].keys())
corr_columns.append('Player')
data2019 = data2019.groupby('Player', as_index=False)[corr_columns].sum()

Construcción del modelo

Para predecir cuántos puntos anotará un jugador de la NBA se van a construir varios modelos de regresión lineal simple de distintas variables. Este tipo de modelos están basados en la siguiente función:

función regresión lineal simple

Se han construido 4 modelos con distintas variables:

  • Total de intentos (TA)
  • Minutos jugados (MP)
  • Asistencias
  • Robos

Realizar predicción

Una vez construidos los modelos se ha realizado la predicción de los puntos de la temporada 2020 para comprobar la efectividad de los distintos modelos.

Se podría decir que la predicción de un modelo es buena cuanto menos difiera puntos que realmente se han anotado. Por ello, se ha calculado la diferencia entre los puntos anotados realmente y las distintas predicciones llevadas a cabo. 

def percentage_change(col1,col2):
    return abs(((col2 - col1) / col1) * 100)

data2020['change_pred_TA'] = percentage_change(data2020['PTS'], data2020['PTS_pred_TA'])
data2020['change_pred_MP'] = percentage_change(data2020['PTS'], data2020['PTS_pred_MP'])
data2020['change_pred_AST'] = percentage_change(data2020['PTS'], data2020['PTS_pred_AST'])
data2020['change_pred_STL'] = percentage_change(data2020['PTS'], data2020['PTS_pred_STL'])

Una vez calculadas estas diferencias se ha calculado cuál es el mejor modelo para cada jugador en base a la menor diferencia calculada para cada jugador en cuestión.

def min_name_dict(test_dict):
    min_value = min(test_dict.values())
    return [key for key in test_dict if test_dict[key] == min_value][0]

data2020['best'] = data2020.apply(lambda row: min_name_dict(row[['change_pred_TA', 'change_pred_MP', 'change_pred_AST',
       'change_pred_STL']].to_dict()).split('_')[-1], axis=1)

Los resultados obtenidos son los siguientes:

ModeloMejor predicción (veces)
Intentos de tiro132
Asistencias121
Minutos jugados74
Robos73

De estos resultados se puede concluir que el mejor modelo de predicción es el basado en los intentos de tiro. Aunque no se puede considerar a este modelo fiable porque se produce el fenómeno de la multicolinealidad, ya que existe demasiada correlación entre los puntos anotados y los intentos de tiro.

También se puede observar como las asistencias es un predictor que se puede considerar aceptable teniendo en cuenta los datos utilizados. Deduciendo que los jugadores más anotadores suelen llevar a cabo bastantes asistencias.

Como conclusión comentar que aunque no podemos considerar que hemos obtenido un modelo de predicción fiable, se pueden utilizar los intentos de tiro para saber cómo esta siendo el rendimiento de un jugador. Debido a que sí un jugador en un punto determinado de la temporada tiene menos intentos de tiro que la temporada anterior en la misma fecha es bastante probable que su rendimiento sea menor. 

Jupyter notebook

Fuentes

Deja un comentario