作者 | 石曉文
來源 | 小小挖掘機
NBA激戰正酣,首輪除掘金和馬刺的較量還沒有結束外,其餘對決都已經結束,本文將手把手帶你可視化分析下各球隊的首輪表現,同時將對次輪最受矚目的火勇大戰進行一個簡單的前瞻分析!
通過本文,你將學會python中使用matplotlib庫繪製柱狀圖、散點圖、雷達圖的相關知識!咱們開始吧!
01 數據獲取及準備我們首先獲取各球隊季後賽首輪的數據,網址是:https://www.basketball-reference.com/playoffs/NBA_2019.html。首先選擇Playoffs:
img然後,下拉到球隊技術統計這裡,導出csv,如果無法直接倒出,可以複製到txt文件裡面(我就是這麼幹的):
img然後,我們再把對手表現的數據導出,這樣基礎數據就準備好了:
img好了,我們使用代碼將數據進行讀取,首先導入所需要的庫:
1import pandas as pd
2import matplotlib.pyplot as plt
3import numpy as np
將數據使用pandas進行讀取,看看數據如何:
1team_stats_df = pd.read_csv('data/nba1.txt',sep=',')
2team_stats_df
同樣,將對手表現的數據讀入,但是這裡列名需要修改一下:
1team_opp_stats_df = pd.read_csv('data/nba2.txt',sep=',')
2team_opp_stats_df.columns = ['Rk','Team','G'] + ['opp' + x for x in team_opp_stats_df.columns[3:]]
將兩個表按照隊名進行merge,這樣oppPTS列,可以表示場均失分,這也是我們在對手表現數據裡面主要關注的列:
1mergedf = pd.merge(team_stats_df,team_opp_stats_df,on=['Team'])
繪製柱狀圖,需要使用的是plt.bar方法,其主要輸入的有兩個參數,一個是x軸的值,一個是高度,比如,我們繪製一下各球隊首輪平均得分的柱狀圖:
1pts = np.array(mergedf[['PTS','oppPTS']].values.tolist())
2index = np.arange(pts.shape[0])
3plt.bar(index,pts[:,1])
4plt.show()
結果如下:
img上面的圖還是有幾個問題的,首先,我們不希望X顯示的是數值,我們希望展示各個球隊名稱的縮寫,同時刻度可以不從0開始,這樣看上去區分度不是十分的明顯。
針對第一個問題,我們需要首先按順序得到各個球隊名稱的縮寫:
1team_name = ['DEN','SA','GSW','PHI','LAC','BKN','POR','HOU','TOR','OKC','UTA','MIL','ORL','BOS','DET','IND']
隨後,將x軸的刻度設置為相應的隊名:
1plt.xticks(index,team_name)
針對第二個問題,我們需要限定y軸的範圍,使用ylim方法:
1plt.ylim(80,140)
這樣,我們得到了下面的圖片:
img上面這部分的完整代碼如下:
1team_name = ['DEN','SA','GSW','PHI','LAC','BKN','POR','HOU','TOR','OKC','UTA','MIL','ORL','BOS','DET','IND']
2plt.ylabel('Score')
3plt.xlabel('Team')
4plt.xticks(index,team_name)
5plt.yticks(np.arange(80,141,10))
6plt.ylim(80,140)
7plt.bar(index,pts[:,0])
8plt.rcParams['figure.figsize'] = (12.0, 6.0)
9plt.show()
此時,我們可能想要將球隊的得失分表示出來,代碼如下:
1fig, ax = plt.subplots()
2bar_width = 0.35
3opacity = 0.4
4rects1 = ax.bar(index, pts[:,0], bar_width,alpha=opacity, color='b',label='OFFENSE')
5rects2 = ax.bar(index + bar_width, pts[:,1], bar_width,alpha=opacity, color='r', label='DEFENSE')
6ax.set_xlabel('Team')
7ax.set_ylabel('PTS')
8ax.set_xticks(index + bar_width / 2)
9ax.set_xticklabels(team_name)
10ax.set_yticks(np.arange(80,141,10))
11plt.show()
繪製的結果如下:
img03 散點圖分析球隊場均得失分這一部分,我們使用散點圖來分析球隊場均得失分,使用plt.scatter函數,分別指定各個點的x和y值:
1plt.scatter(pts[:,0], pts[:,1])
2plt.xlabel('Offense PTS')
3plt.ylabel('Defense PTS')
4plt.show()
得到的效果如下:
img簡單的繪製散點圖,什麼都看不出來,此時,我們提出兩個需求,首先,能不能將各個球隊的簡稱放到各個點的旁邊,這樣我們就能清楚知道哪個點代表哪個球隊。其次,能不能在圖中添加兩條直線,分別表示球隊得分和失分的平均值?我們一個一個來解決。
首先,將球隊標記加入到圖中,我們使用plt.annotate函數,該函數需要傳入三個參數,依次是注釋文本內容,被注釋的坐標點,注釋文字的坐標位置:
1plt.scatter(pts[:,0], pts[:,1])
2plt.xlabel('Offense PTS')
3plt.ylabel('Defense PTS')
4for i in range(len(pts[:,0])):
5 plt.annotate(team_name[i], xy = (pts[i,0], pts[i,1]), xytext = (pts[i,0]+0.1, pts[i,1]+0.1))
6plt.show()
此時的效果就能滿足我們的第一個需求:
img對於第二個需求,我們使用plt.vlines和plt.hlines方法,對於plt.vlines方法,它是在圖中繪製一條豎直線,因此需要指定x軸的坐標,同時需要指定y軸的起始坐標和結束坐標,對於plt.hlines方法,它是在圖中繪製一條水平線,因此需要指定y軸的坐標,同時需要指定x軸的起始坐標和結束坐標:
1offense_mean = np.mean(pts[:,0])
2defense_mean = np.mean(pts[:,1])
3print(offense_mean,defense_mean)
4plt.scatter(pts[:,0], pts[:,1])
5plt.vlines(np.mean(pts[:,0]), np.min(pts[:,1])-5,np.max(pts[:,1])+5,colors = "r", linestyles = "dashed")
6plt.hlines(np.mean(pts[:,1]), np.min(pts[:,0])-5,np.max(pts[:,0])+5,colors = "r", linestyles = "dashed")
7plt.ylim(np.min(pts[:,1])-5,np.max(pts[:,1])+5)
8plt.xlim(np.min(pts[:,0])-5,np.max(pts[:,0])+5)
9labelplt.ylabel('Defense PTS')
10for i in range(len(pts[:,0])):
11 plt.annotate(team_name[i], xy = (pts[i,0], pts[i,1]), xytext = (pts[i,0]+0.1, pts[i,1]+0.1))
結果如下:
img基於上面的散點圖,我們就能很快將十六支球隊分為四個象限。
1)第一象限:進攻好,防守差,包含的球隊有金州勇士、費城76人、洛杉磯快船、布魯克林籃網
2)第二象限:進攻差,防守差,包含的球隊有底特律活塞、俄克拉荷馬雷霆、聖安東尼奧馬刺
3)第三象限:進攻差,防守好,包含的球隊有休斯頓火箭、奧蘭多魔術、印第安納步行者、猶他爵士、多倫多猛龍、波士頓凱爾特人
4)第四象限:進攻好,防守好,包含的球隊有密爾沃基雄鹿、波特蘭開拓者、丹佛掘金
哈哈,是不是跟你想像的不太一樣,比如火箭怎麼能說是進攻差的球隊呢!因為他們的對手爵士是全聯盟常規賽防守第一的球隊,這種因素我們是無法通過這種圖分析出來的。也就是說,數據不能表明一切!
04 基於雷達圖的火勇對決前瞻勇士在今天的比賽中,憑藉杜蘭特的神奇表現,擊敗快船與火箭會師西部半決賽,這也是半決賽對決中最受人矚目的。那麼,我們通過雷達圖來分析下兩隊在季後賽首輪的表現吧。這裡要說明的是,我們的數據是在勇船第六場之前獲取的,因此只有勇士五場的數據。
我們先來看看兩隊的基礎數據,我們挑選了場均得分,場均失分、命中率、三分命中率、籃板、助攻、搶斷、蓋帽、失誤等九項數據:
1two_team_stats = mergedf[(mergedf['Team']=='Houston Rockets') | (mergedf['Team']=='Golden State Warriors')]
2hou_and_gsw_df = two_team_stats[['FG%','3P%','TRB','AST','STL','BLK','PTS','oppPTS','TOV']]
3print(hou_and_gsw_df)
結果如下,其中2代表勇士,7代表火箭:
img接下來,我們想通過雷達圖來對比一下二者的這九項數據,matplotlib裡面的雷達圖貌似必須是統一刻度的,至少我目前還沒有找到如何設置為不同刻度。因此,我們首先將二者的數據,用統一的標準,轉換到0-1區間內:
1hou_and_gsw_data[:,0] = hou_and_gsw_data[:,0] / 0.55hou_and_gsw_data[:,1] = hou_and_gsw_data[:,1] / 0.55hou_and_gsw_data[:,2] = hou_and_gsw_data[:,2] / 55hou_and_gsw_data[:,3] = hou_and_gsw_data[:,3] / 40hou_and_gsw_data[:,4] = hou_and_gsw_data[:,4] / 12hou_and_gsw_data[:,5] = hou_and_gsw_data[:,5] / 12hou_and_gsw_data[:,6] = hou_and_gsw_data[:,6] / 130hou_and_gsw_data[:,7] = hou_and_gsw_data[:,7] / 130hou_and_gsw_data[:,8] = hou_and_gsw_data[:,8] / 20
繪製雷達圖,使用的是極坐標系,因此,我們需要設置一下:
1fig=plt.figure(figsize=(14,8))
2ax1=fig.add_subplot(1,1,1,polar=True) #設置第一個坐標軸為極坐標體系
隨後,我們獲取勇士和火箭的數據,並取得對應的數據標籤:
1gsw=hou_and_gsw_data[0,:]
2hou=hou_and_gsw_data[1,:]
3label=np.array([j for j in hou_and_gsw_df.columns])
隨後,我們基於label的數量,對整圓進行切分,在切分的同時,我們需要加入切分後的第一個元素,以形成一個閉環:
1angle = np.linspace(0, 2*np.pi, len(gsw), endpoint=False)
2np.concatenate((angle, [angle[0]]))
3np.concatenate((gsw, [gsw[0]]))
4hou = np.concatenate((hou, [hou[0]]))
隨後,便可以繪製我們的雷達圖:
1ax1.set_thetagrids(angles*180/np.pi, label, fontproperties="Microsoft Yahei")
2ax1.plot(angles,gsw,"o-",label='GSW')
3ax1.plot(angles,hou,"o-",label='HOU')ax1.set_theta_zero_location('NW')
4ax1.set_rlim(0,1)
5ax1.fill(angles,gsw,facecolor='g', alpha=0.2)
6ax1.set_title("HOU VS GSW",fontproperties="SimHei",fontsize=16)
7plt.legend(loc = 'best')plt.show()
結果如下:
img從雷達圖來看,火箭除了在搶斷和失分上佔據一定優勢外,其他各項數據均落後於勇士。不過還是剛才的那句話,數據不能體現一切,希望火勇雙方能給我們帶來一場精彩絕倫的較量。