Erstellen eines animierten Liniendiagramms für den gleitenden Durchschnitt in R. Abrufen von Daten über die NBA-API

Wir analysieren weiterhin Basketballdaten mit R.


Im Gegensatz zum vorherigen Artikel , der rein unterhaltsam war, können die in diesem Artikel erstellten Grafiken unter dem Gesichtspunkt der Analyse des Teamspiels der Saisonkampagne interessant sein.


Und wir werden gleitende Durchschnittsgraphen für drei Arten von NBA-Teambewertungen erstellen: Angriffs-, Defensiv- und Nettobewertung (d. H. Die Differenz zwischen den ersten beiden). Kurz gesagt. Angriffs- und Verteidigungsbewertungen sind die Anzahl der Punkte, die ein Team für 100 Besitztümer erzielt / verpasst hat. NET-Bewertung - das ist ihr Unterschied für einhundert Besitztümer. Jeder, der mehr über sie erfahren möchte, kann das Glossar zur Basketball-Referenz lesen. Es gibt eine Berechnungsformel, die ich auch mit R implementiert habe, aber ich habe noch keinen Artikel darüber veröffentlicht.


Ich werde auch erklären, warum ich das Diagramm des gleitenden Durchschnitts erstellen werde. In jedem einzelnen Spiel ist der Anteil der Zufälligkeit zu hoch, die Indikatoren springen von 70 auf 150, was die Datenanalyse unbrauchbar macht, und die Grafik selbst ähnelt eher einem Kardiogramm. Wenn wir den kumulierten Durchschnitt nehmen, erhalten wir ein weiteres Extrem: Der Zeitplan ähnelt gedämpften Schwankungen, und Spiele am Ende der Saison, wenn sie zu bereits ausgetragenen 70-75 Spielen hinzugefügt werden, haben praktisch keinen Einfluss auf den Gesamtindikator. Grob gesagt sind sie "nicht sichtbar". Der gleitende Durchschnitt ist in diesem Fall der Ausweg aus der Pattsituation. Einerseits nimmt der Einfluss des Zufalls ab, andererseits gibt es keine übermäßige Anhäufung von Ergebnissen. In der Basketballstatistik machen sie normalerweise einen gleitenden Durchschnitt von 10 Spielen.


Verwendete Bibliotheken


library(httr) library(jsonlite) library(tidyverse) library(lubridate) library(zoo) library(ggthemes) library(gganimate) 

Abrufen von Daten mithilfe der NBA-API


Beim letzten Mal habe ich Daten mit der Erweiterung NBA Data Retriever abgerufen . Dieses Mal werde ich die NBA-API verwenden, um die erforderlichen Daten direkt in R zu laden.


Zuerst finden wir heraus, woher diese Daten stammen. Öffnen Sie dazu die benötigte Seite auf stats.nba.com und rufen Sie die Entwicklertools auf. Öffnen Sie dann Netzwerk -> XHR und drücken Sie F5. In der angezeigten Liste finden wir eine Datei mit einem Namen, der dem Namen der Seite ähnlich ist. Wir brauchen ihn. Nachdem Sie sichergestellt haben, dass Sie die richtige Datei ausgewählt haben, kopieren Sie die Adresse in R. Auf den Bildern sieht es so aus.


Öffnen Sie die gewünschte Datei



Die Datei sollte so aussehen



Kopie an R-Adresse



Jetzt können wir in R Studio arbeiten . Um die benötigten Informationen zu erhalten, verwenden wir die GET Funktion des http Pakets. Damit die Anforderung jedoch korrekt ausgeführt werden kann (dies kann durch die Funktion status_code überprüft werden, sie muss 200 sein), müssen Sie Header hinzufügen, um die Arbeitsparameter der HTTP-Transaktion zu bestimmen


 ##Adding headers request_headers <- c( "accept-encoding" = "gzip, deflate, sdch", "accept-language" = "en-US,en;q=0.8", "cache-control" = "no-cache", "connection" = "keep-alive", "host" = "stats.nba.com", "pragma" = "no-cache", "upgrade-insecure-requests" = "1", "user-agent" = "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_11_2) AppleWebKit/601.3.9 (KHTML, like Gecko) Version/9.0.2 Safari/601.3.9" ) #Getting a response request <- GET(adv_box_team, add_headers(request_headers)) 

