-
Notifications
You must be signed in to change notification settings - Fork 12
/
Copy pathnba_shotchart.py
165 lines (120 loc) · 5.53 KB
/
nba_shotchart.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
import sys
import numpy as np
import pandas as pd
# nba_api
from nba_api.stats.static import players
from nba_api.stats.endpoints import shotchartdetail
from nba_api.stats.endpoints import playercareerstats
# matplotlib
import matplotlib.pyplot as plt
import seaborn as sns
from matplotlib import cm
from matplotlib.patches import Circle, Rectangle, Arc, ConnectionPatch
# get_player_shotchartdetail: player_name, season_id -> player_shotchart_df, league_avg
def get_player_shotchartdetail(player_name, season_id):
# player dictionary
nba_players = players.get_players()
player_dict = [player for player in nba_players if player['full_name'] == player_name][0]
# career dataframe
career = playercareerstats.PlayerCareerStats(player_id=player_dict['id'])
career_df = career.get_data_frames()[0]
# team id during the season
team_id = career_df[career_df['SEASON_ID'] == season_id]['TEAM_ID']
# shotchartdetail endpoints
shotchartlist = shotchartdetail.ShotChartDetail(team_id=int(team_id),
player_id=int(player_dict['id']),
season_type_all_star='Regular Season',
season_nullable=season_id,
context_measure_simple="FGA").get_data_frames()
return shotchartlist[0], shotchartlist[1]
# draw court function
def draw_court(ax=None, color="blue", lw=1, outer_lines=False):
if ax is None:
ax = plt.gca()
# Basketball Hoop
hoop = Circle((0,0), radius=7.5, linewidth=lw, color=color, fill=False)
# Backboard
backboard = Rectangle((-30, -12.5), 60, 0, linewidth=lw, color=color)
# The paint
# outer box
outer_box = Rectangle((-80, -47.5), 160, 190, linewidth=lw, color=color, fill=False)
# inner box
inner_box = Rectangle((-60, -47.5), 120, 190, linewidth=lw, color=color, fill=False)
# Free Throw Top Arc
top_free_throw = Arc((0, 142.5), 120, 120, theta1=0, theta2=180, linewidth=lw, color=color, fill=False)
# Free Bottom Top Arc
bottom_free_throw = Arc((0, 142.5), 120, 120, theta1=180, theta2=0, linewidth=lw, color=color)
# Restricted Zone
restricted = Arc((0, 0), 80, 80, theta1=0, theta2=180, linewidth=lw, color=color)
# Three Point Line
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)
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 court shapes
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]
#outer_lines=True
if outer_lines:
outer_lines = Rectangle((-250, -47.5), 500, 470, linewidth=lw, color=color, fill=False)
court_elements.append(outer_lines)
for element in court_elements:
ax.add_patch(element)
# Shot Chart Function
def shot_chart(data, title="", color="b", xlim=(-250, 250), ylim=(422.5, -47.5), line_color="blue",
court_color="white", court_lw=2, outer_lines=False,
flip_court=False, gridsize=None,
ax=None, despine=False):
if ax is None:
ax = plt.gca()
if not flip_court:
ax.set_xlim(xlim)
ax.set_ylim(ylim)
else:
ax.set_xlim(xlim[::-1])
ax.set_ylim(ylim[::-1])
ax.tick_params(labelbottom="off", labelleft="off")
ax.set_title(title, fontsize=18)
# draws the court using the draw_court()
draw_court(ax, color=line_color, lw=court_lw, outer_lines=outer_lines)
# separate color by make or miss
x_missed = data[data['EVENT_TYPE'] == 'Missed Shot']['LOC_X']
y_missed = data[data['EVENT_TYPE'] == 'Missed Shot']['LOC_Y']
x_made = data[data['EVENT_TYPE'] == 'Made Shot']['LOC_X']
y_made = data[data['EVENT_TYPE'] == 'Made Shot']['LOC_Y']
# Plot missed shots
ax.scatter(x_missed, y_missed, c='r', marker="x", s=300, linewidths=3)
# Plot made shots
ax.scatter(x_made, y_made, facecolors='none', edgecolors='g', marker='o', s=100, linewidths=3)
# Set the spines to match the rest of court lines, makes outer_lines
# somewhat unnecessary
for spine in ax.spines:
ax.spines[spine].set_lw(court_lw)
ax.spines[spine].set_color(line_color)
if despine:
ax.spines["top"].set_visible(False)
ax.spines["bottom"].set_visible(False)
ax.spines["right"].set_visible(False)
ax.spines["left"].set_visible(False)
return ax
if __name__ == "__main__":
# Accept 2 arguments
## First argument is the player name
## Second argument is the season id
### if there is no argument
if (len(sys.argv) == 1):
player_name = "Michael Jordan"
season_id = "1997-98"
else:
player_name = sys.argv[1]
season_id = sys.argv[2]
# title
title = player_name + " Shot Chart " + season_id
# Get Shotchart Data using nba_api
player_shotchart_df, league_avg = get_player_shotchartdetail(player_name, season_id)
# Draw Court and plot Shot Chart
shot_chart(player_shotchart_df, title=title)
# Set the size for our plots
plt.rcParams['figure.figsize'] = (12, 11)
plt.show()