R i Javascript u sjajnoj (Shiny) NBA analizi – Deo II

Kroz prethodni članak prikazane su osnove programskog paketa Shiny, koji omogućava kreiranje reaktivnog korisničkog okruženja u programskom jeziku R. Takođe, istaknut je i značaj sportskih statistika u okviru savremene nauke o podacima, uz opis konkretnih analitičkih ciljeva postavljenih pred Shiny aplikaciju kreiranu za ovu priliku. Ovaj članak će se fokusirati na integraciju Shiny aplikacije u širi kontekst web tehnologija (JS i CSS), kao i na R/Shiny funkcije koje omogućavaju reaktivno programiranje i analizu glavnih komponenti (PCA). Veći deo članka biće posvećen tumačenju rezultata PCA analize u svetlu prethodno postavljenih ciljeva.

Dodatni Javascript i CSS

Jedna od osnovnih ideja naše aplikacije je i kombinovanje nekih Javascript biblioteka sa Shiny funckijama, kao i dodatno podešavanje izgleda korisničkog okruženja. Pored odličnih biblioteka za grafički prikaz u R-u, Javascript nudi animirane i interaktivne grafičke komponente koje na efektniji način mogu istaći rezultate izvršenih analiza. U ovoj aplikaciji ćemo koristiti Chart.js. Bilo koja JS datoteka može se uključiti u Shiny aplikaciju korišćenjem tags funkcija, slično kao i kod uobičajenog HTML-a, u okviru taga head (server.R):

tags$head(
    tags$link(rel = "stylesheet", type = "text/css", href = "bootstrap.css"),
    tags$script(src = "https://code.jquery.com/jquery-3.2.1.js"),
    tags$script(src = "chartUI.js")
  ),

Putanje do .css i .js datoteka su deo Shiny konvencije prema kojoj se sve „web“ datoteke smeštaju u direktorijum www.

Promena izgleda aplikacije postiže se kroz .css fajlove, a posebno kroz fajl bootstrap.css koji može, ali ne mora biti prisutan. Najbolji pristup kontroli izgleda celokupne aplikacije postiže korišćenjem gotovih bootrsrap stilova, koje nudi stranica Bootswatch. Dovoljno je odabrati temu, preuzeti bootstrap.css i sačuvati ga u www direktorijum.

Javascript u www direktorijumu (datoteke chartUI.js, scatterChartConfig.js i radarChartConfig.js) sadrže funkcije zadužene za iscrtavanje grafika ali i za prijem R/Shiny podataka.

R funkcije, reaktivno programiranje i Shiny-JS komunikacija

Prilikom pokretanja aplikacije, automatski se učitava .css datoteka sa svim podacima koji će se koristiti prilikom analize. Slično tome, odmah se izvršava i PCA analiza, dok se korisnicima prikazuju rezultati u okviru dva grafička prikaza, grafika raspršivanja i grafika doprinosa glavnih komponenti.

