Introducción
Desde hace tiempo he estado programando simuladores de beisbol para resolver diferentes problemas que marca la literatura sabermetrica. Acá te explico uno que hice recientemente para optimizar lineups de un equipo.
Jugadores, const y paquetes
Para que esta herramienta funcione necesitaras descargar tres ficheros:
- jugadores.csv: Contiene las métricas de los jugadores que conforman el lineup.
- const.py: Encapsula la lógica de movimiento de bases y anotación de carreras por cada evento.
- simulador.py: Procesa el fichero jugadores.csv y simula partidos y apariciones al plato con la ayuda const.py.
Jugador | outs | ubb | hbp | x1b | x2b | x3b | hr | |
---|---|---|---|---|---|---|---|---|
Jorge Mateo | 88 | 18 | 0 | 22 | 3 | 1 | 0 | |
Dairon Blanco | 93 | 11 | 1 | 35 | 3 | 3 | 0 | |
Jeimer Candelario | 53 | 12 | 1 | 9 | 9 | 2 | 1 | |
Jonah Heim | 45 | 6 | 0 | 15 | 6 | 1 | 0 | |
Peter O'Brien | 95 | 33 | 4 | 21 | 9 | 2 | 9 | |
Yamaico Navarro | 72 | 21 | 1 | 25 | 5 | 0 | 5 | |
Ruben Sosa | 67 | 23 | 1 | 29 | 4 | 0 | 0 | |
Anderson Feliz | 40 | 21 | 1 | 15 | 3 | 0 | 1 | |
Jordany Valdespin | 77 | 18 | 3 | 38 | 6 | 0 | 5 |
Generar Probabilidades
El archivo simulador.py utiliza la función generarProbs para generar una tabla de probabilidades para cada jugador que se encuentre en el archivo jugadores.csv:
def generarProbs( archivo ): | |
probs = {} | |
df = pd.read_csv( archivo ) | |
for idx, row in df.iterrows(): | |
probs[row[0]] = list(row[1:] / sum(row[1:])) | |
return probs |
Simulador de apariciones al plato
La función simularAparicion toma como argumento el nombre de un jugador, genera un numero aleatorio y basado en su tabla de probabilidades calcula el resultado de una aparición al plato.
def simularAparicion( jugador ): | |
probs_jugador = probs[jugador] | |
evento = np.random.choice( const.eventos, 1, p = probs_jugador ) | |
return evento[0] |
Simulador de partidos
La función simularPartido simplemente toma un lineup como argumento y simula 9 innings de bateo. Esta función manda a llamar al simulador de apariciones al plato y utiliza la lógica de transición de bases del archivo const.py; Da como resultado el numero de carreras que anotó la alineación y el número de apariciones al plato que ocurrieron en el partido.
def simularPartido( lineup ): | |
carreras_partido = 0 | |
aparicion = 0 | |
for inning in range( 0, 9 ): | |
outs = 0 | |
estado = '---' | |
while outs < 3: | |
bateador_en_turno = lineup[aparicion % 9] | |
evento = simularAparicion( bateador_en_turno ) | |
if evento == 'out': | |
outs += 1 | |
else: | |
estado = const.transicion[evento][estado] | |
carreras_partido += const.carreras[evento][estado] | |
aparicion += 1 | |
return [ carreras_partido, aparicion ] |
Correr Simulaciones
La función correrSimulaciones simplemente se encarga de simular los N partidos que desees y de poner los N resultados en un data frame:
def correrSimulaciones( lineup ): | |
resultado_simulacion = pd.DataFrame( columns = [ 'carreras', 'apariciones', 'carreras_apariciones'] ) | |
for p in range( 0, const.num_simulaciones ): | |
resultados_partido = simularPartido( lineup ) | |
resultado_simulacion = resultado_simulacion.append( { 'carreras' : resultados_partido[0] | |
, 'apariciones': resultados_partido[1] | |
, 'carreras_apariciones': resultados_partido[0] * 100.0 / resultados_partido[1] | |
} | |
, ignore_index = True | |
) | |
return resultado_simulacion |
Para correr el código, simplemente ejecuta estas lineas. En el próximo post de esta serie veremos algunos de los resultados del simulador.
probs = generarProbs('jugadores.csv') | |
resultados = correrSimulaciones( [ "Ruben Sosa","Jordany Valdespin","Yamaico Navarro","Peter O'Brien","Jeimer Candelario","Jonah Heim","Jorge Mateo","Anderson Feliz","Dairon Blanco" ]) |