Wir bekommen eine Antwort wie diese:



Aber während die Daten, die wir brauchen, nicht sichtbar sind. Um sie zu erhalten, extrahieren wir zuerst den content der Anforderung durch den Funktionsinhalt in eine JSON-Datei und konvertieren ihn dann in eine Liste mit einer Funktion aus dem jsonlite Paket mit dem sprechenden Namen von fromJSON


 boxscore_data <- fromJSON(content(request, as = "text")) 

Als Ergebnis erhalten wir eine Liste, die bereits alle benötigten Informationen enthält, und bringen sie dann einfach in die Form, die für die Arbeit benötigt wird.


Datenaufbereitung


Erstellen Sie dazu eine Datentabelle anstelle einer Liste und fügen Sie dann Spaltenüberschriften hinzu.


 #Convert to tibble data and assigning column names table <- tbl_df(data.frame(boxscore_data$resultSets$rowSet[[1]], stringsAsFactors = FALSE)) names(table) <- toupper(boxscore_data$resultSets$headers[[1]]) 

toupper ist eine Funktion, die alle Zeichen durch Großbuchstaben ersetzt. Danach sollten wir eine Tabelle mit 2460 Zeilen und 46 Spalten erhalten. Grundsätzlich können Sie mit der Tabelle in dieser Form arbeiten. Es ist jedoch besser, unnötige Informationen auszuschließen, um bequemer und schneller arbeiten zu können.


 ##Select the columns you want to analyze rating <- table %>% select(TEAM_ID, TEAM_ABBREVIATION, TEAM_NAME, GAME_ID, GAME_DATE, MATCHUP, WL, E_OFF_RATING, E_DEF_RATING, E_NET_RATING) 

Wenn Sie sich die Quellentabelle ansehen, sehen Sie zwei Arten derselben Bewertung: „normal“ und mit dem Präfix E. Ohne auf Details einzugehen, berücksichtigt die E-Bewertung das Tempo des Spiels und ist daher genauer. Wir nehmen es.


Als nächstes möchte ich die Namen der Bewertungen vereinfachen. Sie müssen in die Funktionsargumente eingeführt werden, und es ist besser, die Notation zu verwenden, die einer Vielzahl von Benutzern besser bekannt ist: ORTG, DRTG, NRTG. Hier kann man "verwirrt" werden, wenn man einen regulären Ausdruck schreibt und durch str_replace , aber das Schreiben ist immer noch ein Vergnügen, und hier können wir perfekt auf sie verzichten. Wir müssen nur die 3, 7, 9 und 12 Zeichen der aktuellen Namen extrahieren, sie kombinieren und die Spaltennamen durch den resultierenden Zeichenvektor ersetzen. All dies geschieht mit den Funktionen des stringr Pakets: str_sub und str_c (ein Analogon der paste0 ).


 ## Renaming columns with E_OFF_RATING on ORTG rating1 <- rating %>% rename_at(vars(starts_with("E_")), list(~str_c(str_sub(., start = 3, end = 3), str_sub(., start = 7, end = 7), str_sub(., start = 9, end = 9), str_sub(., start = 12, end = 12)))) 

at in den dplyr hat dieselbe Eigenschaft wie das dt[, lapply(.SD, func), .SDols = col1] im Paket data.table : Die Aktion wird gleichzeitig auf mehrere Spalten angewendet. Hier wählen wir alle Spalten aus, deren Name mit "E_" beginnt.


Als Ergebnis erhalten wir eine solche Tabelle, mit der wir weiter arbeiten werden:


TEAM_IDTEAM_ABBREVIATIONTEAM_NAMEGAME_IDGAME_DATEMATCHUPWlORTGDRTGNRTG
1610612749MilMilwaukee Dollar00218012262019-04-10T00: 00: 00MIL vs. OkcL.102.4116.8-14,4
1610612766ChaCharlotte Hornissen00218012222019-04-10T00: 00: 00CHA vs. ORLL.121.4130.1-8.6
1610612758SACSacramento Könige00218012302019-04-10T00: 00: 00SAC @ PORL.129.7136.4-6.8
1610612748MIAMiami Hitze00218012212019-04-10T00: 00: 00MIA @ BKNL.84.2103.6-19,4
1610612750MINMinnesota Timberwolves00218012282019-04-10T00: 00: 00MIN @ DENL.98.3103.7-5.4