Dodatne R funkcije, posebno one koje sadrže analitičku logiku, mogu biti u okviru nezavisnih R datoteka (engine.R) koje se mogu uključiti u server.R korišćenjem funkcije source(#putanja do datoteke).

Korisnicima su omogućena dva tipa kontrole prikazanih rezultata, dva padajuća menija gde se mogu izabrati glavne komponente (PCA ose) prikazane na graficima raspršivanja i doprinosa, kao i tip statistika, tim ili protivnik. Kako bi se prikaz automatski ažurirao u skladu sa akcijama korisnika neophodno je iskoristi reaktivne komponente i funkcije koje Shiny nudi. Reaktivne komponente i funkcije mogu se podeliti na dve grupe: 1. Implicitne – u koje spadaju sve promenljive koje se automatski generišu za svaki aktivni element korisničkog okruženja, kao što su dugmad, padajući meniji ili tekstualna polja. Shiny koristi jednostavnu konvenciju za ove elemenete tako što se za globalnu promenljivu input vezuju nazivi elemenata koje programer odredi. Tako npr. upis u tekstualno polje koje je definisano kao textInput("naslov", ...) rezultuje reakcijom promenljive input$naslov, čija vrednost postaje vrednost upisana od strane korisnika. 2. Eksplicitne – koje se ručno deklarišu u kodu (funkcija reactive({})), i predstavljaju okvir (eng. wrapper) standardnih R funkcija, tako da se one izvršavaju bez eksplicitnog poziva, ukoliko se promeni bilo koja reaktivna promenljiva koja se u okviru njih koristi. Ove funkcije se moraju eksplicitno i „posmatrati“ korišćenjem funkcija tipa observe() ili observeEvent().

Tako se kombinacijom reaktivnih promenljivih može konstruisati stablo reaktivnih funkcija koje se kaskadno pozivaju ukoliko se neka od promenljivih izmeni. U slučaju naše aplikacije, osnovni reaktivni deo je:

pcaList <- reactive({
   performPCAwrap(
    getInternalData(),
    data.frame(fpc = extractNum(input$first_pc),
    spc = extractNum(input$second_pc)), input$stats)
 })

 observeEvent(pcaList(), {
     session$sendCustomMessage(type="jsondata", toJSON(pcaList()$scores))
     session$sendCustomMessage(type="jsoninfo", toJSON(pcaList()$pcainfo))
   }
 )

Reaktivna funkcija pcaList izvršava se prilikom pokretanja aplikacije, kao i prilikom promena implicitnih promenljivih input$first_pc, input$second_pc i input$stats koje potiču od padajućih menija i kontrolnih (radio) dugmadi. performPCAwrap je funkcija koja pokreće PCA analizu i vraća rezultate. Tri parametra ove funkcije su ulazni podaci koji se dobijaju funkcijom getInternalData(), R data.frame struktura koja nosi informacije o izabranim glavnim komponentama i promenljiva tipa statistike. pcaList je pod „prismotrom“ kroz observeEvent tako da svaka promena internih parametara, dovodi do pokretanja session funkcija. Konačno, ove funkcije šalju JSON rezultata prema Javascript funkcijama u chartUI.js.

Svi session podaci se u okviru Javascripta mogu pročitati korišćenjem Shiny namespace promenljive kroz metodu addCustomMessageHandler, čime podaci postaju dostupni za dalju obradu i prikaz.

Shiny.addCustomMessageHandler("jsondata",
    function(jsondata) {
        // dalja obrada podataka i slanje prema grafiku
    }
)
Analiza glavni komponenti

Datoteka engine.R sadrži funkcije za učitavanje podataka (getInternalData()) i za izvođenje same analize (performPCA()). U programskom jeziku R postoji veći broj funkcija koje se mogu iskoristiti za izvođenje PCA analize, ali mi ćemo koristiti funkciju prcomp() koja je deo podrazumevanog R paketa stats. Najvažniji deo koda se nalazi u funkcji performPCA:

performPCA <- function(ogd, pcaChoice) {
  pcaTeam <- prcomp(ogd, scale. = TRUE)
  teamOrder <- match(team, rownames(pcaTeam$x))

  pcaScores <- pcaTeam$x[teamOrder,]
  pcaLoadings <- pcaTeam$rotation
  pcaVarianceExplained <- round(summary(pcaTeam)$importance[2,], 2)
  pcaCorrelations <- as.data.frame(cor(ogd[teamOrder,], pcaScores))
  ...

Funkcija prcomp() prima podatke koji su po tipu R data.frame, a od parametara ima scale. U slučaju naše analize, skaliranje ulaznih podataka pre analize je neophodno kako bi se anulirao uticaj različitih tipova promenljivih (različita varijansa i apsolutne vrednosti) na ukupnu varijabilnost podataka. Nakon izvršene analize promenljiva pcaTeam sadrži imenovanu listu rezultata, koji se nakon toga razdvajaju u različite promenljive, kako bi se lakše pripremili za konverziju u JSON i slanje do grafika u Javascript funkcijama. pcaScores predstavlja data.frame sa 17 kolona i 30 redova (za svaki tim), sa ocenama svakog tima na jednoj od 17 glavnih komponenti. Ovi podaci se iscrtavaju na grafiku raspršivanja. Uticaj, odnosno procenat obuhvaćene varijabilnosti podataka za svaku glavnu komponentu sadrži promenljiva pcaVarianceExplained. pcaCorrelations sadrži koeficijente korelacije između originalnih podataka i podataka predstavljenih glavnim komponentama (pcaScores), u okviru data.frame strukture sa 17 redova i 17 kolona. Vrednosti ovih koeficijenata kreću se između -1 i 1, gde 0 označava potpun nedostatak uticaja promenljive na ocenu tima na glavnoj komponenti. Veće vrednosti (bilo negativne ili pozitivne) tumače se kao određen stepen uticaja, a promenljive koje imaju ocene bliske 1 ili -1 predstavljaju one najuticajnije.

Istraživačka analiza performansi NBA timova

Nakon pokretanja aplikacije, PCA analiza se automatski izvršava a prikazani rezultati usklađuju sa rezultatima. Na grafiku raspršivanja prikazan je raspored timova u prostoru prve i druge glavne komponente (PC1, PC2). Položaj svakog tima predstavlja njegovu „ocenu“ na glavnim komponetama. Dijagram doprinosa prikazuje uticaj svake od promenljivih na položaj tima u prostoru izabranih glavnih komponenti (koeficijenti korelacije).

Stream Shot
Izgled aplikacije. a) Dijagram raspršivanja, b) Dijagram doprinosa.

