Verificando as Projecções de Futebol ESPN Fantasy com Python

É domingo de manhã, as listas estão prestes a fechar para a tabela do início da tarde, e você está a decidir entre dois WRs de nono nível para aquele slot WR3. É a Semana 3, ambos são desespero de fios de desespero de renúncia escolhas que você não teve tempo de pesquisar, e francamente, você tem outros peixes para fritar hoje. Veja: a pontuação projetada da ESPN para um cara é 7, para o outro é 8. Você vai com o 8 e pensa, “essa pontuação projetada tem que significar algo, certo?”

tl;dr Essa pontuação projetada não significa essencialmente nada, e nós podemos mostrá-la, usando a (indocumentada) ESPN Fantasy API e um pouco de Python.

Um pouco de fundo

Mike Clay é o homem por trás da cortina das projeções de fantasia da ESPN. Ele jura que tem um “processo longo” que envolve “cálculos estatísticos e entradas subjetivas”. Quero dizer, ele está sendo pago e eu estou escrevendo posts em blogs, então eu não posso odiar muito este misterioso processo estatístico.

Independentemente, tem havido muitas análises comparando as projeções da ESPN com outros sites em todo o espectro, desde posts vermelhos intensos a posts em blogs do NYT.

O consenso parece ser, as projeções da ESPN não são muito boas, baseadas em métricas como “precisão” e R-quadrado, que tentam quantificar o erro geral com um único resumo estatístico.

Mas também notei que há muito pouca informação sobre como verificar isso por si próprio. Este site do footballanalytics.net liga a alguns grandes scripts R, mas não vi que nenhuma projecção ESPN fosse especificamente agarrada (embora eu possa estar enganado).

Explorar …

ESPN está a manter uma época histórica de projecções neste momento, por isso vamos agarrar 2018 e ver o que encontramos.

Vamos fazer uso da ESPN Fantasy API que eu cubro como usar aqui.

Vamos, em poucas palavras, enviar à ESPN o mesmo pedido de GET que o seu site envia os seus próprios servidores quando navegamos para uma página histórica do campeonato. Você pode escutar como essas solicitações são formadas usando as ferramentas de desenvolvimento web do Safari ou um serviço de proxy como Charles ou Fiddler.

Antes de escrevermos o código para pegarmos todos os dados que precisamos, vamos explorar um pedacinho disso:

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()

Isso é explicado com um pouco mais de detalhe no meu post anterior, mas a idéia é que estamos enviando um pedido para a API da ESPN para uma visão específica sobre uma liga, semana e temporada específicas que nos dará informações completas sobre matchup/boxscore incluindo pontos projetados. Os cookies só são necessários para ligas privadas, e novamente, eu cubro aqui.

Se você navegar pela estrutura JSON, você verá que cada equipa de fantasia tem um roster dos seus jogadores, e cada jogador tem uma lista dos seus stats.

(Novamente, uma boa maneira de navegar por esta estrutura é com as ferramentas Web Developer do Safari: vá para uma página de interesse no clube da sua liga de fantasia, abra as ferramentas Web Developer, vá para Recursos, depois procure em XHRs por um objeto com o seu ID da liga. Este será o texto bruto do JSON … mude “Response” para “JSON” na pequena área de cabeçalho para uma interface mais amigável ao estilo explorador.)

Grabbing all 2018 Projections

It’s a little hidden, but within this sub-structure is the projected and real fantasy points for each player on each roster.

Noto que a lista stats para um determinado jogador tem 5-6 entradas, uma das quais é sempre a pontuação projectada e a outra é a real. A pontuação projectada é identificada por statSourceId=1, a real com statSourceId=0.

Vamos usar esta observação para construir um conjunto de loops para enviar pedidos de GET para cada semana, depois extrair cada pontuação projectada/real para cada jogador em 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.

>

Encontramos algo como isto:

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

>

Sim, sim, isto é apenas jogadores em listas, por isso não estamos a capturar nenhum agente livre… mas deve pelo menos dar-nos uma ideia da precisão das projecções da ESPN, por agora….

Vamos plotar “Proj” contra “Actual” para algumas posições diferentes e cruzar os 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, não muito bom. Poderíamos fazer alguns testes estatísticos aqui, mas para o meu olho destreinado parece que aqueles pontos projetados podem estar saindo de uma distribuição uniforme.

Talvez seja melhor em certas semanas? Mais tarde na temporada, talvez? Vamos traçar o erro geral, por semana, por posição. Este código fica um pouco hacky 🙁 mas estou preparado para viver com ele:

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()

Talvez haja uma tendência para o excesso de projeção mais tarde na estação, mas em geral eu também não diria nada aqui. (Note que ao fazer isto perdemos alguma informação sobre se o erro é mais ou menos para projecções altas vs. baixas.)

Quero ver a série temporal de pontos dos jogadores a seguir. A sabedoria do senso comum é que a melhor maneira de adivinhar o que um jogador vai marcar na próxima semana é apenas olhar para as suas últimas 4-5 semanas … quão confiável é a estratégia?

Neste post, eu tento verificar as projeções da ESPN em um nível de escala – talvez as projeções individuais não sejam impressionantes, mas no conjunto, elas magicamente começam a ajudar? (Spoiler: not really.)

Escrito em 5 de Agosto de 2019 por Steven Morse

Deixe uma resposta

O seu endereço de email não será publicado.