我用Python提前“算”出了2018世界杯奪冠球隊(duì)(第二彈......)
2018 年世界杯小組賽已經(jīng)在如火如荼的進(jìn)行中。在上篇文章我用Python分析了4W場比賽,2018世界杯冠軍竟然是……的基礎(chǔ)上,我們繼續(xù)分析世界杯 32 強(qiáng)的實(shí)力情況,以便能夠更進(jìn)一步分析本次世界杯的奪冠熱門球隊(duì)。
三十年河?xùn)|三十年河西,對(duì)于世界杯而言,這個(gè)時(shí)間可能 4 年就足夠。前幾場爆冷,使得天臺(tái)上已經(jīng)擁擠不堪,跳水的股市更是讓天臺(tái)一度混亂。
在文章開始之前,提醒大家:賭球有風(fēng)險(xiǎn),看球須盡興。本文的重點(diǎn)是通過分析 32 強(qiáng)之間的比賽,透過歷史數(shù)據(jù)來預(yù)測奪冠熱門球隊(duì)。
本次分析的數(shù)據(jù)來源于 Kaggle, 包含從 1872 年到今年的數(shù)據(jù),包括世界杯比賽、世界杯預(yù)選賽、亞洲杯、歐洲杯、國家之間的友誼賽等比賽,一共大約 40000 場比賽的情況。
本次的環(huán)境為:
- Window 7 系統(tǒng)
- Python 3.6
- Jupyter Notebook
- pandas version 0.22.0
先來看看數(shù)據(jù)的情況:
- import pandas as pd
- import matplotlib.pyplot as plt
- %matplotlib inline
- plt.style.use('ggplot')
- # 解決matplotlib顯示中文問題
- plt.rcParams['font.sans-serif'] = ['SimHei'] # 指定默認(rèn)字體
- plt.rcParams['axes.unicode_minus'] = False # 解決保存圖像是負(fù)號(hào)'-'顯示為方塊的問題
- df = pd.read_csv('results.csv')
- df.head()
該數(shù)據(jù)集包含的數(shù)據(jù)列的信息如下:
- 日期
- 主隊(duì)名稱
- 客隊(duì)名稱
- 主隊(duì)進(jìn)球數(shù) (不含點(diǎn)球)
- 客隊(duì)進(jìn)球數(shù) (不含點(diǎn)球)
- 比賽的類型
- 比賽所在城市
- 比賽所在國家
- 是否中立
結(jié)果如下:
獲取所有世界杯比賽的數(shù)據(jù)(含預(yù)選賽)
創(chuàng)建一個(gè)新的列數(shù)據(jù),包含獲勝隊(duì)伍的信息,以及獲取所有世界杯比賽的數(shù)據(jù),包含預(yù)選賽。
- 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)]
結(jié)果如下:
世界杯戰(zhàn)績分析 (含預(yù)選賽)
從前文來看, 在世界杯歷史上,實(shí)力最強(qiáng)的 5 支球隊(duì)是德國、阿根廷、巴西、法國、西班牙。
接下來,我們將比賽的范圍擴(kuò)大至包含世界杯預(yù)選賽,通過 5 支球隊(duì)之間的比賽情況來進(jìn)行分析。
- 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 支球隊(duì)在共有 43 場比賽相遇。
通過這 43 場比賽分析后,5 支球隊(duì)的勝負(fù)場數(shù)排名如下:
- 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')
結(jié)果如下:
下面,著重來分析下這 5 支球隊(duì),在世界杯上,兩兩對(duì)陣時(shí)的勝負(fù)情況。
首先自定義兩個(gè)函數(shù),分別獲得兩支球隊(duì)獲勝場數(shù)情況以及自定義繪圖函數(shù),代碼如下:
- # 自定義函數(shù),返回兩支球隊(duì)獲勝場數(shù)情況
- 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
- # 如需獲取本文源代碼,請(qǐng)關(guān)注公眾號(hào)“Python數(shù)據(jù)之道”,
- # 在公眾號(hào)后臺(tái)回復(fù) “code” ,謝謝大家支持。
- # 自定義函數(shù),兩支球隊(duì)在世界杯的對(duì)陣勝負(fù)情況制圖
- 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)
基于上述函數(shù),分析世界杯戰(zhàn)績的結(jié)果如下:
巴西 vs 其他 4 支球隊(duì)
- 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)
- # 如需獲取本文源代碼,請(qǐng)關(guān)注公眾號(hào)“Python數(shù)據(jù)之道”,
- # 在公眾號(hào)后臺(tái)回復(fù) “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()
結(jié)果如下:
統(tǒng)計(jì)現(xiàn)象 1:
在世界杯上的戰(zhàn)績,統(tǒng)計(jì)獲勝場數(shù)如下(不含平局):
巴西 1:1 德國,巴西 6:3 阿根廷,巴西 1:2 法國,巴西 3:1 西班牙
巴西隊(duì),輸贏不好判斷……
德國 vs 其他 3 支球隊(duì)
代碼跟 2.1 部分是類似的,結(jié)果如下:
統(tǒng)計(jì)現(xiàn)象 2:
在世界杯上的戰(zhàn)績,統(tǒng)計(jì)獲勝場數(shù)如下(不含平局):
德國 4:1 阿根廷,德國 2:1 法國,德國 2:1 西班牙
德國在這 5 支球隊(duì)里,獲勝的優(yōu)勢相對(duì)比較明顯。
阿根廷 vs 其他 2 支球隊(duì)
代碼跟 2.1 部分是類似的,結(jié)果如下:
統(tǒng)計(jì)現(xiàn)象3:
在世界杯上的戰(zhàn)績,統(tǒng)計(jì)獲勝場數(shù)如下(不含平局):
阿根廷 2:0 法國,阿根廷 1:0 西班牙
但阿根廷不敵巴西和德國
西班牙 vs 法國
綜合小結(jié)論:從歷屆世界杯上的表現(xiàn)情況來看,分析 5 強(qiáng)之間兩兩對(duì)陣后,發(fā)現(xiàn)德國隊(duì)的表現(xiàn)是最好的,其次巴西和阿根廷的表現(xiàn)也不錯(cuò)。
考慮到,歷屆世界杯的數(shù)據(jù),時(shí)間跨度很大,很多球隊(duì)已經(jīng)發(fā)生了很大變化。
球隊(duì)真實(shí)的情況,可能選擇近幾年的比賽,以及包含不同級(jí)別的比賽,可能分析效果要更好些。
下面,重點(diǎn)來分析 2014 年以來包含所有比賽的情況。
2014 年以來,所有比賽的戰(zhàn)績對(duì)比
首先,時(shí)間選擇 2014 年之后(含 2014 年),距離現(xiàn)在的時(shí)間比較近,相對(duì)來說,球隊(duì)人員的組成變化小一些。
當(dāng)然,這里的時(shí)間選擇,對(duì)于結(jié)果是有影響的。 大家可以探討下這個(gè)因素帶來的影響。
2014 年以來所有球隊(duì)所有比賽勝負(fù)情況概覽:
- 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 多場比賽。針對(duì) 3600 多場比賽分析后,勝負(fù)場數(shù)情況如下:
- 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 年以來,墨西哥,法國,德國、葡萄牙、巴西、比利時(shí)、韓國和西班牙表現(xiàn)相對(duì)較好。
結(jié)果是不是跟想象中的有些差異?6 月 17 日的小組賽,德國不敵墨西哥,看來也不是全無理由的。
但是,本次我們主要還是要考慮 32 強(qiáng)之間的對(duì)陣,這樣更能反映現(xiàn)實(shí)情況。
2014 年以來 32 強(qiáng)相互之間在所有比賽中的概覽情況:
- 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 年以來,巴西、法國、葡萄牙、阿根廷、墨西哥、比利時(shí)、德國、西班牙、英國為前 9 強(qiáng)。
下面我們來分析 Top 9 之間的勝負(fù)情況:
- 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 場比賽(包括友誼賽)。
總體來說,比賽的場數(shù)不是太多,基于這些數(shù)據(jù)來分析,可能對(duì)結(jié)果會(huì)有較大的影響。
九強(qiáng)排名如下:
- 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')
來查看下都統(tǒng)計(jì)了哪些類型的比賽:
- 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)
從上面來看,友誼賽占的比例較大??紤]到友誼賽在有些情況下可能不能比較準(zhǔn)確的反映出球隊(duì)的真實(shí)水平,且友誼賽占的場數(shù)比例較大,我們剔除友誼賽再來看看結(jié)果情況。
2014 年以來 32 強(qiáng)剔除友誼賽后的勝負(fù)情況概覽:
- 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),對(duì)排名還是有影響的。
另外,剔除友誼賽后,總的比賽場數(shù)更少了(只有 13 場),9 強(qiáng)之間有些隊(duì)伍沒有比賽,或者沒有贏過,這個(gè)數(shù)據(jù)用來分析的作用更有限。
當(dāng)然,在分析中 是否要剔除友誼賽,應(yīng)該是值得商榷的。
九強(qiáng)兩兩對(duì)陣的勝負(fù)情況概覽
這里,我們后續(xù)分析采用包含友誼賽的數(shù)據(jù),來分別分析9強(qiáng)之間兩兩對(duì)陣的情況,看看哪支球隊(duì)的勝率更高些。
首先自定義幾個(gè)函數(shù),方便進(jìn)行分析。自定義獲取球隊(duì)某年至今獲勝比例函數(shù):
- # 自定義獲取球隊(duì)某年至今獲勝比例函數(shù)
- 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
自定義獲取兩支球隊(duì)歷史獲勝情況對(duì)比函數(shù):
- # 自定義獲取兩支球隊(duì)歷史獲勝情況對(duì)比函數(shù)
- 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
自定義兩支球隊(duì)歷史獲勝情況制圖函數(shù):
- # 自定義兩支球隊(duì)歷史獲勝情況制圖函數(shù)
- 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)
這些函數(shù)有什么用呢,首先我們來分析下巴西 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 軸代表的含義是從某年至今(數(shù)據(jù)集含有部分 2018 年的比賽數(shù)據(jù)),兩支球隊(duì)的勝負(fù)情況。
例如 2012 對(duì)應(yīng)的是德國跟巴西從 2012 年至今,兩支球隊(duì)的勝負(fù)情況。所以,時(shí)間越早,兩支球隊(duì)的比賽數(shù)量越多,數(shù)據(jù)曲線的波動(dòng)可能要小些。
但由于球隊(duì)的成員組成在不斷的變化,會(huì)導(dǎo)致越早的數(shù)據(jù),其分析價(jià)值越弱。 因此,選擇合適的年份進(jìn)行分析就顯得很重要。
有童鞋說,如果我要同時(shí)分析德國對(duì)陣另外 8 支球隊(duì)呢?
這里,用上面的函數(shù),也是很迅速的,代碼如下:
- 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()
運(yùn)行上述代碼后,將會(huì)繪制 8 張圖,下面只放上其中幾張圖。
同理,如果你喜歡巴西隊(duì)或者別的球隊(duì),也可以用上述代碼進(jìn)行分析。
用上述函數(shù)可以快速的分析兩支球隊(duì)的歷史勝負(fù)情況,當(dāng)然,有些球隊(duì)之間,相遇很少,或者近些年沒有遭遇過,那分析結(jié)果可能就不好用了。
當(dāng)然,數(shù)據(jù)分析的只是歷史情況,足球是圓的,場上瞬息萬變。比如,阿根廷現(xiàn)在岌岌可危,梅西內(nèi)心慌得一比……
預(yù)測
本屆世界杯真的是爆冷太多:
- 意大利,荷蘭,連小組賽都沒進(jìn)
- 阿根廷,可以說現(xiàn)在已涼了半截
- 德國隊(duì),若不是最后的絕殺,也差不多可以送首涼涼了,不過現(xiàn)在看已回血大半
最后,來放上我的神預(yù)測。黑馬年年有,今年特別多,預(yù)測不準(zhǔn),坐等 pia pia 打臉。
當(dāng)然,其實(shí)我內(nèi)心深處希望是下面這樣的。怎么樣,為強(qiáng)大的內(nèi)心點(diǎn)贊吧~~
特別說明: 以上數(shù)據(jù)分析,純屬個(gè)人學(xué)習(xí)用,預(yù)測結(jié)果與實(shí)際情況可能偏差很大,不能用于其他用途。