Verificarea proiecțiilor ESPN Fantasy Football Projections cu Python

Este duminică dimineața, listele sunt pe cale să se blocheze pentru lista de după-amiaza devreme, și vă decideți între două WR-uri de al nouălea nivel pentru acel slot WR3. Este săptămâna 3, amândoi sunt ambele alegeri de disperare pe sârmă de renunțare pe care nu ați avut timp să le cercetați și, sincer, aveți alți pești de prăjit astăzi. Iată: scorul proiectat de ESPN pentru un tip este 7, pentru celălalt tip este 8. Te duci cu 8 și te gândești, „acel scor proiectat trebuie să însemne ceva, nu-i așa?”

tl;dr Acel scor proiectat nu înseamnă în esență nimic, iar noi putem arăta acest lucru, folosind (nedocumentat) ESPN Fantasy API și un pic de Python.

Un pic de fundal

Mike Clay este omul din spatele cortinei proiecțiilor de fantezie ale ESPN. El jură că are un „proces îndelungat” care implică „calcule statistice și intrări subiective”. Adică el este plătit, iar eu scriu postări pe blog, așa că nu pot să urăsc prea mult orice ar fi acest proces statistic misterios.

Indiferent, au existat multe analize care au comparat proiecțiile ESPN cu alte site-uri de pe tot spectrul, de la postări intense pe reddit până la postări pe blogul NYT.

Consensul pare să fie, proiecțiile ESPN nu sunt foarte bune, pe baza unor măsurători precum „acuratețea” și R pătrat care încearcă să cuantifice eroarea generală cu o singură statistică rezumativă.

Dar am observat, de asemenea, că există foarte puține informații despre cum să verifice acest lucru pentru sine. Acest site de la footballanalytics.net trimite la câteva scripturi R grozave, dar nu am văzut că niciunul nu apucă proiecțiile ESPN în mod specific (deși aș putea să mă înșel).

Explorând …

ESPN menține un sezon istoric de proiecții în acest moment, așa că haideți să apucăm 2018 și să vedem ce găsim.

Ne vom folosi de API-ul ESPN Fantasy, pe care am acoperit modul de utilizare aici.

În câteva cuvinte, vom trimite către ESPN aceeași cerere GET pe care site-ul său o trimite propriilor servere atunci când navigăm către o pagină istorică a ligii. Puteți trage cu urechea la modul în care sunt formate aceste cereri folosind instrumentele Web Developer din Safari sau un serviciu proxy precum Charles sau Fiddler.

Anainte de a scrie codul pentru a prelua toate datele de care avem nevoie, haideți să explorăm o mică parte din el:

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

Acest lucru este explicat puțin mai detaliat în postarea mea anterioară, dar ideea este că trimitem o cerere către API-ul ESPN pentru o vizualizare specifică pe o anumită ligă, săptămână și sezon, care ne va oferi informații complete despre meciuri/boxscore, inclusiv puncte proiectate. Cookie-urile sunt necesare doar pentru ligile private și, din nou, le acopăr aici.

Dacă navigați prin structura JSON, veți descoperi că fiecare echipă fantastică are un roster al jucătorilor săi, iar fiecare jucător are o listă cu stats.

(Din nou, o modalitate plăcută de a naviga prin această structură este cu instrumentele Web Developer din Safari: mergeți la o pagină de interes din clubul ligii dumneavoastră fantastice, deschideți instrumentele Web Developer, mergeți la Resurse, apoi căutați sub XHRs un obiect cu ID-ul ligii dumneavoastră. Acesta va fi textul brut al JSON … schimbați „Response” în „JSON” în zona micului antet pentru o interfață mai prietenoasă în stil explorator.)

Grabbing all 2018 Projections

Este puțin ascuns, dar în cadrul acestei sub-structuri se află punctele de fantezie proiectate și reale pentru fiecare jucător din fiecare listă.

Am observat că lista stats pentru un anumit jucător are 5-6 intrări, dintre care una este întotdeauna scorul proiectat și alta este cel real. Scorul proiectat este identificat cu statSourceId=1, iar cel real cu statSourceId=0.

Să folosim această observație pentru a construi un set de bucle pentru a trimite cereri GET pentru fiecare săptămână, apoi să extragem fiecare scor proiectat/real pentru fiecare jucător din fiecare listă.

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.

Obținem ceva de genul acesta:

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

Da, da, este vorba doar de jucătorii din liste, așa că nu capturăm niciun agent liber … dar ar trebui să ne dea cel puțin o idee despre acuratețea proiecțiilor ESPN, deocamdată.

Să comparăm „Proj” cu „Actual” pentru câteva poziții diferite și să ne încrucișăm degetele …

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, nu e grozav. Am putea face niște teste statistice aici, dar pentru ochiul meu neexperimentat se pare că acele puncte proiectate ar putea la fel de bine să iasă dintr-o distribuție uniformă.

Poate că e mai bine în anumite săptămâni? Mai târziu în sezon, poate? Să trasăm eroarea generală, pe săptămână, pe poziție. Acest cod devine un pic hacky 🙁 dar sunt pregătit să trăiesc cu el:

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

Poate că există o tendință de supraproiectare mai târziu în sezon, dar în general nu aș spune nimic nici aici. (Rețineți că făcând acest lucru am pierdut unele informații cu privire la faptul dacă eroarea este mai mare sau mai mică pentru proiecțiile mari față de cele mici.)

Am dori să analizez în continuare seriile temporale ale punctelor jucătorilor. Înțelepciunea de bun simț este că cel mai bun mod de a ghici ce scor va avea un jucător săptămâna viitoare este să te uiți doar la ultimele 4-5 săptămâni … cât de fiabilă este această strategie?

În acest post, încerc să verific proiecțiile ESPN la nivel de listă – poate că proiecțiile individuale nu sunt impresionante, dar în ansamblu încep să ajute în mod magic? (Spoiler: nu chiar.)

Scris pe 5 august 2019 de Steven Morse

Lasă un răspuns

Adresa ta de email nu va fi publicată.