Pregledom dijagrama raspršivanja za prvu i drugu glavnu komponentu uočljiv je trend grupisanja timova u sredini koordinatnog sistema (prostor glavnih komponenti), uz pojavu timova koji značajno odstupaju od većine. Tako su dva ekstremna tima duž ose prve glavne komponente (PC1) Dallas Mavericks (DAL) u pozitivnom, i Phoenix Suns (PHX) u negativnom pravcu. Sa dijagrama doprinosa može se pročitati (prelaskom kursora miša preko tačaka obojenih polja) da naveću negativnu korelaciju imaju promeljnive X2PA, FGA, FG, TRB i ORB, a pozitivnu X3P i X3PA. Pozitivne korelacije su inače niske (oko 0.3) pa se može zaključiti da PC1 razdvaja timove koji su dobri u skokovima (TRB i ORB) i koji daju veliki broj poena ali koji puno i pokušavaju (X2P, X2PA, FGA, FG) od onih koji daju malo poena i imaju malo skokova. Pored ekipe Suns-a, inače jedne od najlošijih ekipa sezone 2016/2017, slično su pozicionirane i ekipe Oklahoma City Thunder (OKC), Denver Nuggets (DEN) i Golden State Warriors (GSW) koje su znatno bolje i uključuju i šampionski tim (GSW). Igrači Mavericks-a imaju najniži prosek skokova po utakmici, što uz najniži broj ostvarenih poena (FG), kao i veliki broj upućenih šuteva za 3 poena (X3PA) jasno govori o njihovom opredeljenju u napadu i problemima u odbrani. PC1 ističe izuzetnu važnost broja skokova za uspeh tima. Takođe, govori i o tome da je, ukoliko je broj ostvarenih poena nizak (FGA, FG), veći broj šuteva za tri poena loša timska taktika.

PC1PC2 scatter
Raspored timova u prostoru prve dve glavne komponente. a) Dallas Maverics, b) Detroit Pistons, c) Phoenix Suns, d) Golden State Warriors, e) Houston Rockets, f) Cleveland Cavaliers, g) Boston Celtics.
PC1PC2 radar
Najuticajnije promenljive na prvoj i drugoj glavnoj komponenti. a) Prosečan broj ostvarenih šuteva za 2 poena i prosečan broj pokušaja, b) Prosečan broj ostvarenih šuteva i broj pokušaja, c) Prosečan broj svih skokova i skokova u napadu, d) Prosečan broj asistencija, e) Prosečan broj poena, f) Prosečan broj ostvarenih slobodnih bacana i broj pokušaja, g) Prosečan broj ostvarenih šuteva za tri poena i broj pokušaja.

