このブログではBリーグ以外の話を扱うつもりはないですが、NBAのデータAPIで遊んでみたのでそれの共有です。興味のある方もいるかもしれないと思いましたので。
ちなみに私も最初はNBAのデータを使ってブログを書こうかなと思ったりしたのですが、NBAだと似たようなことをやっている人が既にいっぱいいそうだと思ったのと、やはり愛着(愛情?)という面でBリーグ以上の気持ちがNBAに持てなかったので今に至っています。
ただやはりNBAだとAPIがあったりするので便利ですね。そんなに使いやすいAPIだという印象は持ちませんでしたが(ドキュメントもあまりなさそうです)、それでもほぼリアルタイムで最新の情報が手に入るのは素晴らしいことです。
以下のRのコードはここのドキュメント群を参考にして書いたもので、ゴールデンステート・ウォリアーズ各選手の今季のスタッツを取得し、そしてこのブログで前にやりました出場時間のヒートマップを作成しています。各チームのTeam IDはこちらで分かります。
if (!require(dplyr)) { install.packages("dplyr") library(dplyr) } if (!require(ggplot2)) { install.packages("ggplot2") library(ggplot2) } if (!require(stringr)) { install.packages("stringr") library(stringr) } if(!require(httr)) { install.packages("httr") library(httr) } if(!require(jsonlite)) { install.packages("jsonlite") library(jsonlite) } teamId <- 1610612744 # Get game log (a.k.a. schedule) to get Game IDs url <- paste0("https://stats.nba.com/stats/teamgamelog", "?DateFrom=&DateTo=&LeagueID=&Season=2018-19&SeasonType=Regular+Season", "&TeamID=", as.character(teamId)) httpResponse = GET(url, add_headers(Referer = "http://stats.nba.com"), accept_json()) res <- content(httpResponse) colNames <- res$resultSets[[1]]$headers numGames <- length(res$resultSets[[1]]$rowSet) df.games <- data.frame() for (i in 1:numGames) { arrayRow <- as.character(res$resultSets[[1]]$rowSet[[i]]) df <- as.data.frame(matrix(arrayRow, nrow = 1), stringsAsFactors = FALSE) colnames(df) <- colNames df.games <- rbind(df.games, df) } # Add game index df.games$Game_ID <- as.character(df.games$Game_ID) df.games <- df.games %>% arrange(Game_ID) %>% mutate(Game_Index = row_number()) # Access each game df.boxscore <- data.frame() for (gameId in df.games$Game_ID) { url <- paste0("https://stats.nba.com/stats/boxscoretraditionalv2", "?EndPeriod=1&EndRange=0", "&GameID=", gameId, "&RangeType=0&StartPeriod=1&StartRange=0") httpResponse = GET(url, add_headers(Referer = "http://stats.nba.com"), accept_json()) res <- content(httpResponse) colNames <- res$resultSets[[1]]$headers for (i in 1:length(res$resultSets[[1]]$rowSet)) { arrayRow <- as.character(res$resultSets[[1]]$rowSet[[i]]) df <- as.data.frame(matrix(arrayRow, nrow = 1), stringsAsFactors = FALSE) colnames(df) <- colNames df.boxscore <- rbind(df.boxscore, df) } } df.output <- merge(df.boxscore, df.games[, c("Game_ID", "Team_ID", "Game_Index")], by.x = c("GAME_ID", "TEAM_ID"), by.y = c("Game_ID", "Team_ID")) ConvertMinStrToDec <- function(min_str) { Convert <- function(item) { min <- as.numeric(item[1]) min <- min + as.numeric(item[2]) / 60 round(min, 2) } ls <- sapply(stringr::str_split(min_str, ":"), Convert) return(ls) } df.output$MIN_NUM <- ConvertMinStrToDec(df.output$MIN) df.output[is.na(df.output$MIN_NUM),]$MIN_NUM <- 0 ggplot() + geom_tile(data = df.output, aes(x = Game_Index, y = PLAYER_NAME, fill = MIN_NUM)) + ylab("") + xlab("nth Game") + ggtitle("Miniutes Played over Games - Golden State Warriors") + scale_fill_continuous(high = "navy", low = "white", guide_legend(title = "MIN")) + scale_x_continuous(breaks = seq(5, 82, by = 5)) + theme(plot.background = element_blank(), plot.title = element_text(hjust = 0.5), panel.grid.minor = element_blank(), panel.grid.major = element_blank(), panel.background = element_blank(), axis.line = element_blank(), axis.ticks = element_blank(), axis.text.y = element_text(hjust = 1, size = 8), axis.text.x = element_text(size = 8), strip.text = element_text(face = "bold"), strip.background = element_rect(fill = "white", colour = "white") )
結果は以下のようなヒートマップになりました。ロスターの人数は多めのNBAですが、長いシーズン、やはりみんな起用されているのですね。カズンズのプレータイムは今後どうなっていきますかね~。
今後BリーグがこのようなAPIの作成に着手するかは分かりませんが、それなりの投資になりますし、想像するに優先順位は低めの案件でしょうね。
私は純粋なソフトウェアエンジニア的視点で、APIなどを開放することによりサードパーティーの参加を促しエコシステムを強化する、という戦略の信奉者なので期待したいところではありますが。
追記
過去のデータも取得できることがわかったので、ついでに前人未到の72勝を記録したときのシカゴ・ブルズのヒートマップも作ってみました。マイケル・ジョーダンは全試合出場していました。
追記(2019.12.13)
NBA APIの仕様が変わり、元のコードだとAPIのアクセスがブロックされるようになりましたのコードを修正しました。