Die Funktion running_offnet_rating_nba zum Zeichnen und Animieren eines gleitenden Durchschnitts.


Lassen Sie uns wie beim letzten Mal eine Funktion erstellen, um minimale Änderungen an den Berechnungen vorzunehmen.


Die Funktion rolling_offnet_rating_nba :


 rolling_offnet_rating_nba <- function(table, name, variable, col1 = col1, col2 = col2) 

Tabelle ist der Name der Datentabelle,
name - die Abkürzung des Teams, für das die Grafiken erstellt werden ("BOS", "LAL" usw.).
Variable - die Bewertung, die berechnet wird (hier sind zwei Optionen, ORTG oder NRTG, für die Schutzbewertung habe ich eine separate Funktion erstellt)
col1 und col2 - Linienfarbe bei einem Wert über / unter dem Durchschnitt.


Die meisten dplyr Funktionen verwenden eine nicht standardmäßige Auswertung (NSE ). Dies ist ein allgemeiner Begriff, der bedeutet, dass sich ihre Bewertung von der in R üblichen Bewertung unterscheidet. Dies ermöglicht es uns, das Schreiben von Code zu vereinfachen und mit SQL-Datenbanken zu arbeiten. Das Minus ist jedoch, dass wir den Wert nicht durch ein an anderer Stelle definiertes äquivalentes Objekt ersetzen können.


Dplyr verwendet die Tidy-Bewertung . Daher müssen spezielle Werkzeuge (Zitierfunktionen, Operator !!) verwendet werden, um Probleme zu lösen, die während der Programmierung auftreten. Sie können hier mehr darüber lesen und hier sehen .


Der folgende Code nimmt den Namen des Funktionsarguments und schreibt den Ausdruck, der ihm präsentiert wurde. (Um zu verstehen, wie enquo und ähnliche Funktionen funktionieren, ist es hilfreich, die Ausgabe dieser Funktion zu drucken.)


 ##Return the entered value in the function argument in the type quosure quo_rating <- enquo(variable) quo_col1 <- enquo(col1) quo_col2 <- enquo(col2) 

Als nächstes ändern wir das Datenformat einiger Spalten: Wir machen GAME_DATE des Zeichens zu einer Spalte im Datumsformat und machen die Bewertungsspalten numerisch. Weil Wir wenden die as.numeric Funktion auf drei Spalten an, dann wird mutate_at anstelle von mutate_at verwendet. Und wir sortieren alles in aufsteigender Reihenfolge des Datums.


 ##Changing the data type of multiple columns test1 <- table %>% mutate(GAME_DATE = as.Date(ymd_hms(GAME_DATE))) %>% mutate_at(vars(ORTG:NRTG), list(~as.numeric)) %>% arrange(GAME_DATE) 

Und dann berechnen wir den gleitenden Durchschnitt von 10 Spielen der Mannschaft, die wir brauchen. Verwenden Sie dazu die Funktion rollmeanr aus dem zoo Paket. r am Ende des Namens bedeutet, dass das Ergebnis rechtsbündig sein sollte. In den ersten neun Spielen der Saison ist ein gleitender Durchschnitt von 10 Spielen einfach nicht zu berechnen. Daher lassen wir diese Felder unverändert, indem wir sie mit dem Füllargument in NA ausfüllen. na.omit entfernt aus der Tabelle die Zeilen, in denen diese NA vorkommen.


 ##The calculation of the moving average team <- test1 %>% filter(TEAM_ABBREVIATION == "DAL") %>% mutate(RATING = rollmeanr(ORTG, k = 10, fill= NA)) %>% na.omit(test1) 

Der Teamtisch sieht folgendermaßen aus:


TEAM_IDTEAM_ABBREVIATIONTEAM_NAMEGAME_IDGAME_DATEMATCHUPWlORTGDRTGNRTGBewertung
1610612742DALDallas Außenseiter00218001502018-11-06DAL vs. WarW.116.899,217.6105,51
1610612742DALDallas Außenseiter00218001602018-11-07DAL @ UTAL.98.5112.0-13,6104,92
1610612742DALDallas Außenseiter00218001812018-11-10DAL vs. OkcW.115,0101.113.9104.13
1610612742DALDallas Außenseiter00218001932018-11-12DAL @ CHIW.98.391.07.3103.03
1610612742DALDallas Außenseiter00218002102018-11-14DAL vs. UTAW.117,365,851.6105,34

