摘要:本文意在創(chuàng)建一個(gè)得分圖,該圖同時(shí)描繪了從場上不同位置投籃得分的百分比和投籃次數(shù),這和個(gè)人網(wǎng)站上的帖子類似。接下來,我們需要繪制一個(gè)包含得分圖的籃球場圖。球員照片會出現(xiàn)在得分圖的右下角。的解決辦法是將命中率與聯(lián)賽平均分關(guān)聯(lián)。
本文意在創(chuàng)建一個(gè)得分圖,該圖同時(shí)描繪了從場上不同位置投籃得分的百分比和投籃次數(shù),這和 Austin Clemen 個(gè)人網(wǎng)站上的帖子 http://www.austinclemens.com/shotcharts/ 類似 。
為了實(shí)現(xiàn)這個(gè)得分圖,筆者參考了 Savvas Tjortjoglou 的帖子 http://savvastjortjoglou.com/nba-shot-sharts.html。這篇帖子很棒,但是他只描述了從不同位置投籃的次數(shù)。而筆者對在不同位置的投籃次數(shù)和進(jìn)球百分比都很感興趣,所以還需要進(jìn)一步的工作,在原有基礎(chǔ)上添加些東西,下面是實(shí)現(xiàn)過程。
#import some libraries and tell ipython we want inline figures rather than interactive figures. %matplotlib inline import matplotlib.pyplot as plt, pandas as pd, numpy as np, matplotlib as mpl
首先,我們需要獲得每個(gè)球員的投籃數(shù)據(jù)。利用 Savvas Tjortjoglou 貼出的代碼,筆者從 NBA.com 網(wǎng)站 API 上獲取了數(shù)據(jù)。在此不會貼出這個(gè)函數(shù)的結(jié)果。如果你感興趣,推薦你去看看 Savvas Tjortjoglou 的博客。
def aqcuire_shootingData(PlayerID,Season): import requests shot_chart_url = "http://stats.nba.com/stats/shotchartdetail?CFID=33&CFPARAMS="+Season+"&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="+Season+"&SeasonSegment=&SeasonType=Regular+Season&TeamID=" "0&VsConference=&VsDivision=&mode=Advanced&showDetails=0&showShots=1&showZones=0" response = requests.get(shot_chart_url) headers = response.json()["resultSets"][0]["headers"] shots = response.json()["resultSets"][0]["rowSet"] shot_df = pd.DataFrame(shots, columns=headers) return shot_df
接下來,我們需要繪制一個(gè)包含得分圖的籃球場圖。該籃球場圖例必須使用與NBA.com API 相同的坐標(biāo)系統(tǒng)。例如,3分位置的投籃距籃筐必須為 X 單位,上籃距離籃筐則是 Y 單位。同樣,筆者再次使用了 Savvas Tjortjoglou 的代碼(哈哈,否則的話,搞明白 NBA.com 網(wǎng)站的坐標(biāo)系統(tǒng)肯定會耗費(fèi)不少的時(shí)間)。
def draw_court(ax=None, color="black", lw=2, outer_lines=False): from matplotlib.patches import Circle, Rectangle, Arc if ax is None: ax = plt.gca() hoop = Circle((0, 0), radius=7.5, linewidth=lw, color=color, fill=False) backboard = Rectangle((-30, -7.5), 60, -1, linewidth=lw, color=color) outer_box = Rectangle((-80, -47.5), 160, 190, linewidth=lw, color=color, fill=False) inner_box = Rectangle((-60, -47.5), 120, 190, linewidth=lw, color=color, fill=False) top_free_throw = Arc((0, 142.5), 120, 120, theta1=0, theta2=180, linewidth=lw, color=color, fill=False) bottom_free_throw = Arc((0, 142.5), 120, 120, theta1=180, theta2=0, linewidth=lw, color=color, linestyle="dashed") restricted = Arc((0, 0), 80, 80, theta1=0, theta2=180, linewidth=lw, color=color) 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_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) 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: 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) ax.set_xticklabels([]) ax.set_yticklabels([]) ax.set_xticks([]) ax.set_yticks([]) return ax
我想創(chuàng)造一個(gè)不同位置的投籃百分比數(shù)組,因此決定利用 matplot 的 Hexbin 函數(shù) http://matplotlib.org/api/pyplot_api.html 將投籃位置均勻地分組到六邊形中。該函數(shù)會對每個(gè)六邊形中每一個(gè)位置的投籃次數(shù)進(jìn)行計(jì)數(shù)。
六邊形是均勻的分布在 XY 網(wǎng)格中。「gridsize」變量控制六邊形的數(shù)目?!竐xtent」變量控制第一個(gè)和最后一個(gè)六邊形的繪制位置(一般來說第一個(gè)六邊形的位置基于第一個(gè)投籃的位置)。
計(jì)算命中率則需要對每個(gè)六邊形中投籃的次數(shù)和投籃得分次數(shù)進(jìn)行計(jì)數(shù),因此筆者對同一位置的投籃和得分?jǐn)?shù)分別運(yùn)行 hexbin 函數(shù)。然后,只需用每個(gè)位置的進(jìn)球數(shù)除以投籃數(shù)。
def find_shootingPcts(shot_df, gridNum): x = shot_df.LOC_X[shot_df["LOC_Y"]<425.1] #i want to make sure to only include shots I can draw y = shot_df.LOC_Y[shot_df["LOC_Y"]<425.1] x_made = shot_df.LOC_X[(shot_df["SHOT_MADE_FLAG"]==1) & (shot_df["LOC_Y"]<425.1)] y_made = shot_df.LOC_Y[(shot_df["SHOT_MADE_FLAG"]==1) & (shot_df["LOC_Y"]<425.1)] #compute number of shots made and taken from each hexbin location hb_shot = plt.hexbin(x, y, gridsize=gridNum, extent=(-250,250,425,-50)); plt.close() #don"t want to show this figure! hb_made = plt.hexbin(x_made, y_made, gridsize=gridNum, extent=(-250,250,425,-50),cmap=plt.cm.Reds); plt.close() #compute shooting percentage ShootingPctLocs = hb_made.get_array() / hb_shot.get_array() ShootingPctLocs[np.isnan(ShootingPctLocs)] = 0 #makes 0/0s=0 return (ShootingPctLocs, hb_shot)
筆者非常喜歡 Savvas Tjortjoglou 在他的得分圖中加入了球員頭像的做法,因此也順道用了他的這部分代碼。球員照片會出現(xiàn)在得分圖的右下角。
def acquire_playerPic(PlayerID, zoom, offset=(250,400)): from matplotlib import offsetbox as osb import urllib pic = urllib.urlretrieve("http://stats.nba.com/media/players/230x185/"+PlayerID+".png",PlayerID+".png") player_pic = plt.imread(pic[0]) img = osb.OffsetImage(player_pic, zoom) #img.set_offset(offset) img = osb.AnnotationBbox(img, offset,xycoords="data",pad=0.0, box_alignment=(1,0), frameon=False) return img
筆者想用連續(xù)的顏色圖來描述投籃進(jìn)球百分比,紅圈越多代表著更高的進(jìn)球百分比。雖然「紅」顏色圖示效果不錯(cuò),但是它會將0%的投籃進(jìn)球百分比顯示為白色http://matplotlib.org/users/colormaps.html,而這樣顯示就會不明顯,所以筆者用淡粉紅色代表0%的命中率,因此對紅顏色圖做了下面的修改。
#cmap = plt.cm.Reds #cdict = cmap._segmentdata cdict = { "blue": [(0.0, 0.6313725709915161, 0.6313725709915161), (0.25, 0.4470588266849518, 0.4470588266849518), (0.5, 0.29019609093666077, 0.29019609093666077), (0.75, 0.11372549086809158, 0.11372549086809158), (1.0, 0.05098039284348488, 0.05098039284348488)], "green": [(0.0, 0.7333333492279053, 0.7333333492279053), (0.25, 0.572549045085907, 0.572549045085907), (0.5, 0.4156862795352936, 0.4156862795352936), (0.75, 0.0941176488995552, 0.0941176488995552), (1.0, 0.0, 0.0)], "red": [(0.0, 0.9882352948188782, 0.9882352948188782), (0.25, 0.9882352948188782, 0.9882352948188782), (0.5, 0.9843137264251709, 0.9843137264251709), (0.75, 0.7960784435272217, 0.7960784435272217), (1.0, 0.40392157435417175, 0.40392157435417175)] } mymap = mpl.colors.LinearSegmentedColormap("my_colormap", cdict, 1024)
好了,現(xiàn)在需要做的就是將它們合并到一塊兒。下面所示的較大函數(shù)會利用上文描述的函數(shù)來創(chuàng)建一個(gè)描述投籃命中率的得分圖,百分比由紅圈表示(紅色越深 = 更高的命中率),投籃次數(shù)則由圓圈的大小決定(圓圈越大 = 投籃次數(shù)越多)。需要注意的是,圓圈在交疊之前都能增大。一旦圓圈開始交疊,就無法繼續(xù)增大。
在這個(gè)函數(shù)中,計(jì)算了每個(gè)位置的投籃進(jìn)球百分比和投籃次數(shù)。然后畫出在該位置投籃的次數(shù)(圓圈大小)和進(jìn)球百分比(圓圈顏色深淺)。
def shooting_plot(shot_df, plot_size=(12,8),gridNum=30): from matplotlib.patches import Circle x = shot_df.LOC_X[shot_df["LOC_Y"]<425.1] y = shot_df.LOC_Y[shot_df["LOC_Y"]<425.1] #compute shooting percentage and # of shots (ShootingPctLocs, shotNumber) = find_shootingPcts(shot_df, gridNum) #draw figure and court fig = plt.figure(figsize=plot_size)#(12,7) cmap = mymap #my modified colormap ax = plt.axes([0.1, 0.1, 0.8, 0.8]) #where to place the plot within the figure draw_court(outer_lines=False) plt.xlim(-250,250) plt.ylim(400, -25) #draw player image zoom = np.float(plot_size[0])/(12.0*2) #how much to zoom the player"s pic. I have this hackily dependent on figure size img = acquire_playerPic(PlayerID, zoom) ax.add_artist(img) #draw circles for i, shots in enumerate(ShootingPctLocs): restricted = Circle(shotNumber.get_offsets()[i], radius=shotNumber.get_array()[i], color=cmap(shots),alpha=0.8, fill=True) if restricted.radius > 240/gridNum: restricted.radius=240/gridNum ax.add_patch(restricted) #draw color bar ax2 = fig.add_axes([0.92, 0.1, 0.02, 0.8]) cb = mpl.colorbar.ColorbarBase(ax2,cmap=cmap, orientation="vertical") cb.set_label("Shooting %") cb.set_ticks([0.0, 0.25, 0.5, 0.75, 1.0]) cb.set_ticklabels(["0%","25%", "50%","75%", "100%"]) plt.show() return ax
好了,大功告成!因?yàn)楣P者是森林狼隊(duì)的粉絲,在下面用幾分鐘跑出了森林狼隊(duì)前六甲的得分圖。
PlayerID = "203952" #andrew wiggins shot_df = aqcuire_shootingData(PlayerID,"2015-16") ax = shooting_plot(shot_df, plot_size=(12,8));
PlayerID = "1626157" #karl anthony towns shot_df = aqcuire_shootingData(PlayerID,"2015-16") ax = shooting_plot(shot_df, plot_size=(12,8));
PlayerID = "203897" #zach lavine shot_df = aqcuire_shootingData(PlayerID,"2015-16") ax = shooting_plot(shot_df, plot_size=(12,8));
PlayerID = "203476" #gorgui deing shot_df = aqcuire_shootingData(PlayerID,"2015-16") ax = shooting_plot(shot_df, plot_size=(12,8));
PlayerID = "2755" #kevin martin shot_df = aqcuire_shootingData(PlayerID,"2015-16") ax = shooting_plot(shot_df, plot_size=(12,8));
PlayerID = "201937" #ricky rubio shot_df = aqcuire_shootingData(PlayerID,"2015-16") ax = shooting_plot(shot_df, plot_size=(12,8));
使用 hexbin 函數(shù)也是有隱患的,第一它并沒有解釋由于三分線而導(dǎo)致的非線性特性(一些 hexbin 函數(shù)同時(shí)包括了2分和3分的投籃)。它很好的限定了一些窗口來進(jìn)行3分投籃,但如果沒有這個(gè)位置的硬編碼就沒有辦法做到這一點(diǎn)。此外 hexbin 方法的一個(gè)優(yōu)點(diǎn)與是可以很容易地改變窗口的數(shù)量,但不確定是否可以同樣靈活的處理2分投籃和3分投籃。
另外一個(gè)隱患在于此圖將所有投籃都一視同仁,這相當(dāng)不公平。在禁區(qū)投籃命中40%和三分線后的投籃命中40%可是大不相同。Austin Clemens 的解決辦法是將命中率與聯(lián)賽平均分關(guān)聯(lián)。也許過幾天筆者也會實(shí)現(xiàn)與之類似的功能。
原文 Creating NBA Shot Charts 作者 Dan Vatterott ,本文由 OneAPM 工程師編譯整理。
OneAPM 能夠幫你查看 Python 應(yīng)用程序的方方面面,不僅能夠監(jiān)控終端的用戶體驗(yàn),還能監(jiān)控服務(wù)器性能,同時(shí)還支持追蹤數(shù)據(jù)庫、第三方 API 和 Web 服務(wù)器的各種問題。想閱讀更多技術(shù)文章,請?jiān)L問 OneAPM 官方技術(shù)博客。
本文轉(zhuǎn)自 OneAPM 官方博客
文章版權(quán)歸作者所有,未經(jīng)允許請勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請注明本文地址:http://www.ezyhdfw.cn/yun/37726.html
摘要:作為一個(gè)正奮戰(zhàn)在之路上的球迷,開始了一次數(shù)據(jù)分析實(shí)戰(zhàn),于是,以分析球賽數(shù)據(jù)為起點(diǎn)的操作開始了前言作為一個(gè)功能強(qiáng)大的編程語言,如今在數(shù)據(jù)分析機(jī)器學(xué)習(xí)人工智能等方面如日中天。 Casey 豈安業(yè)務(wù)風(fēng)險(xiǎn)分析師主要負(fù)責(zé)豈安科技RED.Q的數(shù)據(jù)分析和運(yùn)營工作。 12月19日,科比再次站在斯臺普斯中心球館中央,見證自己的兩件球衣高懸于球館上空。作為一個(gè)正奮戰(zhàn)在 Python 之路上的球迷,...
小編寫這篇文章的主要目的,主要是用來給大家做出一個(gè)解答,解答關(guān)于Scrapy框架的事情,主要是使用Scrapy框架,爬取網(wǎng)頁,然后保存到一個(gè)專業(yè)數(shù)據(jù)庫中,這個(gè)數(shù)據(jù)庫就是Mysql,那么,其具體要怎么實(shí)現(xiàn)呢?下面就給大家詳細(xì)的解答?! 〈蠹液?,這一期阿彬給大家分享Scrapy爬蟲框架與本地Mysql的使用。今天阿彬爬取的網(wǎng)頁是虎撲體育網(wǎng)。 ?。?)打開虎撲體育網(wǎng),分析一下網(wǎng)頁的數(shù)據(jù),使用xpat...
摘要:近日各隊(duì)紛紛發(fā)布季前賽賽程,迎接新賽季。實(shí)際上,數(shù)據(jù)分析團(tuán)隊(duì)對于現(xiàn)在的球隊(duì)已經(jīng)算是標(biāo)配了,無論是在球員選擇還是戰(zhàn)術(shù)制定上都起著重要的作用。于是,我們就借助官方數(shù)據(jù)和的繪圖函數(shù),來看一看不同的球員都是在什么位置出手投籃的。比如林書豪就是。 showImg(https://segmentfault.com/img/remote/1460000019790849); 作者 | Crossin...
摘要:在本文中,我們將以的決策樹和隨機(jī)森林預(yù)測獲勝者。用決策樹和隨機(jī)森林預(yù)測獲勝者導(dǎo)入數(shù)據(jù)集并解析日期導(dǎo)入熊貓作為。這將幫助我們查看決策樹分類器的預(yù)測是否正確?;煜仃囷@示了我們決策樹的正確和不正確的分類。 showImg(https://segmentfault.com/img/bVbcr26?w=750&h=383); 在本文中,我們將以Scikit-learn的決策樹和隨機(jī)森林預(yù)測NB...
閱讀 623·2021-08-31 09:45
閱讀 1723·2021-08-11 11:19
閱讀 948·2019-08-30 15:55
閱讀 901·2019-08-30 10:52
閱讀 2929·2019-08-29 13:11
閱讀 2993·2019-08-23 17:08
閱讀 2899·2019-08-23 15:11
閱讀 3140·2019-08-23 14:33