Goal: Visualize every shot a player takes during a single game with information on the closest defender Steps:
In [45]:
# Getting Basic Data
import goldsberry
import pandas as pd
%matplotlib inline
import matplotlib.pyplot as plt
import seaborn as sns
pd.set_option("display.max_columns", 50)
pd.options.mode.chained_assignment = None
print goldsberry.__version__
print pd.__version__
In [15]:
# Getting Players List
players_2015 = goldsberry.PlayerList(Season='2015-16')
players_2015 = pd.DataFrame(players_2015.players())
In [22]:
harden_id = players_2015.loc[players_2015['DISPLAY_LAST_COMMA_FIRST'].str.contains("Harden"), 'PERSON_ID']
In [34]:
#XY Shot Charts
harden_shots = goldsberry.player.shot_chart(harden_id.values.tolist()[0], Season='2015-16')
harden_shots = pd.DataFrame(harden_shots.chart())
In [35]:
harden_shots.shape
Out[35]:
In [36]:
harden_shots.head()
Out[36]:
The query in the below box no longer works thanks to the NBA restricting access to the data.
In [39]:
dashboard = goldsberry.player.shot_dashboard(harden_id)
In [40]:
pd.DataFrame(dashboard.dribble())
Out[40]:
In [4]:
#Sort XY Shots and Assign a Shot Number
#ShotNumber will be used to merge the two datasets.
harden_shots.sort(['GAME_ID', 'GAME_EVENT_ID'], inplace=True)
harden_shots['SHOT_NUMBER'] = harden_shots.groupby(['GAME_ID', 'PLAYER_ID'])['GAME_EVENT_ID'].cumcount()+1
In [5]:
#Merge data into a single dataframe
harden_shots_full = pd.merge(harden_shots, harden_shots_advanced, on=['GAME_ID', 'SHOT_NUMBER'], how='left')
In [9]:
harden_shots_full.head()
Out[9]:
In [46]:
sns.set_style("white")
sns.set_color_codes()
plt.figure(figsize=(12,11))
plt.scatter(harden_shots.LOC_X, harden_shots.LOC_Y)
plt.show()
In [47]:
def draw_court(ax=None, color='black', lw=2, outer_lines=False):
# If an axes object isn't provided to plot onto, just get current one
if ax is None:
ax = plt.gca()
# Create the various parts of an NBA basketball court
# Create the basketball hoop
# Diameter of a hoop is 18" so it has a radius of 9", which is a value
# 7.5 in our coordinate system
hoop = Circle((0, 0), radius=7.5, linewidth=lw, color=color, fill=False)
# Create backboard
backboard = Rectangle((-30, -7.5), 60, -1, linewidth=lw, color=color)
# The paint
# Create the outer box 0f the paint, width=16ft, height=19ft
outer_box = Rectangle((-80, -47.5), 160, 190, linewidth=lw, color=color,
fill=False)
# Create the inner box of the paint, widt=12ft, height=19ft
inner_box = Rectangle((-60, -47.5), 120, 190, linewidth=lw, color=color,
fill=False)
# Create free throw top arc
top_free_throw = Arc((0, 142.5), 120, 120, theta1=0, theta2=180,
linewidth=lw, color=color, fill=False)
# Create free throw bottom arc
bottom_free_throw = Arc((0, 142.5), 120, 120, theta1=180, theta2=0,
linewidth=lw, color=color, linestyle='dashed')
# Restricted Zone, it is an arc with 4ft radius from center of the hoop
restricted = Arc((0, 0), 80, 80, theta1=0, theta2=180, linewidth=lw,
color=color)
# Three point line
# Create the side 3pt lines, they are 14ft long before they begin to arc
corner_three_a = Rectangle((-220, -47.5), 0, 140, linewidth=lw,
color=color)
corner_three_b = Rectangle((220, -47.5), 0, 140, linewidth=lw, color=color)
# 3pt arc - center of arc will be the hoop, arc is 23'9" away from hoop
# I just played around with the theta values until they lined up with the
# threes
three_arc = Arc((0, 0), 475, 475, theta1=22, theta2=158, linewidth=lw,
color=color)
# Center Court
center_outer_arc = Arc((0, 422.5), 120, 120, theta1=180, theta2=0,
linewidth=lw, color=color)
center_inner_arc = Arc((0, 422.5), 40, 40, theta1=180, theta2=0,
linewidth=lw, color=color)
# List of the court elements to be plotted onto the axes
court_elements = [hoop, backboard, outer_box, inner_box, top_free_throw,
bottom_free_throw, restricted, corner_three_a,
corner_three_b, three_arc, center_outer_arc,
center_inner_arc]
if outer_lines:
# Draw the half court line, baseline and side out bound lines
outer_lines = Rectangle((-250, -47.5), 500, 470, linewidth=lw,
color=color, fill=False)
court_elements.append(outer_lines)
# Add the court elements onto the axes
for element in court_elements:
ax.add_patch(element)
return ax
Unfortunately, the NBA has blocked access to the data that was used to construct the following shot charts. Prior to about February, they had data that contained very interesting metrics on individual shots. One of those metrics was the proximity of the nearest defender.
The following charts basically graph circles around the shot location on court that mark where the defender was at the time of shot. A bigger circle means the shooter was more wide-open. We do not know where on the circle the defender as, only that the defender was somewhere on the perimeter of the circle
In [35]:
plt.figure(figsize=(12,11))
plt.scatter(harden_shots_full.LOC_X[0], harden_shots_full.LOC_Y[0])
draw_court()
defender = Circle(xy, def_dist, alpha=.5)
fig = plt.gcf()
fig.gca().add_artist(defender)
# Descending values along the axis from left to right
plt.xlim(-300,300)
plt.ylim(422.5, -47.5)
Out[35]:
In [43]:
len(harden_shots_full)
Out[43]:
In [65]:
def draw_defender_radius(df, ax=None, alpha = .25):
# If an axes object isn't provided to plot onto, just get current one
if ax is None:
ax = plt.gca()
for i in range(len(df)):
defender = Circle((df.LOC_X[i],df.LOC_Y[i]),
radius = df.CLOSE_DEF_DIST[i]*10,
alpha = alpha)
ax.add_patch(defender)
return ax
In [52]:
def fancy_shotchart(df):
plt.figure(figsize=(12,11))
plt.scatter(df.LOC_X, df.LOC_Y)
draw_court()
draw_defender_radius(df)
# Descending values along the axis from left to right
plt.xlim(-300,300)
plt.ylim(422.5, -47.5)
In [53]:
harden_game = harden_shots_full.ix[harden_shots.GAME_ID == '0021400003']
In [66]:
fancy_shotchart(harden_game)
In [76]:
plt.figure(figsize=(12,11))
plt.scatter(harden_game.LOC_X, harden_game.LOC_Y,
s=pi*(harden_game.CLOSE_DEF_DIST*10)**2,
alpha = .25, c = harden_game.SHOT_MADE_FLAG,
cmap = plt.cm.RdYlGn)
plt.scatter(harden_game.LOC_X, harden_game.LOC_Y, c='black')
draw_court()
# Descending values along the axis from left to right
plt.xlim(-300,300)
plt.ylim(422.5, -47.5)
Out[76]:
In [77]:
def fancy_shots(df):
plt.figure(figsize=(12,11))
plt.scatter(df.LOC_X, df.LOC_Y,
s=pi*(df.CLOSE_DEF_DIST*10)**2,
alpha = .25, c = df.SHOT_MADE_FLAG,
cmap = plt.cm.RdYlGn)
plt.scatter(df.LOC_X, df.LOC_Y, c='black')
draw_court()
# Descending values along the axis from left to right
plt.xlim(-300,300)
plt.ylim(422.5, -47.5)
In [79]:
fancy_shots(harden_shots_full.ix[harden_shots.GAME_ID == '0021400003'])
In [82]:
fancy_shots(harden_shots_full.ix[harden_shots.GAME_ID == '0021400087'])
In [83]:
fancy_shots(harden_shots_full.ix[harden_shots.GAME_ID == '0021400512'])