Grundsätzlich haben wir bereits die Informationen erhalten, die wir benötigen. Mit zwei Codezeilen können Sie ein Liniendiagramm erstellen. Die schwarze Linie auf weißem Hintergrund ist jedoch sowohl aus ästhetischer als auch aus informativer Sicht von geringem Interesse. Ein weiterer Teil des "Funktionskörpers" korrigiert dies.


Zunächst addieren wir die Daten zum durchschnittlichen 10. und 21. (zehnten von unten) Bewertungswert sowie zum Datum 10 des Teamspiels (d. H. Dem ersten, für das der gleitende Durchschnitt berechnet wird und der nach dem Löschen der Zeilen aus NA in der Teamtabelle verbleibt). .


 ##The average, 10 and 21 ratings in the entire League. average <- league %>% mutate(average = mean(!! quo_rating)) %>% select(average) %>% unique() %>% .$average top10 <- league %>% arrange(desc(!! quo_rating)) %>% select(!! quo_rating) %>% slice(10) top10 <- top10[[1]] bottom10 <- league %>% arrange(desc(!! quo_rating)) %>% select(!! quo_rating) %>% slice(21) bottom10 <- bottom10[[1]] ##Getting the date of the first rollaverage data <- team %>% select(GAME_DATE) %>% arrange(GAME_DATE) data <- data[[1,1]] 

Von zuvor nicht verwendeten Funktionen wird hier die slice Funktion angezeigt, die die Zeilen anhand ihrer Seriennummer auswählt.


Als nächstes wählen wir 2 Farben und deren Namen aus. Die Daten werden wie beim letzten Mal aus der Tabelle table_color . Der Name wird in der Überschrift des Diagramms verwendet, um zu erklären, welche der Farben Werten unter dem Durchschnitt entspricht und welche höher sind.


 ##Getting color and color_name selected color color1 <- table_color %>% filter(TEAM_ABBREVIATION == name) %>% select(!! quo_col1) color1 <- color1[[1]] color2 <- table_color %>% filter(TEAM_ABBREVIATION == name) %>% select(!! quo_col2) color2 <- color2[[1]] name1 <- paste0("name_", quo_name(quo_col1)) name2 <- paste0("name_", quo_name(quo_col2)) name_color1 <- table_color %>% filter(TEAM_ABBREVIATION == name) %>% select(name1) name_color1 <- name_color1[[1]] name_color2 <- table_color %>% filter(TEAM_ABBREVIATION == name) %>% select(name2) name_color2 <- name_color2[[1]] 

Die Funktionsargumente sind standardmäßig col1 und col2. Dies sind die erste und die zweite Farbe der Befehle. In den meisten Fällen (genauer in 26) müssen diese Werte nicht geändert werden. Für vier Teams sollte jedoch die folgende Farbe in ihrer Farbpalette verwendet werden. In Dallas und Minnesota sind die erste und die zweite Farbe zu ähnlich, während sie in Milwaukee und Brooklyn auf weißem Hintergrund nicht sichtbar sind. Sowohl das als auch ein anderes erschweren das Lesen des Zeitplans, daher lohnt es sich, das Argument col2 = col3 für sie zu verwenden.


Als nächstes erhalten wir die maximale Bewertung für das Team. Wir benötigen diesen Wert, um den Text mit dem Bewertungswert in der Tabelle anzuordnen. Ich möchte auf die letzte Codezeile achten. Es kam vor, dass die Funktionen in 89 von 90 Fällen Diagramme perfekt darstellten, aber beim Erstellen einer Schutzbewertung gab Milwaukee einen Fehler aus. Es stellte sich heraus, dass der maximale Bewertungswert in Milwaukee zweimal erreicht wird und ggplot2 natürlich zu schwören beginnt, dass die Ästhetik in unserem Fall entweder 1 oder 73 sein sollte. Daher benötigen wir einen einzigen maximalen Bewertungswert.


 ##The maximum value of the rating max <- team %>% filter(RATING == max(RATING)) %>% select(RATING) max <- max[[1]] 

