我用Python提前“算”出了2018世界杯奪冠球隊(第二彈......)
2018 年世界杯小組賽已經在如火如荼的進行中。在上篇文章我用Python分析了4W場比賽,2018世界杯冠軍竟然是……的基礎上,我們繼續分析世界杯 32 強的實力情況,以便能夠更進一步分析本次世界杯的奪冠熱門球隊。
三十年河東三十年河西,對于世界杯而言,這個時間可能 4 年就足夠。前幾場爆冷,使得天臺上已經擁擠不堪,跳水的股市更是讓天臺一度混亂。
在文章開始之前,提醒大家:賭球有風險,看球須盡興。本文的重點是通過分析 32 強之間的比賽,透過歷史數據來預測奪冠熱門球隊。
本次分析的數據來源于 Kaggle, 包含從 1872 年到今年的數據,包括世界杯比賽、世界杯預選賽、亞洲杯、歐洲杯、國家之間的友誼賽等比賽,一共大約 40000 場比賽的情況。
本次的環境為:
- Window 7 系統
- Python 3.6
- Jupyter Notebook
- pandas version 0.22.0
先來看看數據的情況:
- import pandas as pd
- import matplotlib.pyplot as plt
- %matplotlib inline
- plt.style.use('ggplot')
- # 解決matplotlib顯示中文問題
- plt.rcParams['font.sans-serif'] = ['SimHei'] # 指定默認字體
- plt.rcParams['axes.unicode_minus'] = False # 解決保存圖像是負號'-'顯示為方塊的問題
- df = pd.read_csv('results.csv')
- df.head()
該數據集包含的數據列的信息如下:
- 日期
- 主隊名稱
- 客隊名稱
- 主隊進球數 (不含點球)
- 客隊進球數 (不含點球)
- 比賽的類型
- 比賽所在城市
- 比賽所在國家
- 是否中立
結果如下:
獲取所有世界杯比賽的數據(含預選賽)
創建一個新的列數據,包含獲勝隊伍的信息,以及獲取所有世界杯比賽的數據,包含預選賽。
- mask = df['home_score'] - df['away_score']
- df.loc[mask > 0, 'win_team'] = df.loc[mask > 0, 'home_team']
- df.loc[mask < 0, 'win_team'] = df.loc[mask < 0, 'away_team']
- df.loc[mask == 0, 'win_team'] = 'Draw'
- df_FIFA_all = df[df['tournament'].str.contains('FIFA', regex=True)]
結果如下:
世界杯戰績分析 (含預選賽)
從前文來看, 在世界杯歷史上,實力最強的 5 支球隊是德國、阿根廷、巴西、法國、西班牙。
接下來,我們將比賽的范圍擴大至包含世界杯預選賽,通過 5 支球隊之間的比賽情況來進行分析。
- team_top5 = ['Germany', 'Argentina', 'Brazil', 'France', 'Spain']
- df_FIFA_top5 = df_FIFA_all[(df_FIFA_all['home_team'].isin(team_top5))&
- (df_FIFA_all['away_team'].isin(team_top5))]
- df_FIFA_top5.shape
- out:
- (43, 10)
在世界杯歷史上,5 支球隊在共有 43 場比賽相遇。
通過這 43 場比賽分析后,5 支球隊的勝負場數排名如下:
- s_FIFA_top5 = df_FIFA_top5.groupby('win_team')['win_team'].count()
- s_FIFA_top5.drop('Draw', inplace=True)
- s_FIFA_top5.sort_values(ascending=False, inplace=True)
- s_FIFA_top5.plot(kind='bar', figsize=(10,6), title='Top Five in World Cup')
結果如下:
下面,著重來分析下這 5 支球隊,在世界杯上,兩兩對陣時的勝負情況。
首先自定義兩個函數,分別獲得兩支球隊獲勝場數情況以及自定義繪圖函數,代碼如下:
- # 自定義函數,返回兩支球隊獲勝場數情況
- def team_vs(df,team_A,team_B):
- df_team_A_B = df[(df['home_team'].isin([team_A,team_B]))&
- (df['away_team'].isin([team_A,team_B]))]
- s_win_team = df_team_A_B.groupby('win_team')['win_team'].count()
- return s_win_team
- # 如需獲取本文源代碼,請關注公眾號“Python數據之道”,
- # 在公眾號后臺回復 “code” ,謝謝大家支持。
- # 自定義函數,兩支球隊在世界杯的對陣勝負情況制圖
- def team_vs_plot(df,team_A,team_B,ax):
- s_win_FIFA = team_vs(df,team_A,team_B)
- title = team_A + ' vs ' +team_B
- s_win_FIFA.plot(kind='bar', ax =ax)
- ax.set_xlabel('')
- ax.set_title(title,fontdict={'fontsize':10})
- ax.set_xticklabels(s_win_FIFA.index, rotation=20)
基于上述函數,分析世界杯戰績的結果如下:
巴西 vs 其他 4 支球隊
- f, axes = plt.subplots(figsize=(10,10), ncols=2, nrows=2)
- ax1, ax2,ax3,ax4 = axes.ravel()
- team_vs_plot(df_FIFA_all,'Brazil','Germany',ax=ax1)
- team_vs_plot(df_FIFA_all,'Brazil','Argentina',ax=ax2)
- team_vs_plot(df_FIFA_all,'Brazil','France',ax=ax3)
- team_vs_plot(df_FIFA_all,'Brazil','Spain',ax=ax4)
- # 如需獲取本文源代碼,請關注公眾號“Python數據之道”,
- # 在公眾號后臺回復 “code” ,謝謝大家支持。
- # set main title of the figure
- plt.suptitle('Brazil vs other Top 4 teams in World Cup', fontsize=14, fontweight='bold', x=0.5, y=0.94)
- plt.show()
結果如下:
統計現象 1:
在世界杯上的戰績,統計獲勝場數如下(不含平局):
巴西 1:1 德國,巴西 6:3 阿根廷,巴西 1:2 法國,巴西 3:1 西班牙
巴西隊,輸贏不好判斷……
德國 vs 其他 3 支球隊
代碼跟 2.1 部分是類似的,結果如下:
統計現象 2:
在世界杯上的戰績,統計獲勝場數如下(不含平局):
德國 4:1 阿根廷,德國 2:1 法國,德國 2:1 西班牙
德國在這 5 支球隊里,獲勝的優勢相對比較明顯。
阿根廷 vs 其他 2 支球隊
代碼跟 2.1 部分是類似的,結果如下:
統計現象3:
在世界杯上的戰績,統計獲勝場數如下(不含平局):
阿根廷 2:0 法國,阿根廷 1:0 西班牙
但阿根廷不敵巴西和德國
西班牙 vs 法國
綜合小結論:從歷屆世界杯上的表現情況來看,分析 5 強之間兩兩對陣后,發現德國隊的表現是最好的,其次巴西和阿根廷的表現也不錯。
考慮到,歷屆世界杯的數據,時間跨度很大,很多球隊已經發生了很大變化。
球隊真實的情況,可能選擇近幾年的比賽,以及包含不同級別的比賽,可能分析效果要更好些。
下面,重點來分析 2014 年以來包含所有比賽的情況。
2014 年以來,所有比賽的戰績對比
首先,時間選擇 2014 年之后(含 2014 年),距離現在的時間比較近,相對來說,球隊人員的組成變化小一些。
當然,這里的時間選擇,對于結果是有影響的。 大家可以探討下這個因素帶來的影響。
2014 年以來所有球隊所有比賽勝負情況概覽:
- df['date'] = pd.to_datetime(df['date'])
- df['year'] = df['date'].dt.year
- df_since_2014 = df[df['year']>=2014]
- df_since_2014.shape
2014 年以來,共有 3600 多場比賽。針對 3600 多場比賽分析后,勝負場數情況如下:
- s_all = df_since_2014.groupby('win_team')['win_team'].count()
- s_all.drop('Draw', inplace=True)
- s_all.sort_values(ascending=True, inplace=True)
- s_all.tail(50).plot(kind='barh', figsize=(8,16), tick_label='',title='Top 50 in all tournament since 2014')
從上圖來看,2014 年以來,墨西哥,法國,德國、葡萄牙、巴西、比利時、韓國和西班牙表現相對較好。
結果是不是跟想象中的有些差異?6 月 17 日的小組賽,德國不敵墨西哥,看來也不是全無理由的。
但是,本次我們主要還是要考慮 32 強之間的對陣,這樣更能反映現實情況。
2014 年以來 32 強相互之間在所有比賽中的概覽情況:
- team_list = ['Russia', 'Germany', 'Brazil', 'Portugal', 'Argentina', 'Belgium', 'Poland', 'France',
- 'Spain', 'Peru', 'Switzerland', 'England', 'Colombia', 'Mexico', 'Uruguay', 'Croatia',
- 'Denmark', 'Iceland', 'Costa Rica', 'Sweden', 'Tunisia', 'Egypt', 'Senegal', 'Iran',
- 'Serbia', 'Nigeria', 'Australia', 'Japan', 'Morocco', 'Panama', 'Korea Republic', 'Saudi Arabia']
- df_top32 = df_since_2014[(df_since_2014['home_team'].isin(team_list))&(df_since_2014['away_team'].isin(team_list))]
- s_top32 = df_top32.groupby('win_team')['win_team'].count()
- s_top32.drop('Draw', inplace=True)
- s_top32.sort_values(ascending=True, inplace=True)
- s_top32.plot(kind='barh', figsize=(8,12), tick_label='',title='Top 32 in all tournament since 2014')
- # plt.ylabel('')
從上圖來看,自 2014 年以來,巴西、法國、葡萄牙、阿根廷、墨西哥、比利時、德國、西班牙、英國為前 9 強。
下面我們來分析 Top 9 之間的勝負情況:
- team_top9 = [ 'Brazil', 'France', 'Portugal',
- 'Argentina','Mexico','Belgium',
- 'Germany','Spain','England']
- df_top9 = df_since_2014[(df_since_2014['home_team'].isin(team_top9))&
- (df_since_2014['away_team'].isin(team_top9))]
- df_top9.shape
2014 年以來,Top 9 之間一共踢了 44 場比賽(包括友誼賽)。
總體來說,比賽的場數不是太多,基于這些數據來分析,可能對結果會有較大的影響。
九強排名如下:
- s_top9 = df_top9.groupby('win_team')['win_team'].count()
- s_top9.drop('Draw', inplace=True)
- s_top9.sort_values(ascending=False, inplace=True)
- s_top9.plot(kind='bar', figsize=(10,6), title='Top 9 in all tournament since 2014')
來查看下都統計了哪些類型的比賽:
- s_tournament = df_top9.groupby('tournament')['tournament'].count()
- s_tournament_percentage = s_tournament/s_tournament.sum()
- # s_tournament_percentage.sort_values(ascending=False, inplace=True)
- s_tournament_percentage.tail(20).plot(kind='pie', figsize=(10,10), autopct='%.1f%%',
- startangle=150, title='percentage of tournament', label='',
- labeldistance=1.25, pctdistance=1.08)
從上面來看,友誼賽占的比例較大??紤]到友誼賽在有些情況下可能不能比較準確的反映出球隊的真實水平,且友誼賽占的場數比例較大,我們剔除友誼賽再來看看結果情況。
2014 年以來 32 強剔除友誼賽后的勝負情況概覽:
- df_top9_no_friendly = df_top9[df_top9['tournament']!= 'Friendly']
- df_top9_no_friendly.groupby('tournament')['tournament'].count()
- out:
- tournament
- Confederations Cup 3
- FIFA World Cup 6
- FIFA World Cup qualification 2
- UEFA Euro 2
- Name: tournament, dtype: int64
剔除友誼賽后,比賽類型分布如下:
剔除友誼賽后,Top 9 的情況如下:
在概覽中可以看出,是否剔除友誼賽(Friendly),對排名還是有影響的。
另外,剔除友誼賽后,總的比賽場數更少了(只有 13 場),9 強之間有些隊伍沒有比賽,或者沒有贏過,這個數據用來分析的作用更有限。
當然,在分析中 是否要剔除友誼賽,應該是值得商榷的。
九強兩兩對陣的勝負情況概覽
這里,我們后續分析采用包含友誼賽的數據,來分別分析9強之間兩兩對陣的情況,看看哪支球隊的勝率更高些。
首先自定義幾個函數,方便進行分析。自定義獲取球隊某年至今獲勝比例函數:
- # 自定義獲取球隊某年至今獲勝比例函數
- def probability(df,year,team_A,team_B):
- prob = []
- df_year = df[df['year']>= year]
- s = team_vs(df_year,team_A,team_B)
- s_team_A = 0 if s.get(team_A) is None else s.get(team_A)
- s_A_win = s_team_A/s.sum()
- s_team_B = 0 if s.get(team_B) is None else s.get(team_B)
- s_B_win = s_team_B/s.sum()
- s_draw = 1 - s_A_win - s_B_win
- prob.append(year)
- prob.append(s_A_win)
- prob.append(s_B_win)
- prob.append(s_draw)
- return prob
自定義獲取兩支球隊歷史獲勝情況對比函數:
- # 自定義獲取兩支球隊歷史獲勝情況對比函數
- def his_team_data(df,year_start,year_end,team_A,team_B):
- row_team = []
- # df_team = pd.DataFrame(columns=('year', 'team_A_win', 'team_B_win', 'draw'))
- for yr in list(range(year_start,year_end+1)):
- team_A_vs_team_B = probability(df,yr,team_A,team_B)
- row_team.append(team_A_vs_team_B)
- team_A_win = team_A + '_win_percentage'
- team_B_win = team_B + '_win_percentage'
- df_team = pd.DataFrame(row_team, columns=('year', team_A_win, team_B_win, 'draw_percentage'))
- return df_team
自定義兩支球隊歷史獲勝情況制圖函數:
- # 自定義兩支球隊歷史獲勝情況制圖函數
- def team_plot(df,year_start,year_end,team_A,team_B,ax):
- team_A_vs_team_B = team_A + '_vs_' + team_B
- team_A_vs_team_B = his_team_data(df,year_start,year_end,team_A,team_B)
- title = team_A + ' vs ' + team_B
- columns = [team_A+'_win_percentage',team_B+'_win_percentage','draw_percentage']
- team_A_vs_team_B.set_index('year')[columns].plot(kind='line',figsize=(10,6), title=title,ax=ax)
這些函數有什么用呢,首先我們來分析下巴西 vs 德國的情況,如下:
- team_A = 'Brazil'
- team_B = 'Germany'
- f, axes = plt.subplots(figsize=(6,12), ncols=1, nrows=2)
- ax1, ax2 = axes.ravel()
- team_plot(df,1930,2016,team_A,team_B,ax1)
- ax1.set_xlabel('')
- team_plot(df,2000,2016,team_A,team_B,ax2)
- ax2.set_title('')
- plt.show()
上述圖中,x 軸代表的含義是從某年至今(數據集含有部分 2018 年的比賽數據),兩支球隊的勝負情況。
例如 2012 對應的是德國跟巴西從 2012 年至今,兩支球隊的勝負情況。所以,時間越早,兩支球隊的比賽數量越多,數據曲線的波動可能要小些。
但由于球隊的成員組成在不斷的變化,會導致越早的數據,其分析價值越弱。 因此,選擇合適的年份進行分析就顯得很重要。
有童鞋說,如果我要同時分析德國對陣另外 8 支球隊呢?
這里,用上面的函數,也是很迅速的,代碼如下:
- team_A = 'Germany'
- for team in ['France','Portugal', 'Argentina','Mexico','Belgium','Brazil','Spain','England']:
- team_B = team
- f, axes = plt.subplots(figsize=(6,12), ncols=1, nrows=2)
- ax1, ax2 = axes.ravel()
- team_plot(df,1930,2016,team_A,team_B,ax1)
- ax1.set_xlabel('')
- team_plot(df,2000,2016,team_A,team_B,ax2)
- ax2.set_title('')
- plt.show()
運行上述代碼后,將會繪制 8 張圖,下面只放上其中幾張圖。
同理,如果你喜歡巴西隊或者別的球隊,也可以用上述代碼進行分析。
用上述函數可以快速的分析兩支球隊的歷史勝負情況,當然,有些球隊之間,相遇很少,或者近些年沒有遭遇過,那分析結果可能就不好用了。
當然,數據分析的只是歷史情況,足球是圓的,場上瞬息萬變。比如,阿根廷現在岌岌可危,梅西內心慌得一比……
預測
本屆世界杯真的是爆冷太多:
- 意大利,荷蘭,連小組賽都沒進
- 阿根廷,可以說現在已涼了半截
- 德國隊,若不是最后的絕殺,也差不多可以送首涼涼了,不過現在看已回血大半
最后,來放上我的神預測。黑馬年年有,今年特別多,預測不準,坐等 pia pia 打臉。
當然,其實我內心深處希望是下面這樣的。怎么樣,為強大的內心點贊吧~~
特別說明: 以上數據分析,純屬個人學習用,預測結果與實際情況可能偏差很大,不能用于其他用途。