En la parte I de este tutorial, el objetivo fue estimar el número de partidos que ganó un equipo de la Major League Baseball (MLB) en una temporada, a partir de sus estadísticas y otras variables de esa campaña.
En la segunda parte del proyecto, probarás un modelo de regresión logística y uno de random forest de Scikit-Learn (sklearn) para predecir qué jugadores serán elegidos para el Salón de la Fama a partir de sus estadísticas y premios de carrera. Cada fila representará a un jugador y datos de su trayectoria. En béisbol hay cuatro grandes subgrupos por posición: infielders, outfielders, pitchers y catchers. Aquí nos centraremos en infielders y outfielders.
El National Baseball Hall of Fame and Museum está en Cooperstown (Nueva York) y se inauguró en 1939. Un jugador puede ser elegido para el Salón de la Fama si cumple estos criterios:
Haber disputado al menos diez temporadas;
Llevar retirado al menos cinco temporadas;
Un comité de evaluación debe aprobar su inclusión en la papeleta; la mayoría de quienes jugaron regularmente diez o más años se consideran dignos;
No estar en la lista de inelegibles (es decir, no estar expulsado del béisbol);
Se considera elegido si recibe al menos el 75% de los votos;
Permanece en la papeleta al año siguiente si recibe al menos el 5% de los votos y puede estar un máximo de 10 años.
Un jugador no elegido puede entrar por el Veterans Committee o por un comité especial designado por el comisionado de la MLB, pero en este proyecto nos centraremos solo en quienes fueron elegidos por votación. En esencia, queremos saber:
«¿Puedes construir un modelo de machine learning que prediga con precisión si un jugador de la MLB será elegido para el Salón de la Fama?».
En la primera parte cargaste los datos desde una base de datos SQLite. Esta vez los cargarás desde archivos CSV. Sean Lahman recopiló estos datos en su sitio web.
Importación de datos
Necesitarás leer los siguientes CSV en DataFrames de pandas:
Master.csv aporta nombres de jugadores, fecha de nacimiento (DOB) e información biográfica;
Fielding.csv contiene estadísticas defensivas;
Batting.csv contiene estadísticas de bateo;
también leerás AwardsPlayers.csv con los premios ganados por los jugadores;
AllstarFull.csv incluye todas las apariciones en el All-Star Game;
necesitarás los datos de votación al Salón de la Fama en HallOfFame.csv;
y Appearances.csv, con el detalle de posiciones en las que actuó cada jugador.
No te preocupes si algún concepto no está claro aún: lo irás viendo a medida que avances.
Primero, descarga los CSV desde aquí. Idealmente, guarda todos los datos en una carpeta específica para este proyecto. Luego abre tu terminal o arranca Jupyter Notebook y ponte manos a la obra.
Carga pandas como pd y lee cada CSV. Incluirás solo ciertas columnas; puedes revisar las disponibles aquí.
# Import data to DataFramesimport pandas as pd# Read in the CSV filesmaster_df = pd.read_csv('Master.csv',usecols=['playerID','nameFirst','nameLast','bats','throws','debut','finalGame'])fielding_df = pd.read_csv('Fielding.csv',usecols=['playerID','yearID','stint','teamID','lgID','POS','G','GS','InnOuts','PO','A','E','DP'])batting_df = pd.read_csv('Batting.csv')awards_df = pd.read_csv('AwardsPlayers.csv', usecols=['playerID','awardID','yearID'])allstar_df = pd.read_csv('AllstarFull.csv', usecols=['playerID','yearID'])hof_df = pd.read_csv('HallOfFame.csv',usecols=['playerID','yearid','votedBy','needed_note','inducted','category'])appearances_df = pd.read_csv('Appearances.csv')Con la tecnología de
Nota: en este tutorial, los datos se cargarán por ti.
Limpieza y preprocesamiento
Ahora que has importado los datos, puedes empezar a limpiarlos y preprocesarlos. Como trabajas con 7 CSV, conviene revisarlos uno a uno: en las siguientes secciones, veremos los DataFrames por separado en este orden: batting_df, fielding_df, awards_df, allstar_df, hof_df, appearances_df y master_df.
batting_df
Empezarás con el DataFrame batting_df. Imprime las primeras filas con head():
# Print first few rows of `batting_df`
batting_df.______
Consejo: como has importado todas las columnas de esta tabla, utiliza el atributo columns para verlas y entender con qué trabajas. Usa también cualquier otra función de Pandas para inspeccionar tus datos a fondo.
Al inspeccionar los resultados, notarás tres cosas sobre batting_df y sus columnas:
Hay muchas IDs. Es normal: los datos provienen de una base de datos y todas las tablas se relacionan entre sí, como es habitual en bases de datos relacionales.
playerID, identificador del jugador;
yearID, el año;
teamID, el equipo en el que juega;
lgID, la liga.
Como esperarías en batting_df, hay muchas estadísticas de los jugadores; por ejemplo:
G número de partidos,
AB turnos al bate,
R carreras,
H hits,
HR home runs,
RBI carreras impulsadas,
SO strikeouts.
Los datos no están en formato ordenado (tidy). Si no te suena el concepto, aquí tienes una explicación detallada de tidy data aquí. Asegurar el formato tidy es crucial: en resumen, cada fila debe ser una observación (en este caso, la carrera de un jugador) y cada columna, una variable. La mayoría de filas actuales son temporadas individuales; debes agregarlas para que cada jugador tenga una sola fila con su carrera.
Para conseguirlo, crearás diccionarios para cada jugador dentro de un diccionario maestro. Agregarás las estadísticas de cada jugador a su diccionario y luego convertirás el diccionario principal en un DataFrame.
Para empezar, crea un diccionario para estadísticas del jugador y otro para los años jugados. Agrega las estadísticas de batting_df al diccionario player_stats y los años jugados a years_played:
Ahora recorre el diccionario years_played y añade el número de años jugados por cada jugador a player_stats:
# Iterate through `years_played` and add the number of years played to `player_stats`for k, v in years_played.items(): player_stats[k]['Years_Played'] = len(list(set(v)))Con la tecnología de
fielding_df
Seguimos con fielding_df. Esta tabla contiene más estadísticas de jugadores y es muy similar a batting_df. Como ya agregaste estadísticas por jugador en el diccionario player_stats, conviene hacer lo mismo con las de fielding_df.
Concretamente, añadirás nuevas claves a cada diccionario de jugador dentro de player_stats mientras iteras por fielding_df. Primero crea una fielder_list para identificar qué jugadores ya tienen claves de fildeo inicializadas, y sumar correctamente:
# Initialize `fielder_list`fielder_list = []# Add fielding stats to `player_stats` from `fielding_df`for i, row in fielding_df.iterrows(): playerID = row['playerID'] Gf = row['G'] GSf = row['GS'] POf = row['PO'] Af = row['A'] Ef = row['E'] DPf = row['DP'] if playerID in player_stats and playerID in fielder_list: player_stats[playerID]['Gf'] = player_stats[playerID]['Gf'] + Gf player_stats[playerID]['GSf'] = player_stats[playerID]['GSf'] + GSf player_stats[playerID]['POf'] = player_stats[playerID]['POf'] + POf player_stats[playerID]['Af'] = player_stats[playerID]['Af'] + Af player_stats[playerID]['Ef'] = player_stats[playerID]['Ef'] + Ef player_stats[playerID]['DPf'] = player_stats[playerID]['DPf'] + DPf else: fielder_list.append(playerID) player_stats[playerID]['Gf'] = Gf player_stats[playerID]['GSf'] = GSf player_stats[playerID]['POf'] = POf player_stats[playerID]['Af'] = Af player_stats[playerID]['Ef'] = Ef player_stats[playerID]['DPf'] = DPfCon la tecnología de
awards_df
Vamos ahora con awards_df: a lo largo de su carrera, los jugadores pueden recibir distintos premios por su rendimiento. Este DataFrame recoge los premios ganados.
Una buena forma de inspeccionarlo es imprimir los premios únicos con unique(). O prueba con head(), tail(), info(), etc., para conocer mejor los datos.
Consejo: del primer bloque sabes que solo leíste tres columnas: playerID, awardID y yearID.
# Print out unique awards from `awards_df`
print(awards_df['awardID'].________)
Como ves, hay bastantes premios en awards_df… ¿los conoces todos?
Como comentamos en la Parte I, conocer bien los datos es clave. Por ejemplo, los mejores premios a incluir son Most Valuable Player, Rookie of the Year, Gold Glove, Silver Slugger y World Series MVP, algo que concluí basándome en mi experiencia siguiendo la MLB.
Podríamos determinar qué premios se correlacionan más con entrar al Salón de la Fama, pero requeriría bastante más código.
Ahora añade un conteo de cada premio en el diccionario de cada jugador dentro de player_stats: primero filtra awards_df para cada premio, creando cinco series. Luego crea una lista llamada awards_list con las cinco series. Como hiciste con fielding_df, crea listas para identificar qué claves se han añadido en player_stats para cada premio. Crea una lista lists con esas cinco listas. Ya puedes iterar por awards_list y contar cuántos de cada premio tiene cada jugador:
# Create DataFrames for each awardmvp = awards_df[awards_df['awardID'] == 'Most Valuable Player']roy = awards_df[awards_df['awardID'] == 'Rookie of the Year']gg = awards_df[awards_df['awardID'] == 'Gold Glove']ss = awards_df[awards_df['awardID'] == 'Silver Slugger']ws_mvp = awards_df[awards_df['awardID'] == 'World Series MVP']# Include each DataFrame in `awards_list`awards_list = [mvp,roy,gg,ss,ws_mvp]# Initialize lists for each of the above DataFramesmvp_list = []roy_list = []gg_list = []ss_list = []ws_mvp_list = []# Include each of the above lists in `lists`lists = [mvp_list,roy_list,gg_list,ss_list,ws_mvp_list]# Add a count for each award for each player in `player_stats`for index, v in enumerate(awards_list): for i, row in v.iterrows(): playerID = row['playerID'] award = row['awardID'] if playerID in player_stats and playerID in lists[index]: player_stats[playerID][award] += 1 else: lists[index].append(playerID) player_stats[playerID][award] = 1Con la tecnología de
allstar_df
El DataFrame allstar_df indica qué jugadores aparecieron en All-Star Games. Es un partido de exhibición a mitad de temporada. La MLB tiene dos ligas: Americana y Nacional. Los 25 mejores de cada liga representan a su liga.
Como antes, itera por allstar_df y añade un conteo de apariciones al diccionario de cada jugador en player_stats.
# Initialize `allstar_list`allstar_list = []# Add a count for each Allstar game appearance for each player in `player_stats`for i, row in allstar_df.iterrows(): playerID = row['playerID'] if playerID in player_stats and playerID in allstar_list: player_stats[playerID]['AS_games'] += 1 else: allstar_list.append(playerID) player_stats[playerID]['AS_games'] = 1Con la tecnología de
hof_df
Seguimos con hof_df, que recoge qué jugadores, mánagers y directivos fueron nominados o elegidos para el Salón de la Fama y por qué vía.
¡Añadir esta información a player_stats puede ser muy valioso! Para ello, filtra hof_df para incluir solo jugadores elegidos al Salón. Luego recorre hof_df para marcar en player_stats quiénes fueron elegidos y cómo:
# filter `hof_df` to include only instances where a player was inducted into the Hall of Famehof_df = hof_df[(hof_df['inducted'] == 'Y') & (hof_df['category'] == 'Player')]# Indicate which players in `player_stats` were inducted into the Hall of Famefor i, row in hof_df.iterrows(): playerID = row['playerID'] if playerID in player_stats: player_stats[playerID]['HoF'] = 1 player_stats[playerID]['votedBy'] = row['votedBy']Con la tecnología de
player_stats
Ya has reunido datos de batting_df, fielding_df, awards_df, allstar_df y hof_df en el diccionario player_stats. Falta trabajar con appearances_df, pero ahora es buen momento para convertir player_stats en DataFrame.
Usa pandas con from_dict() para crear stats_df:
# Convert `player_stats` into a DataFramestats_df = pd.DataFrame.from_dict(player_stats, orient='index')Con la tecnología de
El DataFrame stats_df usa el playerID como índice. Añade la columna playerID a partir del índice y después haz un inner join con master_df.
Recuerda: un inner join selecciona filas de ambas tablas solo si hay coincidencia entre columnas. Aquí lo usas porque quieres mantener la información común entre stats_df y master_df.
# Add a column for playerID from the `stats_df` index
stats_df['playerID'] = stats_df._____
# Join `stats_df` and `master_df`
master_df = master_df.join(________,on='playerID',how='inner', rsuffix='mstr')
# Inspect first rows of `master_df`
master_df.______
appearances_df
El último DataFrame que debes revisar antes de crear nuevas variables es appearances_df. Contiene el número de apariciones de cada jugador en cada posición por año.
Imprime las primeras filas con head():
print(appearances_df.______)
Consejo: usa la consola de IPython para inspeccionar con info(), tail(), y los atributos columns e index. Verás que appearances_df tiene 21 columnas; algunas son claras (IDs ya vistas) y otras más crípticas como G_3b, G_dh, etc.
Ahora conviene consultar la información del README:
G_all Total de partidos jugados
GS Partidos como titular
G_batting Partidos en los que bateó
G_defense Partidos en los que jugó a la defensiva
G_p Partidos como pitcher
G_c Partidos como catcher
G_1b Primera base
G_2b Segunda base
G_3b Tercera base
G_ss Shortstop (parador en corto)
G_lf Jardín izquierdo
G_cf Jardín central
G_rf Jardín derecho
G_of Outfielder
G_dh Bateador designado
G_ph Bateador emergente
G_pr Corredor emergente
Como ves, hay mucha información en appearances_df que debes extraer. También hay que agregar apariciones por posición para obtener totales de carrera.
Como recordarás de la primera parte, a lo largo de la historia de la MLB hubo eras con más o menos carreras por partido. Esto afecta mucho a las estadísticas de un jugador. Los votantes del Salón de la Fama lo tienen en cuenta, así que tu modelo también debe hacerlo.
Para recoger esta información, crea un diccionario llamado pos_dict. Recorre appearances_df y añade un diccionario por jugador como hiciste con player_stats. Agrega las apariciones por posición usando la posición como clave y el número de apariciones como valor. Agrega además el número de partidos jugados en cada era usando la era como clave y el conteo de partidos como valor.
# Initialize a dictionarypos_dict = {}# Iterate through `appearances_df`# Add a count for the number of appearances for each player at each position# Also add a count for the number of games played for each player in each era.for i, row in appearances_df.iterrows(): ID = row['playerID'] year = row['yearID'] if ID in pos_dict: pos_dict[ID]['G_all'] = pos_dict[ID]['G_all'] + row['G_all'] pos_dict[ID]['G_p'] = pos_dict[ID]['G_p'] + row['G_p'] pos_dict[ID]['G_c'] = pos_dict[ID]['G_c'] + row['G_c'] pos_dict[ID]['G_1b'] = pos_dict[ID]['G_1b'] + row['G_1b'] pos_dict[ID]['G_2b'] = pos_dict[ID]['G_2b'] + row['G_2b'] pos_dict[ID]['G_3b'] = pos_dict[ID]['G_3b'] + row['G_3b'] pos_dict[ID]['G_ss'] = pos_dict[ID]['G_ss'] + row['G_ss'] pos_dict[ID]['G_lf'] = pos_dict[ID]['G_lf'] + row['G_lf'] pos_dict[ID]['G_cf'] = pos_dict[ID]['G_cf'] + row['G_cf'] pos_dict[ID]['G_rf'] = pos_dict[ID]['G_rf'] + row['G_rf'] pos_dict[ID]['G_of'] = pos_dict[ID]['G_of'] + row['G_of'] pos_dict[ID]['G_dh'] = pos_dict[ID]['G_dh'] + row['G_dh'] if year < 1920: pos_dict[ID]['pre1920'] = pos_dict[ID]['pre1920'] + row['G_all'] elif year >= 1920 and year <= 1941: pos_dict[ID]['1920-41'] = pos_dict[ID]['1920-41'] + row['G_all'] elif year >= 1942 and year <= 1945: pos_dict[ID]['1942-45'] = pos_dict[ID]['1942-45'] + row['G_all'] elif year >= 1946 and year <= 1962: pos_dict[ID]['1946-62'] = pos_dict[ID]['1946-62'] + row['G_all'] elif year >= 1963 and year <= 1976: pos_dict[ID]['1963-76'] = pos_dict[ID]['1963-76'] + row['G_all'] elif year >= 1977 and year <= 1992: pos_dict[ID]['1977-92'] = pos_dict[ID]['1977-92'] + row['G_all'] elif year >= 1993 and year <= 2009: pos_dict[ID]['1993-2009'] = pos_dict[ID]['1993-2009'] + row['G_all'] elif year > 2009: pos_dict[ID]['post2009'] = pos_dict[ID]['post2009'] + row['G_all'] else: pos_dict[ID] = {} pos_dict[ID]['G_all'] = row['G_all'] pos_dict[ID]['G_p'] = row['G_p'] pos_dict[ID]['G_c'] = row['G_c'] pos_dict[ID]['G_1b'] = row['G_1b'] pos_dict[ID]['G_2b'] = row['G_2b'] pos_dict[ID]['G_3b'] = row['G_3b'] pos_dict[ID]['G_ss'] = row['G_ss'] pos_dict[ID]['G_lf'] = row['G_lf'] pos_dict[ID]['G_cf'] = row['G_cf'] pos_dict[ID]['G_rf'] = row['G_rf'] pos_dict[ID]['G_of'] = row['G_of'] pos_dict[ID]['G_dh'] = row['G_dh'] pos_dict[ID]['pre1920'] = 0 pos_dict[ID]['1920-41'] = 0 pos_dict[ID]['1942-45'] = 0 pos_dict[ID]['1946-62'] = 0 pos_dict[ID]['1963-76'] = 0 pos_dict[ID]['1977-92'] = 0 pos_dict[ID]['1993-2009'] = 0 pos_dict[ID]['post2009'] = 0 if year < 1920: pos_dict[ID]['pre1920'] = row['G_all'] elif year >= 1920 and year <= 1941: pos_dict[ID]['1920-41'] = row['G_all'] elif year >= 1942 and year <= 1945: pos_dict[ID]['1942-45'] = row['G_all'] elif year >= 1946 and year <= 1962: pos_dict[ID]['1946-62'] = row['G_all'] elif year >= 1963 and year <= 1976: pos_dict[ID]['1963-76'] = row['G_all'] elif year >= 1977 and year <= 1992: pos_dict[ID]['1977-92'] = row['G_all'] elif year >= 1993 and year <= 2009: pos_dict[ID]['1993-2009'] = row['G_all'] elif year > 2009: pos_dict[ID]['post2009'] = row['G_all']Con la tecnología de
Ahora que el diccionario está completo, conviértelo a DataFrame con from_dict():
# Convert the `pos_dict` to a DataFramepos_df = pd.DataFrame.from_dict(pos_dict, orient='index')Con la tecnología de
Imprime las columnas e inspecciona un poco más:
# Print the columns
print(pos_df._______)
# Print the first rows
print(pos_df.______)
A continuación, calcula el porcentaje de veces que cada jugador actuó en cada posición y en cada era.
Primero, crea una lista pos_col_list a partir de las columnas de pos_df y elimina la cadena ‘G_all’. Luego crea columnas nuevas en pos_df para el porcentaje por posición y por era, iterando por pos_col_list y dividiendo los partidos en cada posición/era entre el total del jugador. Por último, imprime las primeras filas:
# Create a list from the columns of `pos_df`
pos_col_list = pos_df.columns.________
# Remove the string 'G_all'
pos_col_list.remove('G_all')
# Loop through the list and divide each column by the players total games played
for col in pos_col_list:
column = col + '_percent'
pos_df[column] = pos_df[col] / pos_df['G_all']
# Print out the first rows of `pos_df`
print(pos_df._______)
Como mencionamos, este proyecto se centra en infielders y outfielders, ya que cada posición se juzga de forma distinta para el Salón de la Fama. Infielders y outfielders se evalúan de forma similar; pitchers y catchers se juzgan más por defensa, lo cual queda fuera del alcance de este proyecto.
Como en los primeros años muchos jugadores actuaban en varias posiciones, elimina pitchers y catchers filtrando a quienes jugaron más del 10% de sus partidos en alguna de esas posiciones.
# Filter `pos_df` to eliminate players who played 10% or more of their games as Pitchers or Catchers
pos_df = pos_df[(pos_df['G_p_percent'] < 0.1) & (pos_df['G_c_percent'] < 0.1)]
# Get info on `pos_df`
pos_df.______
master_df
Recuerda que hiciste un inner join entre master_df y stats_df (compuesto con datos de batting_df, fielding_df, awards_df, allstar_df y hof_df) para lograr un dataset más uniforme con estadísticas y datos maestros (nombres, fecha de debut y biografía).
Ahora quieres unir el master_df uniforme con pos_df; ¡ojo! Aquí debes usar un right join porque solo quieres conservar a los jugadores que estén en pos_df:
# Join `pos_df` and `master_df`
master_df = master_df.join(pos_df,on='playerID',how='right')
# Print out the first rows of `master_df`
print(master_df.______)
Ahora filtra master_df para dejar solo jugadores que fueron elegidos por votación o que no entraron en absoluto.
Algunos entran por el Veterans Committee o por comités especiales. Suelen ser seleccionados por motivos más allá de las estadísticas —difíciles de cuantificar para tu modelo—, así que es mejor excluirlos.
# Replace NA values with `None`
master_df['votedBy'] = master_df['votedBy'].fillna('NoNe')
# Filter `master_df` to include only players who were voted into the Hall of Fame or Players who did not make it at all
master_df = master_df[(master_df['_______'] == 'None') | (master_df['votedBy'] == 'BBWAA') | (master_df['votedBy'] == 'Run Off')]
# Inspect `master_df`
master_df.info()
Las columnas bats y throws deben convertirse a numérico: crea una función que convierta R en 1 y L en 0. Usa apply() para crear bats_R y throws_R:
Las columnas debut y finalGame son cadenas. Debes extraer el año. Importa datetime, convierte ambas a objeto datetime con pd.to_datetime(), luego extrae el año con dt.strftime('%Y') y conviértelo a numérico con pd.to_numeric().
# Import datetime
from datetime import datetime
# Convert the `debut` column to datetime
master_df['debut'] = pd.___________(master_df['debut'])
# Convert the `finalGame` column to datetime
master_df['finalGame'] = pd.to_datetime(master_df['finalGame'])
# Create new columns for debutYear and finalYear
master_df['debutYear'] = pd.to_numeric(master_df['debut'].dt.strftime('%Y'), errors='coerce')
master_df['finalYear'] = _____________(master_df['finalGame'].dt.strftime('%Y'), errors='coerce')
# Return the first rows of `master_df`
print(master_df.head())
Actualmente, master_df tiene algunas columnas innecesarias. Usa drop() sobre master_df y crea un nuevo DataFrame llamado df. Imprime las columnas de df y muestra el recuento de nulos por columna.
En la primera parte, trabajabas con estadísticas de equipo y los nulos podían deberse a datos faltantes (p. ej., es raro que un equipo pase una temporada sin un double play).
Con jugadores, es más probable que los nulos sean ceros.
Por eso, crea una lista de columnas numéricas y recórrela para rellenar nulos con 0 usando fillna(). Luego usa dropna() para eliminar nulos restantes.
Este artículo trata de cómo y cuándo utilizar la clasificación Random Forest con scikit-learn. Centrado en conceptos, flujo de trabajo y ejemplos. También veremos cómo utilizar la matriz de confusión y las importancias de las características.
Aprende sobre la regresión logística, sus propiedades básicas, y construye un modelo de machine learning sobre una aplicación del mundo real en Python.
En este tutorial, aprenderás sobre la clasificación mediante árboles de decisión, las medidas de selección de atributos y cómo crear y optimizar un clasificador de árboles de decisión utilizando el paquete Scikit-learn de Python.
En la parte I de este tutorial, el objetivo fue estimar el número de partidos que ganó un equipo de la Major League Baseball (MLB) en una temporada, a partir de sus estadísticas y otras variables de esa campaña.
En la segunda parte del proyecto, probarás un modelo de regresión logística y uno de random forest de Scikit-Learn (
sklearn) para predecir qué jugadores serán elegidos para el Salón de la Fama a partir de sus estadísticas y premios de carrera. Cada fila representará a un jugador y datos de su trayectoria. En béisbol hay cuatro grandes subgrupos por posición: infielders, outfielders, pitchers y catchers. Aquí nos centraremos en infielders y outfielders.El National Baseball Hall of Fame and Museum está en Cooperstown (Nueva York) y se inauguró en 1939. Un jugador puede ser elegido para el Salón de la Fama si cumple estos criterios:
Un jugador no elegido puede entrar por el Veterans Committee o por un comité especial designado por el comisionado de la MLB, pero en este proyecto nos centraremos solo en quienes fueron elegidos por votación. En esencia, queremos saber:
«¿Puedes construir un modelo de machine learning que prediga con precisión si un jugador de la MLB será elegido para el Salón de la Fama?».
En la primera parte cargaste los datos desde una base de datos SQLite. Esta vez los cargarás desde archivos CSV. Sean Lahman recopiló estos datos en su sitio web.
Importación de datos
Necesitarás leer los siguientes CSV en DataFrames de
pandas:Master.csvaporta nombres de jugadores, fecha de nacimiento (DOB) e información biográfica;Fielding.csvcontiene estadísticas defensivas;Batting.csvcontiene estadísticas de bateo;AwardsPlayers.csvcon los premios ganados por los jugadores;AllstarFull.csvincluye todas las apariciones en el All-Star Game;HallOfFame.csv;Appearances.csv, con el detalle de posiciones en las que actuó cada jugador.No te preocupes si algún concepto no está claro aún: lo irás viendo a medida que avances.
Primero, descarga los CSV desde aquí. Idealmente, guarda todos los datos en una carpeta específica para este proyecto. Luego abre tu terminal o arranca Jupyter Notebook y ponte manos a la obra.
Carga
pandascomopdy lee cada CSV. Incluirás solo ciertas columnas; puedes revisar las disponibles aquí.Nota: en este tutorial, los datos se cargarán por ti.
Limpieza y preprocesamiento
Ahora que has importado los datos, puedes empezar a limpiarlos y preprocesarlos. Como trabajas con 7 CSV, conviene revisarlos uno a uno: en las siguientes secciones, veremos los DataFrames por separado en este orden:
batting_df,fielding_df,awards_df,allstar_df,hof_df,appearances_dfymaster_df.batting_dfEmpezarás con el DataFrame
batting_df. Imprime las primeras filas conhead():Consejo: como has importado todas las columnas de esta tabla, utiliza el atributo
columnspara verlas y entender con qué trabajas. Usa también cualquier otra función de Pandas para inspeccionar tus datos a fondo.Al inspeccionar los resultados, notarás tres cosas sobre
batting_dfy sus columnas:playerID, identificador del jugador;yearID, el año;teamID, el equipo en el que juega;lgID, la liga.batting_df, hay muchas estadísticas de los jugadores; por ejemplo:Gnúmero de partidos,ABturnos al bate,Rcarreras,Hhits,HRhome runs,RBIcarreras impulsadas,SOstrikeouts.Para conseguirlo, crearás diccionarios para cada jugador dentro de un diccionario maestro. Agregarás las estadísticas de cada jugador a su diccionario y luego convertirás el diccionario principal en un DataFrame.
Para empezar, crea un diccionario para estadísticas del jugador y otro para los años jugados. Agrega las estadísticas de
batting_dfal diccionarioplayer_statsy los años jugados ayears_played:Ahora recorre el diccionario
years_playedy añade el número de años jugados por cada jugador aplayer_stats:fielding_dfSeguimos con
fielding_df. Esta tabla contiene más estadísticas de jugadores y es muy similar abatting_df. Como ya agregaste estadísticas por jugador en el diccionarioplayer_stats, conviene hacer lo mismo con las defielding_df.Concretamente, añadirás nuevas claves a cada diccionario de jugador dentro de
player_statsmientras iteras porfielding_df. Primero crea unafielder_listpara identificar qué jugadores ya tienen claves de fildeo inicializadas, y sumar correctamente:awards_dfVamos ahora con
awards_df: a lo largo de su carrera, los jugadores pueden recibir distintos premios por su rendimiento. Este DataFrame recoge los premios ganados.Una buena forma de inspeccionarlo es imprimir los premios únicos con
unique(). O prueba conhead(),tail(),info(), etc., para conocer mejor los datos.Consejo: del primer bloque sabes que solo leíste tres columnas:
playerID,awardIDyyearID.Como ves, hay bastantes premios en
awards_df… ¿los conoces todos?Como comentamos en la Parte I, conocer bien los datos es clave. Por ejemplo, los mejores premios a incluir son Most Valuable Player, Rookie of the Year, Gold Glove, Silver Slugger y World Series MVP, algo que concluí basándome en mi experiencia siguiendo la MLB.
Podríamos determinar qué premios se correlacionan más con entrar al Salón de la Fama, pero requeriría bastante más código.
Ahora añade un conteo de cada premio en el diccionario de cada jugador dentro de
player_stats: primero filtraawards_dfpara cada premio, creando cinco series. Luego crea una lista llamadaawards_listcon las cinco series. Como hiciste confielding_df, crea listas para identificar qué claves se han añadido enplayer_statspara cada premio. Crea una listalistscon esas cinco listas. Ya puedes iterar porawards_listy contar cuántos de cada premio tiene cada jugador:allstar_dfEl DataFrame
allstar_dfindica qué jugadores aparecieron en All-Star Games. Es un partido de exhibición a mitad de temporada. La MLB tiene dos ligas: Americana y Nacional. Los 25 mejores de cada liga representan a su liga.Como antes, itera por
allstar_dfy añade un conteo de apariciones al diccionario de cada jugador enplayer_stats.hof_dfSeguimos con
hof_df, que recoge qué jugadores, mánagers y directivos fueron nominados o elegidos para el Salón de la Fama y por qué vía.¡Añadir esta información a
player_statspuede ser muy valioso! Para ello, filtrahof_dfpara incluir solo jugadores elegidos al Salón. Luego recorrehof_dfpara marcar enplayer_statsquiénes fueron elegidos y cómo:player_statsYa has reunido datos de
batting_df,fielding_df,awards_df,allstar_dfyhof_dfen el diccionarioplayer_stats. Falta trabajar conappearances_df, pero ahora es buen momento para convertirplayer_statsen DataFrame.Usa
pandasconfrom_dict()para crearstats_df:El DataFrame
stats_dfusa elplayerIDcomo índice. Añade la columnaplayerIDa partir del índice y después haz un inner join conmaster_df.Recuerda: un inner join selecciona filas de ambas tablas solo si hay coincidencia entre columnas. Aquí lo usas porque quieres mantener la información común entre
stats_dfymaster_df.appearances_dfEl último DataFrame que debes revisar antes de crear nuevas variables es
appearances_df. Contiene el número de apariciones de cada jugador en cada posición por año.Imprime las primeras filas con
head():Consejo: usa la consola de IPython para inspeccionar con
info(),tail(), y los atributoscolumnseindex. Verás queappearances_dftiene 21 columnas; algunas son claras (IDs ya vistas) y otras más crípticas comoG_3b,G_dh, etc.Ahora conviene consultar la información del README:
G_allTotal de partidos jugadosGSPartidos como titularG_battingPartidos en los que bateóG_defensePartidos en los que jugó a la defensivaG_pPartidos como pitcherG_cPartidos como catcherG_1bPrimera baseG_2bSegunda baseG_3bTercera baseG_ssShortstop (parador en corto)G_lfJardín izquierdoG_cfJardín centralG_rfJardín derechoG_ofOutfielderG_dhBateador designadoG_phBateador emergenteG_prCorredor emergenteComo ves, hay mucha información en
appearances_dfque debes extraer. También hay que agregar apariciones por posición para obtener totales de carrera.Como recordarás de la primera parte, a lo largo de la historia de la MLB hubo eras con más o menos carreras por partido. Esto afecta mucho a las estadísticas de un jugador. Los votantes del Salón de la Fama lo tienen en cuenta, así que tu modelo también debe hacerlo.
Para recoger esta información, crea un diccionario llamado
pos_dict. Recorreappearances_dfy añade un diccionario por jugador como hiciste conplayer_stats. Agrega las apariciones por posición usando la posición como clave y el número de apariciones como valor. Agrega además el número de partidos jugados en cada era usando la era como clave y el conteo de partidos como valor.Ahora que el diccionario está completo, conviértelo a DataFrame con
from_dict():Imprime las columnas e inspecciona un poco más:
A continuación, calcula el porcentaje de veces que cada jugador actuó en cada posición y en cada era.
Primero, crea una lista
pos_col_lista partir de las columnas depos_dfy elimina la cadena ‘G_all’. Luego crea columnas nuevas enpos_dfpara el porcentaje por posición y por era, iterando porpos_col_listy dividiendo los partidos en cada posición/era entre el total del jugador. Por último, imprime las primeras filas:Como mencionamos, este proyecto se centra en infielders y outfielders, ya que cada posición se juzga de forma distinta para el Salón de la Fama. Infielders y outfielders se evalúan de forma similar; pitchers y catchers se juzgan más por defensa, lo cual queda fuera del alcance de este proyecto.
Como en los primeros años muchos jugadores actuaban en varias posiciones, elimina pitchers y catchers filtrando a quienes jugaron más del 10% de sus partidos en alguna de esas posiciones.
master_dfRecuerda que hiciste un inner join entre
master_dfystats_df(compuesto con datos debatting_df,fielding_df,awards_df,allstar_dfyhof_df) para lograr un dataset más uniforme con estadísticas y datos maestros (nombres, fecha de debut y biografía).Ahora quieres unir el
master_dfuniforme conpos_df; ¡ojo! Aquí debes usar un right join porque solo quieres conservar a los jugadores que estén enpos_df:Ahora filtra
master_dfpara dejar solo jugadores que fueron elegidos por votación o que no entraron en absoluto.Algunos entran por el Veterans Committee o por comités especiales. Suelen ser seleccionados por motivos más allá de las estadísticas —difíciles de cuantificar para tu modelo—, así que es mejor excluirlos.
Las columnas
batsythrowsdeben convertirse a numérico: crea una función que conviertaRen 1 yLen0. Usaapply()para crearbats_Rythrows_R:Las columnas
debutyfinalGameson cadenas. Debes extraer el año. Importadatetime, convierte ambas a objeto datetime conpd.to_datetime(), luego extrae el año condt.strftime('%Y')y conviértelo a numérico conpd.to_numeric().Actualmente,
master_dftiene algunas columnas innecesarias. Usadrop()sobremaster_dfy crea un nuevo DataFrame llamadodf. Imprime las columnas dedfy muestra el recuento de nulos por columna.En la primera parte, trabajabas con estadísticas de equipo y los nulos podían deberse a datos faltantes (p. ej., es raro que un equipo pase una temporada sin un double play).
Con jugadores, es más probable que los nulos sean ceros.
Por eso, crea una lista de columnas numéricas y recórrela para rellenar nulos con 0 usando
fillna(). Luego usadropna()para eliminar nulos restantes.