Comprobando las proyecciones de ESPN Fantasy Football con Python

Es domingo por la mañana, las listas están a punto de bloquearse para la pizarra de la tarde, y usted está decidiendo entre dos WRs de noveno nivel para esa ranura WR3. Es la Semana 3, ambos son selecciones desesperadas del waiver wire que no has tenido tiempo de investigar, y francamente, tienes otros peces que freír hoy. He aquí: la puntuación proyectada de ESPN para un tipo es 7, para el otro tipo es 8. Usted va con el 8 y pensar, «que la puntuación proyectada tiene que significar algo, ¿verdad?»

tl;dr Esa puntuación proyectada significa esencialmente nada, y podemos mostrar, utilizando el (indocumentado) ESPN Fantasy API y un poco de Python.

Un poco de antecedentes

Mike Clay es el hombre detrás de la cortina de las proyecciones de fantasía de ESPN. Él jura que tiene un «largo proceso» que implica «cálculos estadísticos y entradas subjetivas». Quiero decir que a él le pagan y yo escribo entradas en el blog, así que no puedo odiar demasiado lo que sea este misterioso proceso estadístico.

Independientemente de eso, ha habido muchos análisis que comparan las proyecciones de ESPN con otros sitios en todo el espectro, desde intensas entradas en reddit hasta entradas en el blog del NYT.

El consenso parece ser que las proyecciones de ESPN no son muy buenas, basándose en métricas como «precisión» y R-cuadrado que intentan cuantificar el error general con una sola estadística de resumen.

Pero también noto que hay muy poca información sobre cómo comprobar esto por uno mismo. Este sitio de footballanalytics.net enlaza con algunos grandes scripts de R, pero no vi que ninguno agarre las proyecciones de ESPN específicamente (aunque podría estar equivocado).

Explorando …

ESPN está manteniendo una temporada histórica de proyecciones en este momento, así que vamos a agarrar 2018 y ver lo que encontramos.

Haremos uso de la API de ESPN Fantasy que cubro cómo usar aquí.

Vamos, en pocas palabras, a enviar a ESPN la misma petición GET que su sitio web envía a sus propios servidores cuando navegamos a una página de liga histórica. Puedes espiar cómo se forman estas peticiones utilizando las herramientas de Safari para desarrolladores web o un servicio de proxy como Charles o Fiddler.

Antes de escribir el código para obtener todos los datos que necesitamos, vamos a explorar una pequeña parte:

import requestsswid = 'YOUR_SWID'espn_s2 = 'LONG_ESPN_S2_KEY'league_id = 123456season = 2018week = 5url = 'https://fantasy.espn.com/apis/v3/games/ffl/seasons/' + \ str(season) + '/segments/0/leagues/' + str(league_id) + \ '?view=mMatchup&view=mMatchupScore'r = requests.get(url, params={'scoringPeriodId': week}, cookies={"SWID": swid, "espn_s2": espn})d = r.json()

Esto se explica con un poco más de detalle en mi post anterior, pero la idea es que estamos enviando una solicitud a la API de ESPN para una vista específica en una liga, semana y temporada específica que nos dará información completa de matchup/boxscore incluyendo puntos proyectados. Las cookies sólo son necesarias para las ligas privadas, y de nuevo, lo cubro aquí.

Si navegas por la estructura JSON, encontrarás que cada equipo de fantasía tiene un roster de sus jugadores, y cada jugador tiene un listado de su stats.

(De nuevo, una buena forma de navegar por esta estructura es con las herramientas de Desarrollador Web de Safari: ve a una página de interés en la casa club de tu liga de fantasía, abre las herramientas de Desarrollador Web, ve a Recursos, y luego busca en XHRs un objeto con el ID de tu liga. Este será el texto en bruto de la JSON … cambiar «Respuesta» a «JSON» en el área de cabecera poco para una interfaz de estilo explorador más fácil de usar.)

Grabbing todas las proyecciones de 2018

Es un poco escondido, pero dentro de esta subestructura es el proyectado y puntos de fantasía real para cada jugador en cada lista.

Me he dado cuenta de que la lista stats de un jugador concreto tiene 5-6 entradas, una de las cuales es siempre la puntuación proyectada y otra la real. La puntuación proyectada se identifica con statSourceId=1, la real con statSourceId=0.

