From c82669b87447d7e0e0a6680e46a0217fe14c9794 Mon Sep 17 00:00:00 2001 From: Daniel Welch Date: Tue, 26 Dec 2017 12:29:57 -0500 Subject: [PATCH 1/5] fork for plot_team --- NBAapi/__init__.py | 1 + NBAapi/plot.py | 15 +- NBAapi/plot.py.bak | 429 ++++++++++++++++++++++++++++++++++++++++++++ NBAapi/plot_team.py | 427 +++++++++++++++++++++++++++++++++++++++++++ NBAapi/team.py | 144 +++++++-------- NBAapi/team.py.bak | 268 +++++++++++++++++++++++++++ 6 files changed, 1204 insertions(+), 80 deletions(-) create mode 100644 NBAapi/plot.py.bak create mode 100644 NBAapi/plot_team.py create mode 100644 NBAapi/team.py.bak diff --git a/NBAapi/__init__.py b/NBAapi/__init__.py index 28eee18..41e8198 100644 --- a/NBAapi/__init__.py +++ b/NBAapi/__init__.py @@ -1,6 +1,7 @@ #import numpy as np from . import plot +from . import plot_team from . import player from . import team from . import league diff --git a/NBAapi/plot.py b/NBAapi/plot.py index 960aed9..7c56653 100644 --- a/NBAapi/plot.py +++ b/NBAapi/plot.py @@ -1,9 +1,8 @@ -#%% from matplotlib.patches import Circle, Rectangle, Arc import matplotlib.pyplot as plt import numpy as np from scipy import misc -import urllib, cStringIO +import urllib.request, urllib.parse, urllib.error, io from matplotlib.patches import Polygon from matplotlib.collections import PatchCollection from matplotlib.colors import LinearSegmentedColormap @@ -186,7 +185,7 @@ def text_in_zone(string,zone,box_alpha = 0.75,**kwargs): def players_picture(player_id): URL = "http://stats.nba.com/media/players/230x185/%d.png" %player_id - file = cStringIO.StringIO(urllib.urlopen(URL).read()) + file = io.BytesIO(urllib.request.urlopen(URL).read()) return misc.imread(file) def grantland_shotchart(shotchart,leagueavergae,ax=None,short_three=False,fg_range=[-9,9]): @@ -221,7 +220,7 @@ def grantland_shotchart(shotchart,leagueavergae,ax=None,short_three=False,fg_ran counts_norm[(counts>=1) & (counts<2)] = 0.3 patches=[] colors=[] - for offc in xrange(verts.shape[0]): + for offc in range(verts.shape[0]): if counts_norm[offc] != 0: xc,yc = verts[offc][0],verts[offc][1] b[:,0] = xy[:,0]*counts_norm[offc] + xc @@ -269,9 +268,9 @@ def grantland_shotchart(shotchart,leagueavergae,ax=None,short_three=False,fg_ran ax.add_collection(p) p.set_clim([0, len(bins)-1]) - pic = players_picture(shotchart.loc[0,'PLAYER_ID']) - ax.imshow(pic,extent=[15,25,30,37.8261]) - ax.text(20,29,shotchart.loc[0,'PLAYER_NAME'],fontsize=16,horizontalalignment='center',verticalalignment='center') + # pic = players_picture(shotchart.loc[0,'PLAYER_ID']) + # ax.imshow(pic,extent=[15,25,30,37.8261]) + # ax.text(20,29,shotchart.loc[0,'PLAYER_NAME'],fontsize=16,horizontalalignment='center',verticalalignment='center') def shot_zone(X,Y): ''' @@ -426,4 +425,4 @@ def shot_scatter(df,player_pic=True,ax=None,noise=True,**kwargs): pic = players_picture(player_id) ax.imshow(pic,extent=[15,25,30,37.8261]) ax.text(20,29,name,fontsize=16,horizontalalignment='center',verticalalignment='center') - ax.text(0,-7,'By: Doingthedishes',color='black',horizontalalignment='center',fontsize=20,fontweight='bold') \ No newline at end of file + ax.text(0,-7,'By: Doingthedishes',color='black',horizontalalignment='center',fontsize=20,fontweight='bold') diff --git a/NBAapi/plot.py.bak b/NBAapi/plot.py.bak new file mode 100644 index 0000000..960aed9 --- /dev/null +++ b/NBAapi/plot.py.bak @@ -0,0 +1,429 @@ +#%% +from matplotlib.patches import Circle, Rectangle, Arc +import matplotlib.pyplot as plt +import numpy as np +from scipy import misc +import urllib, cStringIO +from matplotlib.patches import Polygon +from matplotlib.collections import PatchCollection +from matplotlib.colors import LinearSegmentedColormap + +def court(ax=None, color='black', lw=4, outer_lines=False,direction='up',short_three=False): + ''' + Plots an NBA court + outer_lines - accepts False or True. Plots the outer side lines of the court. + direction - 'up' or 'down' depending on how you like to view the court + Original function from http://savvastjortjoglou.com/ + ''' + # If an axes object isn't provided to plot onto, just get current one + if ax is None: + if direction=='up': + ax = plt.gca(xlim = [-30,30],ylim = [43,-7],xticks=[],yticks=[],aspect=1.0) + plt.text(22,44,'By: Doingthedishes',color='black',horizontalalignment='center',fontsize=20,fontweight='bold') + elif direction=='down': + ax = plt.gca(xlim = [30,-30],ylim = [-7,43],xticks=[],yticks=[],aspect=1.0) + plt.text(-22,-7,'By: Doingthedishes',color='black',horizontalalignment='center',fontsize=20,fontweight='bold') + else: + ax = plt.gca() + # Create the various parts of an NBA basketball court + + # Create the basketball hoop + # Diameter of a hoop is 1.5 + hoop = Circle((0, 0), radius=0.75, linewidth=lw/2, color=color, fill=False) + + # Create backboard + backboard = Rectangle((-3, -0.75), 6, -0.1, linewidth=lw, color=color) + + # The paint + # Create the outer box of the paint, width=16ft, height=19ft + outer_box = Rectangle((-8, -5.25), 16, 19, linewidth=lw, color=color, + fill=False) + # Create the inner box of the paint, widt=12ft, height=19ft + inner_box = Rectangle((-6, -5.25), 12, 19, linewidth=lw, color=color, + fill=False) + + # Create free throw top arc + top_free_throw = Arc((0, 13.75), 12, 12, theta1=0, theta2=180, + linewidth=lw, color=color, fill=False) + # Create free throw bottom arc + bottom_free_throw = Arc((0, 13.75), 12, 12, 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), 8, 8, theta1=0, theta2=180, linewidth=lw, + color=color) + + # Three point line + if not short_three: + corner_three_a = Rectangle((-22, -5.25), 0, np.sqrt(23.75**2-22.0**2)+5.25, linewidth=lw, + color=color) + corner_three_b = Rectangle((22, -5.25), 0, np.sqrt(23.75**2-22.0**2)+5.25, linewidth=lw, color=color) + # 3pt arc - center of arc will be the hoop, arc is 23'9" away from hoop + three_arc = Arc((0, 0), 47.5, 47.5, theta1=np.arccos(22/23.75)*180/np.pi, theta2=180.0-np.arccos(22/23.75)*180/np.pi, linewidth=lw, + color=color) + else: + corner_three_a = Rectangle((-22, -5.25), 0, 5.25, linewidth=lw, + color=color) + corner_three_b = Rectangle((22, -5.25), 0, 5.25, linewidth=lw, color=color) + # 3pt arc - center of arc will be the hoop, arc is 23'9" away from hoop + three_arc = Arc((0, 0), 44.0, 44.0, theta1=0, theta2=180, 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] + + if outer_lines: + # Draw the half court line, baseline and side out bound lines + outer_lines = Rectangle((-25, -5.25), 50, 46.75, linewidth=lw, + color=color, fill=False) + center_outer_arc = Arc((0, 41.25), 12, 12, theta1=180, theta2=0, + linewidth=lw, color=color) + center_inner_arc = Arc((0, 41.25), 4, 4, theta1=180, theta2=0, + linewidth=lw, color=color) + court_elements = court_elements + [outer_lines,center_outer_arc,center_inner_arc] + else: + ax.plot([-25,25],[-5.25,-5.25],linewidth=lw,color=color) + # Add the court elements onto the axes + for element in court_elements: + ax.add_patch(element) +# ax.axis('off') + return ax + +def zones(**kwargs): + ''' + Plots zones on the court as per NBA.com + the plot adds to the last plot used or starts a new figure + ''' + ax = plt.gca() + zone1 = Arc((0, 0), 16.0, 16.0,theta1 = -41.0,theta2 = 180.0+41.0,**kwargs) + zone2 = Arc((0, 0), 32.0, 32.0,theta1 = -19.2,theta2 = 180.0+19.2, **kwargs) + ax.add_patch(zone1) + ax.add_patch(zone2) + ang = 60.0 + ax.plot([np.cos(ang/180*np.pi)*8, np.cos(ang/180*np.pi)*16], + [np.sin(ang/180*np.pi)*8, np.sin(ang/180*np.pi)*16],**kwargs) + ang = 120.0 + ax.plot([np.cos(ang/180*np.pi)*8, np.cos(ang/180*np.pi)*16], + [np.sin(ang/180*np.pi)*8, np.sin(ang/180*np.pi)*16],**kwargs) + ax.plot([22,25],[14-5.25,14-5.25],**kwargs) + ax.plot([-22,-25],[14-5.25,14-5.25],**kwargs) + ang = 36.0 + ax.plot([np.cos(ang/180*np.pi)*16, np.cos(ang/180*np.pi)*23.75], + [np.sin(ang/180*np.pi)*16, np.sin(ang/180*np.pi)*23.75],**kwargs) + ang = 72.0 + ax.plot([np.cos(ang/180*np.pi)*16, np.cos(ang/180*np.pi)*24], + [np.sin(ang/180*np.pi)*16, np.sin(ang/180*np.pi)*24],**kwargs) + ang = 72.0+36.0 + ax.plot([np.cos(ang/180*np.pi)*16, np.cos(ang/180*np.pi)*24], + [np.sin(ang/180*np.pi)*16, np.sin(ang/180*np.pi)*24],**kwargs) + ang = 72.0*2 + ax.plot([np.cos(ang/180*np.pi)*16, np.cos(ang/180*np.pi)*23.75], + [np.sin(ang/180*np.pi)*16, np.sin(ang/180*np.pi)*23.75],**kwargs) + ang = 72.0 + ax.plot([np.cos(ang/180*np.pi)*24, np.cos(ang/180*np.pi)*41.25], + [np.sin(ang/180*np.pi)*24, 41.25],**kwargs) + ang = 72.0+36.0 + ax.plot([np.cos(ang/180*np.pi)*24, np.cos(ang/180*np.pi)*41.25], + [np.sin(ang/180*np.pi)*24, 41.25],**kwargs) + +def text_in_zone(string,zone,box_alpha = 0.75,**kwargs): + ''' + Adds text to zones. + Input: + string - text to be added + zone - specify zone (tuple) + Options: + box_alpha - string box transparency + ''' + if zone == ('Less Than 8 ft.','Center(C)') : + t = plt.text(0,2,string,horizontalalignment='center',verticalalignment='center',**kwargs) + t.set_bbox(dict(color='white', alpha=box_alpha)) + elif zone[0] == '8-16 ft.': + if zone[1] == 'Center(C)': + t = plt.text(0,12,string,horizontalalignment='center',verticalalignment='center',**kwargs) + t.set_bbox(dict(color='white', alpha=box_alpha)) + elif zone[1] == 'Right Side(R)' : + t = plt.text(12,0,string,horizontalalignment='center',verticalalignment='center',**kwargs) + t.set_bbox(dict(color='white', alpha=box_alpha)) + elif zone[1] == 'Left Side(L)': + t = plt.text(-12,0,string,horizontalalignment='center',verticalalignment='center',**kwargs) + t.set_bbox(dict(color='white', alpha=box_alpha)) + elif zone[0] == '16-24 ft.': + if zone[1] == 'Center(C)': + t = plt.text(0,20,string,horizontalalignment='center',verticalalignment='center',**kwargs) + t.set_bbox(dict(color='white', alpha=box_alpha)) + elif zone[1] == 'Right Side(R)': + t = plt.text(18,7.3,string,horizontalalignment='center',verticalalignment='center',**kwargs) + t.set_bbox(dict(color='white', alpha=box_alpha)) + elif zone[1] == 'Left Side(L)': + t = plt.text(-18,7.3,string,horizontalalignment='center',verticalalignment='center',**kwargs) + t.set_bbox(dict(color='white', alpha=box_alpha)) + elif zone[1] == 'Right Side Center(RC)': + t = plt.text(12,15.5,string,horizontalalignment='center',verticalalignment='center',**kwargs) + t.set_bbox(dict(color='white', alpha=box_alpha)) + elif zone[1] == 'Left Side Center(LC)': + t = plt.text(-12,15.5,string,horizontalalignment='center',verticalalignment='center',**kwargs) + t.set_bbox(dict(color='white', alpha=box_alpha)) + elif zone[0] == '24+ ft.': + if zone[1] == 'Center(C)': + t = plt.text(0,28,string,horizontalalignment='center',verticalalignment='center',**kwargs) + t.set_bbox(dict(color='white', alpha=box_alpha)) + elif zone[1] == 'Right Side(R)': + t = plt.text(27,0,string,horizontalalignment='center',verticalalignment='center',**kwargs) + t.set_bbox(dict(color='white', alpha=box_alpha)) + elif zone[1] == 'Left Side(L)': + t = plt.text(-27,0,string,horizontalalignment='center',verticalalignment='center',**kwargs) + t.set_bbox(dict(color='white', alpha=box_alpha)) + elif zone[1] == 'Right Side Center(RC)': + t = plt.text(19,24,string,horizontalalignment='center',verticalalignment='center',**kwargs) + t.set_bbox(dict(color='white', alpha=box_alpha)) + elif zone[1] == 'Left Side Center(LC)': + t = plt.text(-19,24,string,horizontalalignment='center',verticalalignment='center',**kwargs) + t.set_bbox(dict(color='white', alpha=box_alpha)) + if zone == ('Back Court Shot', 'Back Court(BC)') : + t = plt.text(0,39,string,horizontalalignment='center',verticalalignment='center',**kwargs) + t.set_bbox(dict(color='white', alpha=box_alpha)) + +def players_picture(player_id): + URL = "http://stats.nba.com/media/players/230x185/%d.png" %player_id + file = cStringIO.StringIO(urllib.urlopen(URL).read()) + return misc.imread(file) + +def grantland_shotchart(shotchart,leagueavergae,ax=None,short_three=False,fg_range=[-9,9]): + LA = leagueavergae.loc[:,'SHOT_ZONE_AREA':'FGM'].groupby(['SHOT_ZONE_RANGE','SHOT_ZONE_AREA']).sum() + LA['FGP'] = 1.0*LA['FGM']/LA['FGA'] + player = shotchart.groupby(['SHOT_ZONE_RANGE','SHOT_ZONE_AREA','SHOT_MADE_FLAG']).size().unstack(fill_value=0) + player['FGP'] = 1.0*player.loc[:,1]/player.sum(axis=1) + player_vs_league = (player.loc[:,'FGP'] - LA.loc[:,'FGP'])*100 + x,y = 1.0*shotchart.LOC_X.values/10, 1.0*shotchart.LOC_Y.values/10 + fig = plt.figure() + poly_hexbins = plt.hexbin(x,y, gridsize=35, extent=[-25,25,-6.25,50-6.25]) + counts = poly_hexbins.get_array() + verts = poly_hexbins.get_offsets() + plt.close(fig) +# plt.figure(figsize=figsize,facecolor='white') #(0,0.17,0.57) + if ax is None: + ax = plt.gca(xlim = [30,-30],ylim = [-10,40],xticks=[],yticks=[],aspect=1.0) + ax.text(0,-7,'By: Doingthedishes',color='black',horizontalalignment='center',fontsize=20,fontweight='bold') + court(ax,outer_lines=False,color='black',lw=4.0,direction='down',short_three=short_three); + ax.axis('off') + #nba.plot.zones() + #s=0.8747731368853422 + s = 0.85 + bins = np.concatenate([[-np.inf],np.linspace(fg_range[0],fg_range[1],200),[np.inf]]) + colors = [(0.66, 0.75, 0.66),(0.9,1.0,0.6), (0.8, 0, 0)] + cm = LinearSegmentedColormap.from_list('my_list', colors, N=len(bins)-1) + xy = s*np.array([np.cos(np.linspace(np.pi/6,np.pi*330/180,6)),np.sin(np.linspace(np.pi/6,np.pi*330/180,6))]).T + b = np.zeros((6,2)) + counts_norm = np.zeros_like(counts) + counts_norm[counts>=4] = 1 + counts_norm[(counts>=2) & (counts<4)] = 0.5 + counts_norm[(counts>=1) & (counts<2)] = 0.3 + patches=[] + colors=[] + for offc in xrange(verts.shape[0]): + if counts_norm[offc] != 0: + xc,yc = verts[offc][0],verts[offc][1] + b[:,0] = xy[:,0]*counts_norm[offc] + xc + b[:,1] = xy[:,1]*counts_norm[offc] + yc + if not short_three: + p_diff = player_vs_league.loc[shot_zone(xc,yc)] + else: + p_diff = player_vs_league.loc[shot_zone_short_three(xc,yc)] + inds = np.digitize(p_diff, bins,right=True)-1 + patches.append(Polygon(b)) + colors.append(inds) + + for i in range(5): + xc = 21-2*0.76*i + yc = -7 + b[:,0] = xy[:,0] + xc + b[:,1] = xy[:,1] + yc + patches.append(Polygon(b)) + colors.append(i*50) + plt.text(21,-8.5,'Cold',horizontalalignment='center',verticalalignment='center') + plt.text(21-2*0.76*4,-8.5,'Hot',horizontalalignment='center',verticalalignment='center') + + xc = -14.5 + yc = -7.0 + plt.text(xc,-8.5,'Less',horizontalalignment='center',verticalalignment='center') + b[:,0] = xy[:,0]*0.3 + xc + b[:,1] = xy[:,1]*0.3 + yc + patches.append(Polygon(b)) + colors.append(100) + xc = -16 + b[:,0] = xy[:,0]*0.50 + xc + b[:,1] = xy[:,1]*0.50 + yc + patches.append(Polygon(b)) + colors.append(100) + xc = -18 + b[:,0] = xy[:,0] + xc + b[:,1] = xy[:,1] + yc + plt.text(xc,-8.5,'More',horizontalalignment='center',verticalalignment='center') + + patches.append(Polygon(b)) + colors.append(100) + + p = PatchCollection(patches,cmap=cm,alpha=1) + p.set_array(np.array(colors)) + ax.add_collection(p) + p.set_clim([0, len(bins)-1]) + + pic = players_picture(shotchart.loc[0,'PLAYER_ID']) + ax.imshow(pic,extent=[15,25,30,37.8261]) + ax.text(20,29,shotchart.loc[0,'PLAYER_NAME'],fontsize=16,horizontalalignment='center',verticalalignment='center') + +def shot_zone(X,Y): + ''' + Uses shot coordinates x and y (in feet - divide by 10 if using the shotchart units) + and returns a tuple with the zone location + ''' + r = np.sqrt(X**2+Y**2) + a = np.arctan2(Y,X)*180.0/np.pi + if (Y<0) & (X > 0): + a = 0 + elif (Y<0) & (X < 0): + a = 180 + if r<=8: + z = ('Less Than 8 ft.','Center(C)') + elif (r>8) & (r<=16): + if a < 60: + z = ('8-16 ft.','Right Side(R)') + elif (a>=60) & (a<=120): + z = ('8-16 ft.','Center(C)') + else: + z = ('8-16 ft.','Left Side(L)') + elif (r>16) & (r<=23.75): + if a < 36: + z = ('16-24 ft.','Right Side(R)') + elif (a>=36) & (a<72): + z = ('16-24 ft.','Right Side Center(RC)') + elif (a>=72) & (a<=108): + z = ('16-24 ft.','Center(C)') + elif (a>108) & (a<144): + z = ('16-24 ft.','Left Side Center(LC)') + else: + z = ('16-24 ft.','Left Side(L)') + elif r>23.75: + if a < 72: + z = ('24+ ft.','Right Side Center(RC)') + elif (a>=72) & (a<=108): + z = ('24+ ft.','Center(C)') + else: + z = ('24+ ft.','Left Side Center(LC)') + if (np.abs(X)>=22): + if (X > 0) & (np.abs(Y)<8.75): + z = ('24+ ft.','Right Side(R)') + elif (X < 0) & (np.abs(Y)<8.75): + z = ('24+ ft.','Left Side(L)') + elif (X > 0) & (np.abs(Y)>=8.75): + z = ('24+ ft.','Right Side Center(RC)') + elif (X < 0) & (np.abs(Y)>=8.75): + z = ('24+ ft.','Left Side Center(LC)') + if Y >= 40: + z = ('Back Court Shot', 'Back Court(BC)') + return z + +def shot_zone_short_three(X,Y): + ''' + Uses shot coordinates x and y (in feet - divide by 10 if using the shotchart units) + and returns a tuple with the zone location + ''' + r = np.sqrt(X**2+Y**2) + a = np.arctan2(Y,X)*180.0/np.pi + if (Y<0) & (X > 0): + a = 0 + elif (Y<0) & (X < 0): + a = 180 + if r<=8: + z = ('Less Than 8 ft.','Center(C)') + elif (r>8) & (r<=16): + if a < 60: + z = ('8-16 ft.','Right Side(R)') + elif (a>=60) & (a<=120): + z = ('8-16 ft.','Center(C)') + else: + z = ('8-16 ft.','Left Side(L)') + elif (r>16) & (r<=22.0): + if a < 36: + z = ('16-24 ft.','Right Side(R)') + elif (a>=36) & (a<72): + z = ('16-24 ft.','Right Side Center(RC)') + elif (a>=72) & (a<=108): + z = ('16-24 ft.','Center(C)') + elif (a>108) & (a<144): + z = ('16-24 ft.','Left Side Center(LC)') + else: + z = ('16-24 ft.','Left Side(L)') + elif r>22.0: + if a < 72: + z = ('24+ ft.','Right Side Center(RC)') + elif (a>=72) & (a<=108): + z = ('24+ ft.','Center(C)') + else: + z = ('24+ ft.','Left Side Center(LC)') + if (np.abs(X)>=22): + if (X > 0) & (np.abs(Y)<8.75): + z = ('24+ ft.','Right Side(R)') + elif (X < 0) & (np.abs(Y)<8.75): + z = ('24+ ft.','Left Side(L)') + elif (X > 0) & (np.abs(Y)>=8.75): + z = ('24+ ft.','Right Side Center(RC)') + elif (X < 0) & (np.abs(Y)>=8.75): + z = ('24+ ft.','Left Side Center(LC)') + if Y >= 40: + z = ('Back Court Shot', 'Back Court(BC)') + return z + +def shot_heatmap(df,sigma = 1,log=False,player_pic=True,ax=None,cmap='jet'): + ''' + This function plots a heatmap based on the shot chart. + input - dataframe with x and y coordinates. + optional - log (default false) plots heatmap in log scale. + player (default true) adds player's picture and name if true + sigma - the sigma of the Gaussian kernel. In feet (default=1) + ''' + n,_,_ = np.histogram2d( 0.1*df['LOC_X'].values, 0.1*df['LOC_Y'].values,bins = [500, 500],range = [[-25,25],[-5.25,44.75]]) + KDE = ndimage.filters.gaussian_filter(n,10.0*sigma) + N = 1.0*KDE/np.sum(KDE) + if ax is None: + ax = plt.gca(xlim = [30,-30],ylim = [-7,43],xticks=[],yticks=[],aspect=1.0) + court(ax,outer_lines=True,color='black',lw=2.0,direction='down') + ax.axis('off') + if log: + ax.imshow(np.rot90(np.log10(N+1)),cmap=cmap,extent=[25.0, -25.0, -5.25, 44.75]) + else: + ax.imshow(np.rot90(N),cmap=cmap,extent=[25.0, -25.0, -5.25, 44.75]) + if player_pic: + player_id = df.PLAYER_ID.values[0] + pic = players_picture(player_id) + ax.imshow(pic,extent=[15,25,30,37.8261]) + ax.text(0,-7,'By: Doingthedishes',color='white',horizontalalignment='center',fontsize=20,fontweight='bold') + +def shot_scatter(df,player_pic=True,ax=None,noise=True,**kwargs): + ''' + Plotting scatter plot of shots. + input - dataframe with x and y coordinates. + optional - player_pic (default True) loads player picture. Use if dataframe is for a single player. + ax (default None) can pass plot axis. + noise (default True) adds some random scatter to the data for better visualization + other - any variables that can be passed into the scatter function (e.g. transperecy value) + ''' + if ax is None: + ax = plt.gca(xlim = [30,-30],ylim = [-7,43],xticks=[],yticks=[],aspect=1.0) + court(ax,outer_lines=True,color='black',lw=2.0,direction='down') + ax.axis('off') + if noise: + X = df.LOC_X.values + np.random.normal(loc=0.0, scale=1.5, size=len(df.LOC_X.values)) + Y = df.LOC_Y.values + np.random.normal(loc=0.0, scale=1.5, size=len(df.LOC_Y.values)) + else: + X = df.LOC_X.values + Y = df.LOC_Y.values + ax.scatter(-0.1*X,0.1*Y,**kwargs) + if player_pic: + name = df.PLAYER_NAME.values[0] + player_id = df.PLAYER_ID.values[0] + pic = players_picture(player_id) + ax.imshow(pic,extent=[15,25,30,37.8261]) + ax.text(20,29,name,fontsize=16,horizontalalignment='center',verticalalignment='center') + ax.text(0,-7,'By: Doingthedishes',color='black',horizontalalignment='center',fontsize=20,fontweight='bold') \ No newline at end of file diff --git a/NBAapi/plot_team.py b/NBAapi/plot_team.py new file mode 100644 index 0000000..0ebb80b --- /dev/null +++ b/NBAapi/plot_team.py @@ -0,0 +1,427 @@ +from matplotlib.patches import Circle, Rectangle, Arc +import matplotlib.pyplot as plt +import numpy as np +from scipy import misc +import urllib.request, urllib.parse, urllib.error, io +from matplotlib.patches import Polygon +from matplotlib.collections import PatchCollection +from matplotlib.colors import LinearSegmentedColormap + +def court(ax=None, color='black', lw=4, outer_lines=False,direction='up',short_three=False): + ''' + Plots an NBA court + outer_lines - accepts False or True. Plots the outer side lines of the court. + direction - 'up' or 'down' depending on how you like to view the court + Original function from http://savvastjortjoglou.com/ + ''' + # If an axes object isn't provided to plot onto, just get current one + if ax is None: + if direction=='up': + ax = plt.gca(xlim = [-30,30],ylim = [43,-7],xticks=[],yticks=[],aspect=1.0) + plt.text(22,44,'By: Doingthedishes',color='black',horizontalalignment='center',fontsize=20,fontweight='bold') + elif direction=='down': + ax = plt.gca(xlim = [30,-30],ylim = [-7,43],xticks=[],yticks=[],aspect=1.0) + plt.text(-22,-7,'By: Doingthedishes',color='black',horizontalalignment='center',fontsize=20,fontweight='bold') + else: + ax = plt.gca() + # Create the various parts of an NBA basketball court + + # Create the basketball hoop + # Diameter of a hoop is 1.5 + hoop = Circle((0, 0), radius=0.75, linewidth=lw/2, color=color, fill=False) + + # Create backboard + backboard = Rectangle((-3, -0.75), 6, -0.1, linewidth=lw, color=color) + + # The paint + # Create the outer box of the paint, width=16ft, height=19ft + outer_box = Rectangle((-8, -5.25), 16, 19, linewidth=lw, color=color, + fill=False) + # Create the inner box of the paint, widt=12ft, height=19ft + inner_box = Rectangle((-6, -5.25), 12, 19, linewidth=lw, color=color, + fill=False) + + # Create free throw top arc + top_free_throw = Arc((0, 13.75), 12, 12, theta1=0, theta2=180, + linewidth=lw, color=color, fill=False) + # Create free throw bottom arc + bottom_free_throw = Arc((0, 13.75), 12, 12, 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), 8, 8, theta1=0, theta2=180, linewidth=lw, + color=color) + + # Three point line + if not short_three: + corner_three_a = Rectangle((-22, -5.25), 0, np.sqrt(23.75**2-22.0**2)+5.25, linewidth=lw, + color=color) + corner_three_b = Rectangle((22, -5.25), 0, np.sqrt(23.75**2-22.0**2)+5.25, linewidth=lw, color=color) + # 3pt arc - center of arc will be the hoop, arc is 23'9" away from hoop + three_arc = Arc((0, 0), 47.5, 47.5, theta1=np.arccos(22/23.75)*180/np.pi, theta2=180.0-np.arccos(22/23.75)*180/np.pi, linewidth=lw, + color=color) + else: + corner_three_a = Rectangle((-22, -5.25), 0, 5.25, linewidth=lw, + color=color) + corner_three_b = Rectangle((22, -5.25), 0, 5.25, linewidth=lw, color=color) + # 3pt arc - center of arc will be the hoop, arc is 23'9" away from hoop + three_arc = Arc((0, 0), 44.0, 44.0, theta1=0, theta2=180, 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] + + if outer_lines: + # Draw the half court line, baseline and side out bound lines + outer_lines = Rectangle((-25, -5.25), 50, 46.75, linewidth=lw, + color=color, fill=False) + center_outer_arc = Arc((0, 41.25), 12, 12, theta1=180, theta2=0, + linewidth=lw, color=color) + center_inner_arc = Arc((0, 41.25), 4, 4, theta1=180, theta2=0, + linewidth=lw, color=color) + court_elements = court_elements + [outer_lines,center_outer_arc,center_inner_arc] + else: + ax.plot([-25,25],[-5.25,-5.25],linewidth=lw,color=color) + # Add the court elements onto the axes + for element in court_elements: + ax.add_patch(element) +# ax.axis('off') + return ax + +def zones(**kwargs): + ''' + Plots zones on the court as per NBA.com + the plot adds to the last plot used or starts a new figure + ''' + ax = plt.gca() + zone1 = Arc((0, 0), 16.0, 16.0,theta1 = -41.0,theta2 = 180.0+41.0,**kwargs) + zone2 = Arc((0, 0), 32.0, 32.0,theta1 = -19.2,theta2 = 180.0+19.2, **kwargs) + ax.add_patch(zone1) + ax.add_patch(zone2) + ang = 60.0 + ax.plot([np.cos(ang/180*np.pi)*8, np.cos(ang/180*np.pi)*16], + [np.sin(ang/180*np.pi)*8, np.sin(ang/180*np.pi)*16],**kwargs) + ang = 120.0 + ax.plot([np.cos(ang/180*np.pi)*8, np.cos(ang/180*np.pi)*16], + [np.sin(ang/180*np.pi)*8, np.sin(ang/180*np.pi)*16],**kwargs) + ax.plot([22,25],[14-5.25,14-5.25],**kwargs) + ax.plot([-22,-25],[14-5.25,14-5.25],**kwargs) + ang = 36.0 + ax.plot([np.cos(ang/180*np.pi)*16, np.cos(ang/180*np.pi)*23.75], + [np.sin(ang/180*np.pi)*16, np.sin(ang/180*np.pi)*23.75],**kwargs) + ang = 72.0 + ax.plot([np.cos(ang/180*np.pi)*16, np.cos(ang/180*np.pi)*24], + [np.sin(ang/180*np.pi)*16, np.sin(ang/180*np.pi)*24],**kwargs) + ang = 72.0+36.0 + ax.plot([np.cos(ang/180*np.pi)*16, np.cos(ang/180*np.pi)*24], + [np.sin(ang/180*np.pi)*16, np.sin(ang/180*np.pi)*24],**kwargs) + ang = 72.0*2 + ax.plot([np.cos(ang/180*np.pi)*16, np.cos(ang/180*np.pi)*23.75], + [np.sin(ang/180*np.pi)*16, np.sin(ang/180*np.pi)*23.75],**kwargs) + ang = 72.0 + ax.plot([np.cos(ang/180*np.pi)*24, np.cos(ang/180*np.pi)*41.25], + [np.sin(ang/180*np.pi)*24, 41.25],**kwargs) + ang = 72.0+36.0 + ax.plot([np.cos(ang/180*np.pi)*24, np.cos(ang/180*np.pi)*41.25], + [np.sin(ang/180*np.pi)*24, 41.25],**kwargs) + +def text_in_zone(string,zone,box_alpha = 0.75,**kwargs): + ''' + Adds text to zones. + Input: + string - text to be added + zone - specify zone (tuple) + Options: + box_alpha - string box transparency + ''' + if zone == ('Less Than 8 ft.','Center(C)') : + t = plt.text(0,2,string,horizontalalignment='center',verticalalignment='center',**kwargs) + t.set_bbox(dict(color='white', alpha=box_alpha)) + elif zone[0] == '8-16 ft.': + if zone[1] == 'Center(C)': + t = plt.text(0,12,string,horizontalalignment='center',verticalalignment='center',**kwargs) + t.set_bbox(dict(color='white', alpha=box_alpha)) + elif zone[1] == 'Right Side(R)' : + t = plt.text(12,0,string,horizontalalignment='center',verticalalignment='center',**kwargs) + t.set_bbox(dict(color='white', alpha=box_alpha)) + elif zone[1] == 'Left Side(L)': + t = plt.text(-12,0,string,horizontalalignment='center',verticalalignment='center',**kwargs) + t.set_bbox(dict(color='white', alpha=box_alpha)) + elif zone[0] == '16-24 ft.': + if zone[1] == 'Center(C)': + t = plt.text(0,20,string,horizontalalignment='center',verticalalignment='center',**kwargs) + t.set_bbox(dict(color='white', alpha=box_alpha)) + elif zone[1] == 'Right Side(R)': + t = plt.text(18,7.3,string,horizontalalignment='center',verticalalignment='center',**kwargs) + t.set_bbox(dict(color='white', alpha=box_alpha)) + elif zone[1] == 'Left Side(L)': + t = plt.text(-18,7.3,string,horizontalalignment='center',verticalalignment='center',**kwargs) + t.set_bbox(dict(color='white', alpha=box_alpha)) + elif zone[1] == 'Right Side Center(RC)': + t = plt.text(12,15.5,string,horizontalalignment='center',verticalalignment='center',**kwargs) + t.set_bbox(dict(color='white', alpha=box_alpha)) + elif zone[1] == 'Left Side Center(LC)': + t = plt.text(-12,15.5,string,horizontalalignment='center',verticalalignment='center',**kwargs) + t.set_bbox(dict(color='white', alpha=box_alpha)) + elif zone[0] == '24+ ft.': + if zone[1] == 'Center(C)': + t = plt.text(0,28,string,horizontalalignment='center',verticalalignment='center',**kwargs) + t.set_bbox(dict(color='white', alpha=box_alpha)) + elif zone[1] == 'Right Side(R)': + t = plt.text(27,0,string,horizontalalignment='center',verticalalignment='center',**kwargs) + t.set_bbox(dict(color='white', alpha=box_alpha)) + elif zone[1] == 'Left Side(L)': + t = plt.text(-27,0,string,horizontalalignment='center',verticalalignment='center',**kwargs) + t.set_bbox(dict(color='white', alpha=box_alpha)) + elif zone[1] == 'Right Side Center(RC)': + t = plt.text(19,24,string,horizontalalignment='center',verticalalignment='center',**kwargs) + t.set_bbox(dict(color='white', alpha=box_alpha)) + elif zone[1] == 'Left Side Center(LC)': + t = plt.text(-19,24,string,horizontalalignment='center',verticalalignment='center',**kwargs) + t.set_bbox(dict(color='white', alpha=box_alpha)) + if zone == ('Back Court Shot', 'Back Court(BC)') : + t = plt.text(0,39,string,horizontalalignment='center',verticalalignment='center',**kwargs) + t.set_bbox(dict(color='white', alpha=box_alpha)) + + +# def players_picture(player_id): +# URL = "http://stats.nba.com/media/players/230x185/%d.png" %player_id +# file = io.StringIO(urllib.request.urlopen(URL).read()) +# return misc.imread(file) + +def grantland_shotchart(shotchart,leagueavergae,ax=None,short_three=False,fg_range=[-9,9],figsize=(12,10),img=None): + LA = leagueavergae.loc[:,'SHOT_ZONE_AREA':'FGM'].groupby(['SHOT_ZONE_RANGE','SHOT_ZONE_AREA']).sum() + LA['FGP'] = 1.0*LA['FGM']/LA['FGA'] + team = shotchart.groupby(['SHOT_ZONE_RANGE','SHOT_ZONE_AREA','SHOT_MADE_FLAG']).size().unstack(fill_value=0) + team['FGP'] = 1.0*team.loc[:,1]/team.sum(axis=1) + team_vs_league = (team.loc[:,'FGP'] - LA.loc[:,'FGP'])*100 + x,y = 1.0*shotchart.LOC_X.values/10, 1.0*shotchart.LOC_Y.values/10 + fig = plt.figure() + poly_hexbins = plt.hexbin(x,y, gridsize=35, extent=[-25,25,-6.25,50-6.25]) + counts = poly_hexbins.get_array() + verts = poly_hexbins.get_offsets() + plt.close(fig) + plt.figure(figsize=figsize,facecolor='white') # (0,0.17,0.57) + if ax is None: + ax = plt.gca(xlim = [30,-30],ylim = [-10,40],xticks=[],yticks=[],aspect=1.0) + court(ax,outer_lines=False,color='black',lw=4.0,direction='down',short_three=short_three); + ax.axis('off') + #nba.plot.zones() + #s=0.8747731368853422 + s = 0.85 + bins = np.concatenate([[-np.inf],np.linspace(fg_range[0],fg_range[1],200),[np.inf]]) + colors = [(0.66, 0.75, 0.66),(0.9,1.0,0.6), (0.8, 0, 0)] + cm = LinearSegmentedColormap.from_list('my_list', colors, N=len(bins)-1) + xy = s*np.array([np.cos(np.linspace(np.pi/6,np.pi*330/180,6)),np.sin(np.linspace(np.pi/6,np.pi*330/180,6))]).T + b = np.zeros((6,2)) + counts_norm = np.zeros_like(counts) + counts_norm[counts>=4] = 1 + counts_norm[(counts>=2) & (counts<4)] = 0.5 + counts_norm[(counts>=1) & (counts<2)] = 0.3 + patches=[] + colors=[] + for offc in range(verts.shape[0]): + if counts_norm[offc] != 0: + xc,yc = verts[offc][0],verts[offc][1] + b[:,0] = xy[:,0]*counts_norm[offc] + xc + b[:,1] = xy[:,1]*counts_norm[offc] + yc + if not short_three: + p_diff = team_vs_league.loc[shot_zone(xc,yc)] + else: + p_diff = team_vs_league.loc[shot_zone_short_three(xc,yc)] + inds = np.digitize(p_diff, bins,right=True)-1 + patches.append(Polygon(b)) + colors.append(inds) + + for i in range(5): + xc = 21-2*0.76*i + yc = -7 + b[:,0] = xy[:,0] + xc + b[:,1] = xy[:,1] + yc + patches.append(Polygon(b)) + colors.append(i*50) + plt.text(21,-8.5,'Cold',horizontalalignment='center',verticalalignment='center') + plt.text(21-2*0.76*4,-8.5,'Hot',horizontalalignment='center',verticalalignment='center') + + xc = -14.5 + yc = -7.0 + plt.text(xc,-8.5,'Less',horizontalalignment='center',verticalalignment='center') + b[:,0] = xy[:,0]*0.3 + xc + b[:,1] = xy[:,1]*0.3 + yc + patches.append(Polygon(b)) + colors.append(100) + xc = -16 + b[:,0] = xy[:,0]*0.50 + xc + b[:,1] = xy[:,1]*0.50 + yc + patches.append(Polygon(b)) + colors.append(100) + xc = -18 + b[:,0] = xy[:,0] + xc + b[:,1] = xy[:,1] + yc + plt.text(xc,-8.5,'More',horizontalalignment='center',verticalalignment='center') + + patches.append(Polygon(b)) + colors.append(100) + + p = PatchCollection(patches,cmap=cm,alpha=1) + p.set_array(np.array(colors)) + ax.add_collection(p) + p.set_clim([0, len(bins)-1]) + + if img is not None: + ax.imshow(img,extent=[15,25,30,37.8261]) + +def shot_zone(X,Y): + ''' + Uses shot coordinates x and y (in feet - divide by 10 if using the shotchart units) + and returns a tuple with the zone location + ''' + r = np.sqrt(X**2+Y**2) + a = np.arctan2(Y,X)*180.0/np.pi + if (Y<0) & (X > 0): + a = 0 + elif (Y<0) & (X < 0): + a = 180 + if r<=8: + z = ('Less Than 8 ft.','Center(C)') + elif (r>8) & (r<=16): + if a < 60: + z = ('8-16 ft.','Right Side(R)') + elif (a>=60) & (a<=120): + z = ('8-16 ft.','Center(C)') + else: + z = ('8-16 ft.','Left Side(L)') + elif (r>16) & (r<=23.75): + if a < 36: + z = ('16-24 ft.','Right Side(R)') + elif (a>=36) & (a<72): + z = ('16-24 ft.','Right Side Center(RC)') + elif (a>=72) & (a<=108): + z = ('16-24 ft.','Center(C)') + elif (a>108) & (a<144): + z = ('16-24 ft.','Left Side Center(LC)') + else: + z = ('16-24 ft.','Left Side(L)') + elif r>23.75: + if a < 72: + z = ('24+ ft.','Right Side Center(RC)') + elif (a>=72) & (a<=108): + z = ('24+ ft.','Center(C)') + else: + z = ('24+ ft.','Left Side Center(LC)') + if (np.abs(X)>=22): + if (X > 0) & (np.abs(Y)<8.75): + z = ('24+ ft.','Right Side(R)') + elif (X < 0) & (np.abs(Y)<8.75): + z = ('24+ ft.','Left Side(L)') + elif (X > 0) & (np.abs(Y)>=8.75): + z = ('24+ ft.','Right Side Center(RC)') + elif (X < 0) & (np.abs(Y)>=8.75): + z = ('24+ ft.','Left Side Center(LC)') + if Y >= 40: + z = ('Back Court Shot', 'Back Court(BC)') + return z + +def shot_zone_short_three(X,Y): + ''' + Uses shot coordinates x and y (in feet - divide by 10 if using the shotchart units) + and returns a tuple with the zone location + ''' + r = np.sqrt(X**2+Y**2) + a = np.arctan2(Y,X)*180.0/np.pi + if (Y<0) & (X > 0): + a = 0 + elif (Y<0) & (X < 0): + a = 180 + if r<=8: + z = ('Less Than 8 ft.','Center(C)') + elif (r>8) & (r<=16): + if a < 60: + z = ('8-16 ft.','Right Side(R)') + elif (a>=60) & (a<=120): + z = ('8-16 ft.','Center(C)') + else: + z = ('8-16 ft.','Left Side(L)') + elif (r>16) & (r<=22.0): + if a < 36: + z = ('16-24 ft.','Right Side(R)') + elif (a>=36) & (a<72): + z = ('16-24 ft.','Right Side Center(RC)') + elif (a>=72) & (a<=108): + z = ('16-24 ft.','Center(C)') + elif (a>108) & (a<144): + z = ('16-24 ft.','Left Side Center(LC)') + else: + z = ('16-24 ft.','Left Side(L)') + elif r>22.0: + if a < 72: + z = ('24+ ft.','Right Side Center(RC)') + elif (a>=72) & (a<=108): + z = ('24+ ft.','Center(C)') + else: + z = ('24+ ft.','Left Side Center(LC)') + if (np.abs(X)>=22): + if (X > 0) & (np.abs(Y)<8.75): + z = ('24+ ft.','Right Side(R)') + elif (X < 0) & (np.abs(Y)<8.75): + z = ('24+ ft.','Left Side(L)') + elif (X > 0) & (np.abs(Y)>=8.75): + z = ('24+ ft.','Right Side Center(RC)') + elif (X < 0) & (np.abs(Y)>=8.75): + z = ('24+ ft.','Left Side Center(LC)') + if Y >= 40: + z = ('Back Court Shot', 'Back Court(BC)') + return z + +def shot_heatmap(df,sigma = 1,log=False,player_pic=True,ax=None,cmap='jet'): + ''' + This function plots a heatmap based on the shot chart. + input - dataframe with x and y coordinates. + optional - log (default false) plots heatmap in log scale. + player (default true) adds player's picture and name if true + sigma - the sigma of the Gaussian kernel. In feet (default=1) + ''' + n,_,_ = np.histogram2d( 0.1*df['LOC_X'].values, 0.1*df['LOC_Y'].values,bins = [500, 500],range = [[-25,25],[-5.25,44.75]]) + KDE = ndimage.filters.gaussian_filter(n,10.0*sigma) + N = 1.0*KDE/np.sum(KDE) + if ax is None: + ax = plt.gca(xlim = [30,-30],ylim = [-7,43],xticks=[],yticks=[],aspect=1.0) + court(ax,outer_lines=True,color='black',lw=2.0,direction='down') + ax.axis('off') + if log: + ax.imshow(np.rot90(np.log10(N+1)),cmap=cmap,extent=[25.0, -25.0, -5.25, 44.75]) + else: + ax.imshow(np.rot90(N),cmap=cmap,extent=[25.0, -25.0, -5.25, 44.75]) + if player_pic: + player_id = df.PLAYER_ID.values[0] + pic = players_picture(player_id) + ax.imshow(pic,extent=[15,25,30,37.8261]) + ax.text(0,-7,'By: Doingthedishes',color='white',horizontalalignment='center',fontsize=20,fontweight='bold') + +def shot_scatter(df,player_pic=True,ax=None,noise=True,**kwargs): + ''' + Plotting scatter plot of shots. + input - dataframe with x and y coordinates. + optional - player_pic (default True) loads player picture. Use if dataframe is for a single player. + ax (default None) can pass plot axis. + noise (default True) adds some random scatter to the data for better visualization + other - any variables that can be passed into the scatter function (e.g. transperecy value) + ''' + if ax is None: + ax = plt.gca(xlim = [30,-30],ylim = [-7,43],xticks=[],yticks=[],aspect=1.0) + court(ax,outer_lines=True,color='black',lw=2.0,direction='down') + ax.axis('off') + if noise: + X = df.LOC_X.values + np.random.normal(loc=0.0, scale=1.5, size=len(df.LOC_X.values)) + Y = df.LOC_Y.values + np.random.normal(loc=0.0, scale=1.5, size=len(df.LOC_Y.values)) + else: + X = df.LOC_X.values + Y = df.LOC_Y.values + ax.scatter(-0.1*X,0.1*Y,**kwargs) + if player_pic: + name = df.PLAYER_NAME.values[0] + player_id = df.PLAYER_ID.values[0] + pic = players_picture(player_id) + ax.imshow(pic,extent=[15,25,30,37.8261]) + ax.text(20,29,name,fontsize=16,horizontalalignment='center',verticalalignment='center') + ax.text(0,-7,'By: Doingthedishes',color='black',horizontalalignment='center',fontsize=20,fontweight='bold') diff --git a/NBAapi/team.py b/NBAapi/team.py index 785a3ba..469663e 100644 --- a/NBAapi/team.py +++ b/NBAapi/team.py @@ -81,42 +81,42 @@ def roster(teamid,season='2016-17',leagueid='00'): return pd.DataFrame(data['resultSets'][0]['rowSet'],columns=data['resultSets'][0]['headers']) def team_list(): - teams = pd.DataFrame({'ABBREVIATION': {0: u'ATL', - 1: u'BOS', - 2: u'CHA', - 3: u'CHI', - 4: u'CLE', - 5: u'DAL', - 6: u'DEN', - 7: u'DET', - 8: u'GSW', - 9: u'HOU', - 10: u'IND', - 11: u'LAC', - 12: u'LAL', - 13: u'MIA', - 14: u'MIL', - 15: u'MIN', - 16: u'NJN', - 17: u'NYK', - 18: u'ORL', - 19: u'PHI', - 20: u'PHX', - 21: u'POR', - 22: u'SAC', - 23: u'SAS', - 24: u'SEA', - 25: u'TOR', - 26: u'UTA', - 27: u'VAN', - 28: u'WAS', - 29: u'MEM', - 30: u'NOH', - 31: u'CHA', - 32: u'OKC', - 33: u'BKN', - 34: u'NOP', - 35: u'LAC'}, + teams = pd.DataFrame({'ABBREVIATION': {0: 'ATL', + 1: 'BOS', + 2: 'CHA', + 3: 'CHI', + 4: 'CLE', + 5: 'DAL', + 6: 'DEN', + 7: 'DET', + 8: 'GSW', + 9: 'HOU', + 10: 'IND', + 11: 'LAC', + 12: 'LAL', + 13: 'MIA', + 14: 'MIL', + 15: 'MIN', + 16: 'NJN', + 17: 'NYK', + 18: 'ORL', + 19: 'PHI', + 20: 'PHX', + 21: 'POR', + 22: 'SAC', + 23: 'SAS', + 24: 'SEA', + 25: 'TOR', + 26: 'UTA', + 27: 'VAN', + 28: 'WAS', + 29: 'MEM', + 30: 'NOH', + 31: 'CHA', + 32: 'OKC', + 33: 'BKN', + 34: 'NOP', + 35: 'LAC'}, 'TEAM_ID': {0: 1610612737, 1: 1610612738, 2: 1610612766, @@ -153,42 +153,42 @@ def team_list(): 33: 1610612751, 34: 1610612740, 35: 1610612746}, - 'TEAM_NAME': {0: u'Atlanta Hawks', - 1: u'Boston Celtics', - 2: u'Charlotte Hornets', - 3: u'Chicago Bulls', - 4: u'Cleveland Cavaliers', - 5: u'Dallas Mavericks', - 6: u'Denver Nuggets', - 7: u'Detroit Pistons', - 8: u'Golden State Warriors', - 9: u'Houston Rockets', - 10: u'Indiana Pacers', - 11: u'Los Angeles Clippers', - 12: u'Los Angeles Lakers', - 13: u'Miami Heat', - 14: u'Milwaukee Bucks', - 15: u'Minnesota Timberwolves', - 16: u'New Jersey Nets', - 17: u'New York Knicks', - 18: u'Orlando Magic', - 19: u'Philadelphia 76ers', - 20: u'Phoenix Suns', - 21: u'Portland Trail Blazers', - 22: u'Sacramento Kings', - 23: u'San Antonio Spurs', - 24: u'Seattle SuperSonics', - 25: u'Toronto Raptors', - 26: u'Utah Jazz', - 27: u'Vancouver Grizzlies', - 28: u'Washington Wizards', - 29: u'Memphis Grizzlies', - 30: u'New Orleans Hornets', - 31: u'Charlotte Bobcats', - 32: u'Oklahoma City Thunder', - 33: u'Brooklyn Nets', - 34: u'New Orleans Pelicans', - 35: u'LA Clippers'}, + 'TEAM_NAME': {0: 'Atlanta Hawks', + 1: 'Boston Celtics', + 2: 'Charlotte Hornets', + 3: 'Chicago Bulls', + 4: 'Cleveland Cavaliers', + 5: 'Dallas Mavericks', + 6: 'Denver Nuggets', + 7: 'Detroit Pistons', + 8: 'Golden State Warriors', + 9: 'Houston Rockets', + 10: 'Indiana Pacers', + 11: 'Los Angeles Clippers', + 12: 'Los Angeles Lakers', + 13: 'Miami Heat', + 14: 'Milwaukee Bucks', + 15: 'Minnesota Timberwolves', + 16: 'New Jersey Nets', + 17: 'New York Knicks', + 18: 'Orlando Magic', + 19: 'Philadelphia 76ers', + 20: 'Phoenix Suns', + 21: 'Portland Trail Blazers', + 22: 'Sacramento Kings', + 23: 'San Antonio Spurs', + 24: 'Seattle SuperSonics', + 25: 'Toronto Raptors', + 26: 'Utah Jazz', + 27: 'Vancouver Grizzlies', + 28: 'Washington Wizards', + 29: 'Memphis Grizzlies', + 30: 'New Orleans Hornets', + 31: 'Charlotte Bobcats', + 32: 'Oklahoma City Thunder', + 33: 'Brooklyn Nets', + 34: 'New Orleans Pelicans', + 35: 'LA Clippers'}, 'color': {0: (0.8862745098039215, 0.21568627450980393, 0.24313725490196078), 1: (0.0, 0.4470588235294118, 0.2235294117647059), 2: (0.0, 0.5176470588235295, 0.5568627450980392), diff --git a/NBAapi/team.py.bak b/NBAapi/team.py.bak new file mode 100644 index 0000000..785a3ba --- /dev/null +++ b/NBAapi/team.py.bak @@ -0,0 +1,268 @@ +import requests +import pandas as pd + +def stats(conference='',datefrom='',dateto='',div='',gamescope='',gamesegment='',lastngames=0, + leagueid='00',location='',measuretype='Base',month=0,oppenentteamid=0,outcome='', + poround=0,paceadjust='N',permode='PerGame',period=0,playerexperience='', + playerposition='',plusminus='N',rank='N', + season='2015-16',seasonsegment='',seasontype='Regular Season',shotclockrange='', + starterbench='',teamid=0,vsconference='',vsdivision=''): + url = 'http://stats.nba.com/stats/leaguedashteamstats?' + api_param = { + 'Conference' :conference, + 'DateFrom' : datefrom, + 'DateTo' : dateto, + 'Division' : div, + 'GameScope' : gamescope, + 'GameSegment' : gamesegment, + 'LastNGames' : lastngames, + 'LeagueID' : leagueid, + 'Location' : location, + 'MeasureType' : measuretype, + 'Month' : month, + 'OpponentTeamID' : oppenentteamid, + 'Outcome' : outcome, + 'PORound' : poround, + 'PaceAdjust' : paceadjust, + 'PerMode' : permode, + 'Period' : period, + 'PlayerExperience' : playerexperience, + 'PlayerPosition' : playerposition, + 'PlusMinus' : plusminus, + 'Rank' : rank, + 'Season' : season, + 'SeasonSegment' : seasonsegment, + 'SeasonType' : seasontype, + 'ShotClockRange' : shotclockrange, + 'StarterBench' : starterbench, + 'TeamID' : teamid, + 'VsConference': vsconference, + 'VsDivision' : vsdivision + } + u_a = "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/48.0.2564.82 Safari/537.36" + response = requests.get(url,params=api_param,headers={"USER-AGENT":u_a}) + data = response.json() + return pd.DataFrame(data['resultSets'][0]['rowSet'],columns=data['resultSets'][0]['headers']) + +def gamelog(teamid,seasontype='Regular Season',leagueid='00',season='2016-17'): + url = 'http://stats.nba.com/stats/teamgamelog?' + api_param = { + 'SeasonType' : seasontype, + 'LeagueID' : leagueid, + 'Season' : season, + 'teamID' : teamid, + } + u_a = "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/48.0.2564.82 Safari/537.36" + response = requests.get(url,params=api_param,headers={"USER-AGENT":u_a}) + data = response.json() + return pd.DataFrame(data['resultSets'][0]['rowSet'],columns=data['resultSets'][0]['headers']) + + +def commonteamyears(leagueid='00'): + url = 'http://stats.nba.com/stats/commonteamyears?' + api_param = { + 'LeagueID' : leagueid, + } + u_a = "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/48.0.2564.82 Safari/537.36" + response = requests.get(url,params=api_param,headers={"USER-AGENT":u_a}) + data = response.json() + return pd.DataFrame(data['resultSets'][0]['rowSet'],columns=data['resultSets'][0]['headers']) + +def roster(teamid,season='2016-17',leagueid='00'): + url = 'http://stats.nba.com/stats/commonteamroster?' + api_param = { + 'LeagueID' : leagueid, + 'Season' : season, + 'teamID' : teamid, + } + u_a = "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/48.0.2564.82 Safari/537.36" + response = requests.get(url,params=api_param,headers={"USER-AGENT":u_a}) + data = response.json() + return pd.DataFrame(data['resultSets'][0]['rowSet'],columns=data['resultSets'][0]['headers']) + +def team_list(): + teams = pd.DataFrame({'ABBREVIATION': {0: u'ATL', + 1: u'BOS', + 2: u'CHA', + 3: u'CHI', + 4: u'CLE', + 5: u'DAL', + 6: u'DEN', + 7: u'DET', + 8: u'GSW', + 9: u'HOU', + 10: u'IND', + 11: u'LAC', + 12: u'LAL', + 13: u'MIA', + 14: u'MIL', + 15: u'MIN', + 16: u'NJN', + 17: u'NYK', + 18: u'ORL', + 19: u'PHI', + 20: u'PHX', + 21: u'POR', + 22: u'SAC', + 23: u'SAS', + 24: u'SEA', + 25: u'TOR', + 26: u'UTA', + 27: u'VAN', + 28: u'WAS', + 29: u'MEM', + 30: u'NOH', + 31: u'CHA', + 32: u'OKC', + 33: u'BKN', + 34: u'NOP', + 35: u'LAC'}, + 'TEAM_ID': {0: 1610612737, + 1: 1610612738, + 2: 1610612766, + 3: 1610612741, + 4: 1610612739, + 5: 1610612742, + 6: 1610612743, + 7: 1610612765, + 8: 1610612744, + 9: 1610612745, + 10: 1610612754, + 11: 1610612746, + 12: 1610612747, + 13: 1610612748, + 14: 1610612749, + 15: 1610612750, + 16: 1610612751, + 17: 1610612752, + 18: 1610612753, + 19: 1610612755, + 20: 1610612756, + 21: 1610612757, + 22: 1610612758, + 23: 1610612759, + 24: 1610612760, + 25: 1610612761, + 26: 1610612762, + 27: 1610612763, + 28: 1610612764, + 29: 1610612763, + 30: 1610612740, + 31: 1610612766, + 32: 1610612760, + 33: 1610612751, + 34: 1610612740, + 35: 1610612746}, + 'TEAM_NAME': {0: u'Atlanta Hawks', + 1: u'Boston Celtics', + 2: u'Charlotte Hornets', + 3: u'Chicago Bulls', + 4: u'Cleveland Cavaliers', + 5: u'Dallas Mavericks', + 6: u'Denver Nuggets', + 7: u'Detroit Pistons', + 8: u'Golden State Warriors', + 9: u'Houston Rockets', + 10: u'Indiana Pacers', + 11: u'Los Angeles Clippers', + 12: u'Los Angeles Lakers', + 13: u'Miami Heat', + 14: u'Milwaukee Bucks', + 15: u'Minnesota Timberwolves', + 16: u'New Jersey Nets', + 17: u'New York Knicks', + 18: u'Orlando Magic', + 19: u'Philadelphia 76ers', + 20: u'Phoenix Suns', + 21: u'Portland Trail Blazers', + 22: u'Sacramento Kings', + 23: u'San Antonio Spurs', + 24: u'Seattle SuperSonics', + 25: u'Toronto Raptors', + 26: u'Utah Jazz', + 27: u'Vancouver Grizzlies', + 28: u'Washington Wizards', + 29: u'Memphis Grizzlies', + 30: u'New Orleans Hornets', + 31: u'Charlotte Bobcats', + 32: u'Oklahoma City Thunder', + 33: u'Brooklyn Nets', + 34: u'New Orleans Pelicans', + 35: u'LA Clippers'}, + 'color': {0: (0.8862745098039215, 0.21568627450980393, 0.24313725490196078), + 1: (0.0, 0.4470588235294118, 0.2235294117647059), + 2: (0.0, 0.5176470588235295, 0.5568627450980392), + 3: (0.7764705882352941, 0.0, 0.2), + 4: (0.5254901960784314, 0.0, 0.2196078431372549), + 5: (0.0, 0.38823529411764707, 0.6862745098039216), + 6: (1.0, 0.7803921568627451, 0.17254901960784313), + 7: (0.0, 0.23921568627450981, 0.6470588235294118), + 8: (0.0, 0.40784313725490196, 0.7019607843137254), + 9: (0.7764705882352941, 0.0, 0.2), + 10: (1.0, 0.7215686274509804, 0.10980392156862745), + 11: (0.0, 0.3333333333333333, 0.6039215686274509), + 12: (0.996078431372549, 0.6627450980392157, 0.15294117647058825), + 13: (0.596078431372549, 0.0, 0.1803921568627451), + 14: (0.17254901960784313, 0.3215686274509804, 0.20392156862745098), + 15: (0.0, 0.24705882352941178, 0.4392156862745098), + 16: (0.0,0.0,0.0), + 17: (0.9529411764705882, 0.3411764705882353, 0.12156862745098039), + 18: (0.0, 0.4196078431372549, 0.7176470588235294), + 19: (0.0, 0.4, 0.7137254901960784), + 20: (0.8941176470588236, 0.37254901960784315, 0.12156862745098039), + 21: (0.8705882352941177, 0.12549019607843137, 0.19607843137254902), + 22: (0.32941176470588235, 0.1803921568627451, 0.5686274509803921), + 23: (0.7137254901960784, 0.7490196078431373, 0.7490196078431373), + 24: (0.0,0.0,0.0), + 25: (0.7764705882352941, 0.0, 0.2), + 26: (0.0, 0.16470588235294117, 0.3607843137254902), + 27: (0.0,0.0,0.0), + 28: (0.0, 0.16470588235294117, 0.3568627450980392), + 29: (0.09411764705882353, 0.16470588235294117, 0.2823529411764706), + 30: (0.0,0.0,0.0), + 31: (0.0, 0.5176470588235295, 0.5568627450980392), + 32: (0.0, 0.49019607843137253, 0.7647058823529411), + 33: (0.0, 0.0, 0.0), + 34: (0.0, 0.16862745098039217, 0.3607843137254902), + 35: (0.0, 0.3333333333333333, 0.6039215686274509)}, + 'conference': {0: 'Eastern', + 1: 'Eastern', + 2: 'Eastern', + 3: 'Eastern', + 4: 'Eastern', + 5: 'Western', + 6: 'Western', + 7: 'Eastern', + 8: 'Western', + 9: 'Western', + 10: 'Eastern', + 11: 'Western', + 12: 'Western', + 13: 'Eastern', + 14: 'Eastern', + 15: 'Western', + 16: 'Eastern', + 17: 'Eastern', + 18: 'Eastern', + 19: 'Eastern', + 20: 'Western', + 21: 'Western', + 22: 'Western', + 23: 'Western', + 24: 'Western', + 25: 'Eastern', + 26: 'Western', + 27: 'Western', + 28: 'Eastern', + 29: 'Western', + 30: 'Western', + 31: 'Eastern', + 32: 'Western', + 33: 'Eastern', + 34: 'Western', + 35: 'Western'}}) + return teams + + + + \ No newline at end of file From 775841aedd0208ce25b882a2501d8b034fbffeeb Mon Sep 17 00:00:00 2001 From: Maxime Date: Fri, 13 Dec 2019 23:37:28 +0100 Subject: [PATCH 2/5] Change some default values Add a referer --- NBAapi/shotchart.py | 77 +++++++++++++++++++++++---------------------- 1 file changed, 39 insertions(+), 38 deletions(-) diff --git a/NBAapi/shotchart.py b/NBAapi/shotchart.py index 301928c..ad53606 100644 --- a/NBAapi/shotchart.py +++ b/NBAapi/shotchart.py @@ -1,50 +1,51 @@ -#%% +# %% import requests import pandas as pd -import numpy as np -def shotchartdetail(leagueid='00',season='2016-17',seasontype='Regular Season',teamid=0, - playerid=0,gameid='',outcome='',location='',month=0, - seasonseg='',datefrom='',dateto='',oppteamid=0,vsconf='', - vsdiv='',pos='',gameseg='',per=0,lastngames=0,aheadbehind='', - contextmeasure='FGM',clutchtime='',rookieyear=''): + +def shotchartdetail(leagueid='00', season='2019-20', seasontype='Regular Season', teamid=0, + playerid=0, gameid='', outcome='', location='', month=0, + seasonseg='', datefrom='', dateto='', oppteamid=0, vsconf='', + vsdiv='', pos='', gameseg='', per=0, lastngames=0, aheadbehind='', + contextmeasure='FGA', clutchtime='', rookieyear=''): ''' Access to NBA API - http://stats.nba.com/stats/shotchartdetail Returns the shotchart requested and the leagueaverage Example: shot_data,leagueaverage = shotchartdetail(season='2016-17') ''' - url = 'http://stats.nba.com/stats/shotchartdetail?' + url = 'https://stats.nba.com/stats/shotchartdetail?' api_param = { - 'LeagueID': leagueid, - 'Season' : season, - 'SeasonType' : seasontype, - 'TeamID' : teamid, - 'PlayerID' : playerid, - 'GameID' : gameid, - 'Outcome' : outcome, - 'Location' : location, - 'Month' : month, - 'SeasonSegment' : seasonseg, - 'DateFrom' : datefrom, - 'DateTo' : dateto, - 'OpponentTeamID' : oppteamid, - 'VsConference' : vsconf, - 'VsDivision' : vsdiv, - 'PlayerPosition' : pos, - 'GameSegment' : gameseg, - 'Period' : per, - 'LastNGames' : lastngames, - 'AheadBehind' : aheadbehind, - 'ContextMeasure' : contextmeasure, - 'ClutchTime' : clutchtime, - 'RookieYear' : rookieyear - } + 'LeagueID': leagueid, + 'Season': season, + 'SeasonType': seasontype, + 'TeamID': teamid, + 'PlayerID': playerid, + 'GameID': gameid, + 'Outcome': outcome, + 'Location': location, + 'Month': month, + 'SeasonSegment': seasonseg, + 'DateFrom': datefrom, + 'DateTo': dateto, + 'OpponentTeamID': oppteamid, + 'VsConference': vsconf, + 'VsDivision': vsdiv, + 'PlayerPosition': pos, + 'GameSegment': gameseg, + 'Period': per, + 'LastNGames': lastngames, + 'AheadBehind': aheadbehind, + 'ContextMeasure': contextmeasure, + 'ClutchTime': clutchtime, + 'RookieYear': rookieyear, + } u_a = "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/48.0.2564.82 Safari/537.36" - response = requests.get(url,params=api_param,headers={"USER-AGENT":u_a}) + response = requests.get(url, params=api_param, headers={ + "USER-AGENT": u_a, + "Referer": "https://stats.nba.com/events/" + }) data = response.json() - Shot_Chart_Detail = pd.DataFrame(data['resultSets'][0]['rowSet'],columns=data['resultSets'][0]['headers']) - LeagueAverage = pd.DataFrame(data['resultSets'][1]['rowSet'],columns=data['resultSets'][1]['headers']) - return Shot_Chart_Detail,LeagueAverage - - + shot_chart_detail = pd.DataFrame(data['resultSets'][0]['rowSet'], columns=data['resultSets'][0]['headers']) + league_average = pd.DataFrame(data['resultSets'][1]['rowSet'], columns=data['resultSets'][1]['headers']) + return shot_chart_detail, league_average \ No newline at end of file From 95063c80ecd9e0f58303af2b333c312267ef0c8a Mon Sep 17 00:00:00 2001 From: Maxime Date: Fri, 13 Dec 2019 23:48:57 +0100 Subject: [PATCH 3/5] Rename some variables to match python usual case --- NBAapi/shotchart.py | 49 ++++++++++++++++++++++++--------------------- 1 file changed, 26 insertions(+), 23 deletions(-) diff --git a/NBAapi/shotchart.py b/NBAapi/shotchart.py index ad53606..d989d2a 100644 --- a/NBAapi/shotchart.py +++ b/NBAapi/shotchart.py @@ -3,11 +3,11 @@ import pandas as pd -def shotchartdetail(leagueid='00', season='2019-20', seasontype='Regular Season', teamid=0, - playerid=0, gameid='', outcome='', location='', month=0, - seasonseg='', datefrom='', dateto='', oppteamid=0, vsconf='', - vsdiv='', pos='', gameseg='', per=0, lastngames=0, aheadbehind='', - contextmeasure='FGA', clutchtime='', rookieyear=''): +def shotchartdetail(league_id='00', season='2019-20', season_type='Regular Season', team_id=0, + player_id=0, game_id='', outcome='', location='', month=0, + season_seg='', date_from='', date_to='', opp_team_id=0, vs_conf='', + vs_div='', pos='', game_seg='', per=0, last_n_games=0, ahead_behind='', + context_measure='FGM', clutch_time='', rookie_year=''): ''' Access to NBA API - http://stats.nba.com/stats/shotchartdetail Returns the shotchart requested and the leagueaverage @@ -16,29 +16,29 @@ def shotchartdetail(leagueid='00', season='2019-20', seasontype='Regular Season' ''' url = 'https://stats.nba.com/stats/shotchartdetail?' api_param = { - 'LeagueID': leagueid, + 'LeagueID': league_id, 'Season': season, - 'SeasonType': seasontype, - 'TeamID': teamid, - 'PlayerID': playerid, - 'GameID': gameid, + 'SeasonType': season_type, + 'TeamID': team_id, + 'PlayerID': player_id, + 'GameID': game_id, 'Outcome': outcome, 'Location': location, 'Month': month, - 'SeasonSegment': seasonseg, - 'DateFrom': datefrom, - 'DateTo': dateto, - 'OpponentTeamID': oppteamid, - 'VsConference': vsconf, - 'VsDivision': vsdiv, + 'SeasonSegment': season_seg, + 'DateFrom': date_from, + 'DateTo': date_to, + 'OpponentTeamID': opp_team_id, + 'VsConference': vs_conf, + 'VsDivision': vs_div, 'PlayerPosition': pos, - 'GameSegment': gameseg, + 'GameSegment': game_seg, 'Period': per, - 'LastNGames': lastngames, - 'AheadBehind': aheadbehind, - 'ContextMeasure': contextmeasure, - 'ClutchTime': clutchtime, - 'RookieYear': rookieyear, + 'LastNGames': last_n_games, + 'AheadBehind': ahead_behind, + 'ContextMeasure': context_measure, + 'ClutchTime': clutch_time, + 'RookieYear': rookie_year, } u_a = "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/48.0.2564.82 Safari/537.36" response = requests.get(url, params=api_param, headers={ @@ -48,4 +48,7 @@ def shotchartdetail(leagueid='00', season='2019-20', seasontype='Regular Season' data = response.json() shot_chart_detail = pd.DataFrame(data['resultSets'][0]['rowSet'], columns=data['resultSets'][0]['headers']) league_average = pd.DataFrame(data['resultSets'][1]['rowSet'], columns=data['resultSets'][1]['headers']) - return shot_chart_detail, league_average \ No newline at end of file + return shot_chart_detail, league_average + +westbrook_chart, avg = shotchartdetail(player_id=203507) +print(westbrook_chart) \ No newline at end of file From 4f1505aa89b9aa8958ca00d1222f04bd11464bee Mon Sep 17 00:00:00 2001 From: Maxime Date: Fri, 13 Dec 2019 23:58:23 +0100 Subject: [PATCH 4/5] Create default headers and use across all functions --- NBAapi/__init__.py | 17 ++++------------- NBAapi/credentials/__init__.py | 4 ++++ NBAapi/draft.py | 29 +++++++++++++++-------------- NBAapi/league.py | 7 ++++--- NBAapi/player.py | 15 ++++++++------- NBAapi/shotchart.py | 12 +++--------- NBAapi/team.py | 9 +++++---- NBAapi/team.py.bak | 8 ++++---- 8 files changed, 47 insertions(+), 54 deletions(-) create mode 100644 NBAapi/credentials/__init__.py diff --git a/NBAapi/__init__.py b/NBAapi/__init__.py index 41e8198..387de55 100644 --- a/NBAapi/__init__.py +++ b/NBAapi/__init__.py @@ -1,21 +1,12 @@ -#import numpy as np +# import numpy as np from . import plot from . import plot_team from . import player -from . import team +from . import team from . import league -from . import shotchart +from . import shotchart from . import draft import pkg_resources -DATA_PATH = pkg_resources.resource_filename('NBAapi', 'data/') - - - - - - - - - +DATA_PATH = pkg_resources.resource_filename('NBAapi', 'data/') \ No newline at end of file diff --git a/NBAapi/credentials/__init__.py b/NBAapi/credentials/__init__.py new file mode 100644 index 0000000..5670dd9 --- /dev/null +++ b/NBAapi/credentials/__init__.py @@ -0,0 +1,4 @@ +DEFAULT_HEADERS = { + "USER-AGENT": "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/48.0.2564.82 Safari/537.36", + "Referer": "https://stats.nba.com/events/" +} diff --git a/NBAapi/draft.py b/NBAapi/draft.py index ef98804..8f7a4e5 100644 --- a/NBAapi/draft.py +++ b/NBAapi/draft.py @@ -1,20 +1,21 @@ import requests import pandas as pd +from NBAapi.credentials import DEFAULT_HEADERS -def drafthistory(college='',leagueid='00',overallpick='',roundnum='',roundpick='', - season='',teamid=0,topx=''): - url = 'http://stats.nba.com/stats/drafthistory?' +def drafthistory(college='', leagueid='00', overallpick='', roundnum='', roundpick='', + season='', teamid=0, topx=''): + url = 'https://stats.nba.com/stats/drafthistory?' api_param = { - 'College' : college, - 'LeagueID' : leagueid, - 'OverallPick' : overallpick, - 'RoundNum' : roundnum, - 'RoundPick' : roundpick, - 'Season' : season, - 'TeamID' : teamid, - 'TopX' : topx + 'College': college, + 'LeagueID': leagueid, + 'OverallPick': overallpick, + 'RoundNum': roundnum, + 'RoundPick': roundpick, + 'Season': season, + 'TeamID': teamid, + 'TopX': topx } - u_a = "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/48.0.2564.82 Safari/537.36" - response = requests.get(url,params=api_param,headers={"USER-AGENT":u_a}) + response = requests.get(url, params=api_param, + headers=DEFAULT_HEADERS) data = response.json() - return pd.DataFrame(data['resultSets'][0]['rowSet'],columns=data['resultSets'][0]['headers']) \ No newline at end of file + return pd.DataFrame(data['resultSets'][0]['rowSet'], columns=data['resultSets'][0]['headers']) diff --git a/NBAapi/league.py b/NBAapi/league.py index 61862a1..8837a44 100644 --- a/NBAapi/league.py +++ b/NBAapi/league.py @@ -1,5 +1,6 @@ import requests import pandas as pd +from NBAapi.credentials import DEFAULT_HEADERS def gamelog(counter = 1000,datefrom='',dateto='',direction='DESC',leagueid='00', playerorteam='T',season='2015-16',seasontype='Regular Season',sorter='PTS'): @@ -16,7 +17,7 @@ def gamelog(counter = 1000,datefrom='',dateto='',direction='DESC',leagueid='00', 'Sorter' : sorter, } u_a = "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/48.0.2564.82 Safari/537.36" - response = requests.get(url,params=api_param,headers={"USER-AGENT":u_a}) + response = requests.get(url,params=api_param,headers=DEFAULT_HEADERS) data = response.json() return pd.DataFrame(data['resultSets'][0]['rowSet'],columns=data['resultSets'][0]['headers']) @@ -64,7 +65,7 @@ def hustlestatsteam(College='',Conference='',Country='',DateFrom='',DateTo='',Di 'Weight' : Weight } u_a = "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/48.0.2564.82 Safari/537.36" - response = requests.get(url,params=api_param,headers={"USER-AGENT":u_a},timeout=1.0) + response = requests.get(url,params=api_param,headers=DEFAULT_HEADERS,timeout=1.0) data = response.json() return pd.DataFrame(data['resultSets'][0]['rowSet'],columns=data['resultSets'][0]['headers']) @@ -76,6 +77,6 @@ def playbyplayv2(gameid,startperiod=0,endperiod=0): 'GameID' : gameid, } u_a = "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/48.0.2564.82 Safari/537.36" - response = requests.get(url,params=api_param,headers={"USER-AGENT":u_a},timeout=1.0) + response = requests.get(url,params=api_param,headers=DEFAULT_HEADERS,timeout=1.0) data = response.json() return pd.DataFrame(data['resultSets'][0]['rowSet'],columns=data['resultSets'][0]['headers']) \ No newline at end of file diff --git a/NBAapi/player.py b/NBAapi/player.py index 5d6521c..577b20c 100644 --- a/NBAapi/player.py +++ b/NBAapi/player.py @@ -1,5 +1,6 @@ import requests import pandas as pd +from NBAapi.credentials import DEFAULT_HEADERS def stats(college='',conference='',country='',datefrom='',dateto='',division='', draftpick='',draftyear='',gamescope='',gamesegment='',height='', @@ -47,7 +48,7 @@ def stats(college='',conference='',country='',datefrom='',dateto='',division='', 'Weight' : weight, } u_a = "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/48.0.2564.82 Safari/537.36" - response = requests.get(url,params=api_param,headers={"USER-AGENT":u_a}) + response = requests.get(url,params=api_param,headers=DEFAULT_HEADERS) data = response.json() return pd.DataFrame(data['resultSets'][0]['rowSet'],columns=data['resultSets'][0]['headers']) @@ -93,7 +94,7 @@ def biostats(college='', conference='', country='', datefrom='', dateto='', divi 'Weight' : weight, } u_a = "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/48.0.2564.82 Safari/537.36" - response = requests.get(url,params=api_param,headers={"USER-AGENT":u_a}) + response = requests.get(url,params=api_param,headers=DEFAULT_HEADERS) data = response.json() return pd.DataFrame(data['resultSets'][0]['rowSet'],columns=data['resultSets'][0]['headers']) @@ -105,7 +106,7 @@ def commonallplayers(currentseason=0,leagueid='00',season='2015-16'): 'Season' : season, } u_a = "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/48.0.2564.82 Safari/537.36" - response = requests.get(url,params=api_param,headers={"USER-AGENT":u_a}) + response = requests.get(url,params=api_param,headers=DEFAULT_HEADERS) data = response.json() return pd.DataFrame(data['resultSets'][0]['rowSet'],columns=data['resultSets'][0]['headers']) @@ -117,7 +118,7 @@ def careerstats(playerid,permode='PerGame',leagueid='00'): 'PlayerID' : playerid, } u_a = "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/48.0.2564.82 Safari/537.36" - response = requests.get(url,params=api_param,headers={"USER-AGENT":u_a}) + response = requests.get(url,params=api_param,headers=DEFAULT_HEADERS) data = response.json() return pd.DataFrame(data['resultSets'][0]['rowSet'],columns=data['resultSets'][0]['headers']) @@ -132,7 +133,7 @@ def gamelog(playerid,DateFrom='',DateTo='',LeagueID='00',Season='2016-17',Season 'SeasonType' : SeasonType, } u_a = "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/48.0.2564.82 Safari/537.36" - response = requests.get(url,params=api_param,headers={"USER-AGENT":u_a}) + response = requests.get(url,params=api_param,headers=DEFAULT_HEADERS) data = response.json() return pd.DataFrame(data['resultSets'][0]['rowSet'],columns=data['resultSets'][0]['headers']) @@ -163,7 +164,7 @@ def dashptreb(playerid,DateFrom='',DateTo='',GameSegment='',LastNGames='0',Leagu 'VsDivision' : VsDivision, } u_a = "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/48.0.2564.82 Safari/537.36" - response = requests.get(url,params=api_param,headers={"USER-AGENT":u_a}) + response = requests.get(url,params=api_param,headers=DEFAULT_HEADERS) data = response.json() return pd.DataFrame(data['resultSets'][0]['rowSet'],columns=data['resultSets'][0]['headers']) @@ -209,7 +210,7 @@ def hustlestatsplayer(College='',Conference='',Country='',DateFrom='',DateTo='', 'Weight' : Weight, } u_a = "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/48.0.2564.82 Safari/537.36" - response = requests.get(url,params=api_param,headers={"USER-AGENT":u_a}) + response = requests.get(url,params=api_param,headers=DEFAULT_HEADERS) data = response.json() return pd.DataFrame(data['resultSets'][0]['rowSet'],columns=data['resultSets'][0]['headers']) diff --git a/NBAapi/shotchart.py b/NBAapi/shotchart.py index d989d2a..2631b66 100644 --- a/NBAapi/shotchart.py +++ b/NBAapi/shotchart.py @@ -1,6 +1,7 @@ # %% import requests import pandas as pd +from NBAapi.credentials import DEFAULT_HEADERS def shotchartdetail(league_id='00', season='2019-20', season_type='Regular Season', team_id=0, @@ -14,7 +15,7 @@ def shotchartdetail(league_id='00', season='2019-20', season_type='Regular Seaso Example: shot_data,leagueaverage = shotchartdetail(season='2016-17') ''' - url = 'https://stats.nba.com/stats/shotchartdetail?' + url = 'http://stats.nba.com/stats/shotchartdetail?' api_param = { 'LeagueID': league_id, 'Season': season, @@ -40,15 +41,8 @@ def shotchartdetail(league_id='00', season='2019-20', season_type='Regular Seaso 'ClutchTime': clutch_time, 'RookieYear': rookie_year, } - u_a = "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/48.0.2564.82 Safari/537.36" - response = requests.get(url, params=api_param, headers={ - "USER-AGENT": u_a, - "Referer": "https://stats.nba.com/events/" - }) + response = requests.get(url, params=api_param, headers=DEFAULT_HEADERS) data = response.json() shot_chart_detail = pd.DataFrame(data['resultSets'][0]['rowSet'], columns=data['resultSets'][0]['headers']) league_average = pd.DataFrame(data['resultSets'][1]['rowSet'], columns=data['resultSets'][1]['headers']) return shot_chart_detail, league_average - -westbrook_chart, avg = shotchartdetail(player_id=203507) -print(westbrook_chart) \ No newline at end of file diff --git a/NBAapi/team.py b/NBAapi/team.py index 469663e..f254031 100644 --- a/NBAapi/team.py +++ b/NBAapi/team.py @@ -1,5 +1,6 @@ import requests import pandas as pd +from NBAapi.credentials import DEFAULT_HEADERS def stats(conference='',datefrom='',dateto='',div='',gamescope='',gamesegment='',lastngames=0, leagueid='00',location='',measuretype='Base',month=0,oppenentteamid=0,outcome='', @@ -40,7 +41,7 @@ def stats(conference='',datefrom='',dateto='',div='',gamescope='',gamesegment='' 'VsDivision' : vsdivision } u_a = "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/48.0.2564.82 Safari/537.36" - response = requests.get(url,params=api_param,headers={"USER-AGENT":u_a}) + response = requests.get(url,params=api_param,headers=DEFAULT_HEADERS) data = response.json() return pd.DataFrame(data['resultSets'][0]['rowSet'],columns=data['resultSets'][0]['headers']) @@ -53,7 +54,7 @@ def gamelog(teamid,seasontype='Regular Season',leagueid='00',season='2016-17'): 'teamID' : teamid, } u_a = "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/48.0.2564.82 Safari/537.36" - response = requests.get(url,params=api_param,headers={"USER-AGENT":u_a}) + response = requests.get(url,params=api_param,headers=DEFAULT_HEADERS) data = response.json() return pd.DataFrame(data['resultSets'][0]['rowSet'],columns=data['resultSets'][0]['headers']) @@ -64,7 +65,7 @@ def commonteamyears(leagueid='00'): 'LeagueID' : leagueid, } u_a = "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/48.0.2564.82 Safari/537.36" - response = requests.get(url,params=api_param,headers={"USER-AGENT":u_a}) + response = requests.get(url,params=api_param,headers=DEFAULT_HEADERS) data = response.json() return pd.DataFrame(data['resultSets'][0]['rowSet'],columns=data['resultSets'][0]['headers']) @@ -76,7 +77,7 @@ def roster(teamid,season='2016-17',leagueid='00'): 'teamID' : teamid, } u_a = "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/48.0.2564.82 Safari/537.36" - response = requests.get(url,params=api_param,headers={"USER-AGENT":u_a}) + response = requests.get(url,params=api_param,headers=DEFAULT_HEADERS) data = response.json() return pd.DataFrame(data['resultSets'][0]['rowSet'],columns=data['resultSets'][0]['headers']) diff --git a/NBAapi/team.py.bak b/NBAapi/team.py.bak index 785a3ba..e0ba107 100644 --- a/NBAapi/team.py.bak +++ b/NBAapi/team.py.bak @@ -40,7 +40,7 @@ def stats(conference='',datefrom='',dateto='',div='',gamescope='',gamesegment='' 'VsDivision' : vsdivision } u_a = "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/48.0.2564.82 Safari/537.36" - response = requests.get(url,params=api_param,headers={"USER-AGENT":u_a}) + response = requests.get(url,params=api_param,headers=DEFAULT_HEADERS) data = response.json() return pd.DataFrame(data['resultSets'][0]['rowSet'],columns=data['resultSets'][0]['headers']) @@ -53,7 +53,7 @@ def gamelog(teamid,seasontype='Regular Season',leagueid='00',season='2016-17'): 'teamID' : teamid, } u_a = "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/48.0.2564.82 Safari/537.36" - response = requests.get(url,params=api_param,headers={"USER-AGENT":u_a}) + response = requests.get(url,params=api_param,headers=DEFAULT_HEADERS) data = response.json() return pd.DataFrame(data['resultSets'][0]['rowSet'],columns=data['resultSets'][0]['headers']) @@ -64,7 +64,7 @@ def commonteamyears(leagueid='00'): 'LeagueID' : leagueid, } u_a = "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/48.0.2564.82 Safari/537.36" - response = requests.get(url,params=api_param,headers={"USER-AGENT":u_a}) + response = requests.get(url,params=api_param,headers=DEFAULT_HEADERS) data = response.json() return pd.DataFrame(data['resultSets'][0]['rowSet'],columns=data['resultSets'][0]['headers']) @@ -76,7 +76,7 @@ def roster(teamid,season='2016-17',leagueid='00'): 'teamID' : teamid, } u_a = "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/48.0.2564.82 Safari/537.36" - response = requests.get(url,params=api_param,headers={"USER-AGENT":u_a}) + response = requests.get(url,params=api_param,headers=DEFAULT_HEADERS) data = response.json() return pd.DataFrame(data['resultSets'][0]['rowSet'],columns=data['resultSets'][0]['headers']) From 37713814d17b8a54185ee6fe6e42b0eefa1ac456 Mon Sep 17 00:00:00 2001 From: Maxime Date: Fri, 7 Feb 2020 15:35:02 +0100 Subject: [PATCH 5/5] Update headers --- NBAapi/credentials/__init__.py | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/NBAapi/credentials/__init__.py b/NBAapi/credentials/__init__.py index 5670dd9..c699bfa 100644 --- a/NBAapi/credentials/__init__.py +++ b/NBAapi/credentials/__init__.py @@ -1,4 +1,10 @@ DEFAULT_HEADERS = { - "USER-AGENT": "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/48.0.2564.82 Safari/537.36", - "Referer": "https://stats.nba.com/events/" + 'User-Agent': 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10.14; rv:72.0) Gecko/20100101 Firefox/72.0', + 'Accept': 'application/json, text/plain, */*', + 'Accept-Language': 'fr,en-US;q=0.7,en;q=0.3', + 'X-NewRelic-ID': 'VQECWF5UChAHUlNTBwgBVw==', + 'x-nba-stats-origin': 'stats', + 'x-nba-stats-token': 'true', + 'Connection': 'keep-alive', + 'Referer': 'https://stats.nba.com/events/?' }