ggplot2 eines statischen Diagramms in ggplot2


 ##Building and save a static chart Sys.setlocale("LC_ALL", "C") gg <- ggplot(team, aes(GAME_DATE, RATING)) + geom_hline(yintercept = c(top10, bottom10), col = c("red", "blue")) + annotate(geom = "text", x = as.Date(data) + 2, y = top10 - 0.2, label = "TOP 10", col = "red") + annotate(geom = "text", x = as.Date(data) + 2, y = bottom10 + 0.2, label = "BOTTOM 10", col = "blue") + geom_line(size = 2, col = if_else(team$RATING > average, color1, color2)) + theme_tufte() + labs(title = paste0(team$TEAM_NAME, " 10-Game Rolling ", quo_name(quo_rating)), subtitle = paste0(paste0(name_color1, " - above average ", quo_name(quo_rating)), "\n", paste0(name_color2, " - below average ", quo_name(quo_rating))), caption = "Source: BBall Index Data & Tools\nTelegram: @NBAatlantic, twitter: @vshufinskiy") theme(plot.title = element_text(size = 12, hjust = 0.5), plot.caption = element_text(size = 10), plot.subtitle = element_text(size = 9)) ggsave(paste0(unique(team$TEAM_NAME), quo_name(quo_rating), ".jpeg"), gg, width = 8, units = "in") 

Von den neuen hier die Verwendung der if_else Funktion zum Ändern der Farbe der Linie in Abhängigkeit davon, ob die Liga-Durchschnittsbewertung höher oder niedriger ist, sowie die erste Zeile, die das Gebietsschema ändert. Dies geschieht so, dass die Abkürzungen für die Namen der Monate entlang der X-Achse in Englisch geschrieben sind.


Animation eines gleitenden Durchschnitts von 10 Spielen.


Bei der Erstellung der Animation habe ich mehrere Lotionen hinzugefügt, die in der statischen Version nicht möglich sind. Erstens das Änderungsdatum (ähnlich wie sich das Jahr im letzten Artikel geändert hat) sowie den Bewertungswert zu einem bestimmten Zeitpunkt. Es ändert auch die Farbe, je nachdem, ob es über oder unter dem Durchschnitt liegt.


 ##Building animations anim <- gg + theme(plot.title = element_text(hjust = 0.5, size = 25), plot.subtitle = element_text(size = 15), plot.caption = element_text(size = 15), axis.text = element_text(size = 15), axis.title = element_text(size = 18)) + geom_text(aes(x = as.Date(data), y = max + 0.5), label = paste0(quo_name(quo_rating)," ", round(team$RATING, digits = 1)), size = 6, col = if_else(team$RATING > average, color1, color2)) + transition_reveal(GAME_DATE) + labs(title = paste0(team$TEAM_NAME, " 10-Game Rolling ", quo_name(quo_rating)), subtitle = paste0(paste0(name_color1, " - above average ",quo_name(quo_rating)), "\n", paste0(name_color2, " - below average ",quo_name(quo_rating)), "\n", "Date: {frame_along}"), caption = paste0("Source: stats.nba.com\nTelegram: @NBAatlantic, twitter: @vshufinskiy")) 

Ergebnis



Auf dem Chart ist es ziemlich offensichtlich, dass Dallas in der zweiten Hälfte von Februar bis März gesunken ist. Die Erklärung dafür ist sehr einfach: Zu diesem Zeitpunkt in der Saison tauschten die Mavericks 4 von 5 Spielern ihrer ersten fünf aus, und der Hauptzugang, der Lette Kristaps Porzingis, spielte wegen des Kreuzbandrisses eine Minute lang nicht.


Hier werde ich nicht auf die Sportkomponente eingehen. Wenn also jemand daran interessiert ist, die verbleibenden 89 Charts der Saison 2018-19 zu sehen, sind Sie in meinem Blog auf sports.ru willkommen, wo ich einen Artikel mit einer Übersicht der interessantesten von ihnen oder in meinem Telegramm schreiben möchte Kanal über die NBA, wo ich sie alle posten werde.


GitHub-Repository

Source: https://habr.com/ru/post/de459142/


All Articles