Usemos esta observación para construir un conjunto de bucles para enviar peticiones GET para cada semana, y luego extraer cada puntuación proyectada/real para cada jugador en cada lista.

import requestsimport pandas as pdleague_id = 123456season = 2018slotcodes = { 0 : 'QB', 2 : 'RB', 4 : 'WR', 6 : 'TE', 16: 'Def', 17: 'K', 20: 'Bench', 21: 'IR', 23: 'Flex'}url = 'https://fantasy.espn.com/apis/v3/games/ffl/seasons/' + \ str(season) + '/segments/0/leagues/' + str(league_id) + \ '?view=mMatchup&view=mMatchupScore'data = print('Week ', end='')for week in range(1, 17): print(week, end=' ') r = requests.get(url, params={'scoringPeriodId': week}, cookies={"SWID": swid, "espn_s2": espn}) d = r.json() for tm in d: tmid = tm for p in tm: name = p slot = p pos = slotcodes # injured status (need try/exc bc of D/ST) inj = 'NA' try: inj = p except: pass # projected/actual points proj, act = None, None for stat in p: if stat != week: continue if stat == 0: act = stat elif stat == 1: proj = stat data.append()print('\nComplete.')data = pd.DataFrame(data, columns=)
Week 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 Complete.

Obtenemos algo así:

data.head()
 Week Team Player Slot Pos Status Proj Actual0 1 1 Leonard Fournette 2 RB QUESTIONABLE 13.891825 5.01 1 1 Christian McCaffrey 2 RB ACTIVE 11.067053 7.02 1 1 Derrick Henry 20 Bench ACTIVE 10.271163 2.03 1 1 Josh Gordon 4 WR OUT 6.153141 7.04 1 1 Philip Rivers 0 QB QUESTIONABLE 26.212294 42.0

Sí, sí, esto es sólo jugadores en listas, así que no estamos capturando ningún agente libre … pero al menos debería darnos una idea de la precisión de las proyecciones de ESPN, por ahora.

Vamos a trazar «Proj» contra «Actual» para algunas posiciones diferentes y cruzar los dedos …

fig, axs = plt.subplots(1,3, sharey=True, figsize=(12, 4))for i, pos in enumerate(): (data .query('Pos == @pos') .pipe((axs.scatter, 'data'), x='Proj', y='Actual', s=50, c='b', alpha=0.5) ) axs.plot(, , 'k--') axs.set(xlim=, ylim=, xlabel='Projected', title=pos)axs.set_ylabel('Actual')plt.tight_layout()plt.show()

Um, no es genial. Podríamos hacer algunas pruebas estadísticas aquí, pero para mi ojo inexperto parece que esos puntos proyectados bien podrían salir de una distribución uniforme.

¿Tal vez sea mejor en ciertas semanas? ¿Tal vez más tarde en la temporada? Vamos a trazar el error general, por semana, por posición. Este código es un poco complicado 🙁 pero estoy preparado para vivir con él:

fig, axs = plt.subplots(1,3, sharey=True, figsize=(13,4))data = data - datadata = pd.cut(data, bins=4, labels=)cols = sns.color_palette('Blues')# dummy plots for a legendfor k,cat in enumerate(): axs.plot(,, c=cols, label='Wk ' + cat)axs.legend()for i, pos in enumerate(): for cat in range(4): t = data.query('Pos == @pos & Cat == @cat') sns.kdeplot(t, color=cols, ax=axs, legend=False) axs.set(xlabel='Actual - Proj', title=pos) plt.show()

Tal vez hay una tendencia a sobreproyectar más tarde en la temporada, pero en general yo diría que nada aquí tampoco. (Nótese que al hacer esto perdimos algo de información sobre si el error es mayor o menor para las proyecciones altas frente a las bajas.)

A continuación me gustaría mirar las series temporales de puntos de los jugadores. La sabiduría de sentido común es que la mejor manera de adivinar lo que un jugador va a anotar la próxima semana es sólo eyeball sus últimos 4-5 semanas … ¿cómo fiable de una estrategia es esto?

En este post, trato de comprobar las proyecciones de ESPN en un nivel de la lista – tal vez las proyecciones individuales no son impresionantes, pero en conjunto se comienzan mágicamente a ayudar? (Spoiler: en realidad no.)

Escrito el 5 de agosto de 2019 por Steven Morse

Deja una respuesta

Tu dirección de correo electrónico no será publicada.