Druga glavna komponenta (PC2) jasnije razdvaja „napadačke“ i najbolje plasirane timove, koji postižu veliki broj poena (PTS), kao što su Houston Rockets (HOU), Cleveland Cavaliers (CLE),Boston Celtics (BOS) i Golden State Warriors (GSW) od većine ostalih. Igrači navedenih timova, npr. James Harden, Stephen Curry i Isaiah Thomas su poznati po velikom broju poena, ali i velikom broju uspešnih pokušaja, posebno za tri poena. Ovaj obrzac ukazuje na povoljan uticaj šuta za tri poena, ali samo ukoliko je i ukupan broj poena i pokušaja veliki. Suprotno, Detroit Pistons (DET), Dalas Mavericks (DAL), Chicago Bulls (CHI), Minesota Timberwolves (MIN) i Orlando Magic (ORL) su, ili zbog povreda ključnih igrača ili zbog nedostatka pravog lidera, davali prosečno najmanji broj poena po utakmici. Visoka korelacija promenljivih koje označavaju uspeh u izvođenju slobodnih bacanja (FT i FTA) ukazuje na činjenicu da ključni igrači pozitivno pozicioniranih, najuspešnijih timova, veoma često izvode slobodna bacanja, što ukazuje na njihov napadački stil i lidersku ulogu, a pre svih ističe stil igre Jamesa Hardena, koji je imao ubedljivo najveći prosek poena tokom sezone. Ipak, na osnovu položaja timova duž ose druge glavne komponente nije lako zaključiti koji je tim osvojio prvenstvo, iako se grupa najuspešnijih relativno lako uočava. Ekipa Warriors-a se izdvaja od ostalih ekipa po broju asistencija (AST), koji je naviši u ligi. Doprinos ove promenljive duž osa glavnih komponenti je neznatno manji od do sada istaknutih, ali utiče i na pozitivan položaj duž druge i na negativan položaj duž prve ose. Uz prosečno veći broj skokova po utakmici od ostalih timova iz grupe najboljih jasni su preduslovi za eventualnog pobednika NBA lige. Prve dve komponente zajedno opisuju 52% ukupne varijabilnosti što se može protumačiti kao pokazatelj „skrivenih“ uticaja i dodatnih informacija. Promenom prikazane glavne komponente (PC3 umesto PC2) uočavamo da ova komponenta opisuje jos 14% varijabilnosti. Položajem timova duž ove ose dominira broj napravljenjih prekršaja (PF), broj izgubljenih lopti (TOV), ali i broj ostvarenih slobodnih bacanja. Najpozitivniji položaj ima ekipa Suns-a, a suprotan ekipa Pistons-a. Suns-i su se našli u grupi dobrih timova duž prve ose, ali iz njihovog položaja na trećoj osi, vidljiv je i razlog njihovog lošeg plasmana protekle sezone. Oni prosečno prave najveći broj faulova, a imaju i najviše izgubljenih lopti. Opet, najveći broj njihovih poena je iz slobodnih bacanja, što ukazuje na njihovu malu efikasnost u šutu iz igre. Položaj Warriors-a i Cavaliers-a duž ove ose dalje može da ukaže na razloge njihovog uspeha, koji leže u činjenici da imaju malo izgubljenih lopti, da prave mali broj prekršaja i da igraju efikasnu i čvrstu odbranu u reketu, slično ekipama Pistons-a, Charlotte Hornets-a (CHA) i posebno New Orleans Pelicans-a (NOP).

PC1PC3 scatter
Raspored timova u prostoru prve i treće glavne komponente. a) Golden State Warriors, b) Detroit Pistons, c) Phoenix Suns, d) Charlotte Hornets, e) New Orleans Pelicans.
PC1PC3 radar
Najuticajnije promenljive na trećoj glavnoj komponenti. a) Prosečan broj prekršaja, b) Prosečan broj izgubljenih lopti, c) Prosečan broj ukradenih lopti.

Tumačenjem rezultata PCA analize možemo da stvorimo sliku performansi NBA timova i da „izvučemo“ određene aspekte igre koji doprinose efikasnoj igri i uspehu tokom NBA sezone. Timovi, se u prostoru osa glavnih komponenti grupišu prema plasmanu, donekle i očekivano, ali se na osnovu prikazanih korelacija mogu odrediti pozitivni i negativni uticaji u okviru timskih performansi. Od 18 promenljivih, koje je teško istovremeno pratiti, videli smo obrazac variaranja na osnovu 3 glavne komponente, koji nam je ukazao na najvažnije odlike igre nekog NBA tima. Pre svega, dobar tim mora se odlikovati dobrom odbranom, velikim brojem skokova i malim brojem prekršaja, odakle se, u fazi napada dalje ističe veliki broj poena uz veliki broj pokušaja. Broj postignutih poena, posebno od strane lidera, značajno utiče na ukupne performanse tima. Timovi koji imaju dominantne ključne igrače, koji puno pokušavaju, šutiraju za 3 poena i idu u prodor, su automatski u boljem položaju za osvajanje trofeja. Na kraju, ukoliko ključni igrači imaju veliki broj asistencija, kao npr. Stephen Curry, Isaiah Thomas ili Kyrie Irving šanse za bolji plasman tokom sezone drastično rastu. Posebni zaključci, iako slični, mogla bi se izvući i u slučaju PCA analize tabele performansi protivnika (dugme tipa radio Odabir tipa performansi). Generalne zaključke, kratka zapažanja ili eventualne nejasnoće prilikom tumačenja glavnih komponenti nakon promene tipa analiziranih performansi ostavljamo za komentare čitalaca.