In my last post I produced some NBA shot charts in R using data scraped from stats.nba.com and ggplot2 . This time I extracted all shot location data available for 490 players and linked it to a Tableau dashboard.

The first dashboard shows each shot attempted during the 2014-15 NBA Regular Season. On the right, it is possible to select team, player, shot type, shot zone and shot range. The table above the chart is also updated in line with the filter selection (click on the image to open the dashboard on a new window).

Another way to visualise this data is to group shots by shot zone. The dashboard below displays shot accuracy for each zone of the court. Note how the “Above the Break 3” area is located in the 2-point region. This is due to the fact that I averaged the X and Y coordinates of all shots taken from “Above the Break 3”. Shots taken from the left and right bring the average location inside the 2-point region.

The half court image is the same I used in my last post. This is how you we add the image to the plot:

Map -> Background Image -> Add Image…

Select the NBA court jpeg file and click Edit. The X field limits should be -250 to 250 and Y field limits should be -52 to 418.

In order to scrape and save the shot data using R, I created a function that extract data for a player and ran it in a loop. This will take some time to run depending on the memory you have available (when I first ran this code it took about 40 minutes). Once we have the data in a readable format it is time to link it to Tableau.

## load the file players.csv from my NBA Shot Chart project repo in my GitHub page. players <- read.csv("players.csv", header = TRUE) ## create function library(rjson) AllShotData <- function(playerID) { # Shot Data shotURL <- paste("http://stats.nba.com/stats/shotchartdetail?CFID=33&CFPARAMS=2014-15&ContextFilter=&ContextMeasure=FGA&DateFrom=&DateTo=&GameID=&GameSegment=&LastNGames=0&LeagueID=00&Location=&MeasureType=Base&Month=0&OpponentTeamID=0&Outcome=&PaceAdjust=N&PerMode=PerGame&Period=0&PlayerID=",playerID,"&PlusMinus=N&Position=&Rank=N&RookieYear=&Season=2014-15&SeasonSegment=&SeasonType=Regular+Season&TeamID=0&VsConference=&VsDivision=&mode=Advanced&showDetails=0&showShots=1&showZones=0", sep = "") # import from JSON shotData <- fromJSON(file = shotURL, method="C") # unlist shot data, save into a data frame shots <- data.frame(matrix(unlist(shotData$resultSets[[1]][[3]]), ncol=21, byrow = TRUE)) # shot data headers colnames(shots) <- shotData$resultSets[[1]][[2]] # covert x and y coordinates into numeric shots$LOC_X <- as.numeric(as.character(shots$LOC_X)) shots$LOC_Y <- as.numeric(as.character(shots$LOC_Y)) shotsSHOT_DISTANCE <- as.numeric(as.character(shots$SHOT_DISTANCE)) # return table return(shots) } ## Execute shot data function in loop shots.output <- data.frame() for (i in players$player_id) { tryCatch({ shots.output <- rbind(shots.output, AllShotData(i)); }, error=function(e){cat("ERROR in series", i,":", conditionMessage(e), "

") }) } # View and save shot data View(shots.output) save(shots.output, file = "Shot Location Data.RData")

Feel free to comment or ask any questions in the comment section below.