Kaggle赛题-“疯狂的三月”NACC篮球赛预测(1)

比赛介绍及数据介绍

赛题地址

首先,很感谢以下几位作者的链接:

【Sports+AI】Kaggle体育比赛预测总结

2019MNACC第四名的解决方案与思路

以上解决方案的kaggle链接

raddar大神的 如何只根据比赛的对阵信息来确定球队的水平并排名

Paulo Pinto在讨论区分享的Ken Pom数据的链接

中文的EDA

python可视化

LGB, XGB, LogReg

动态可视化

更多的基础想法可以参见:https://www.kaggle.com/c/google-cloud-ncaa-march-madness-2020-division-1-mens-tournament/notebooks

赛题介绍

赛题为谷歌云和NACC合作对今年的NCAA男子和女子篮球锦标赛的比赛结果做出预测。

比赛分为两个阶段,目前位于阶段一:参赛选手利用锦标赛过去的结果构建与测试模型。第二阶段:等NACC在三月中开赛,进行真实比赛结果的预测。

赛题的本质:一个二分类问题,但本赛题采用对数损失函数作为评价指标,目的就是给过度自信或者不自信的判断更大的惩罚。

where

is the number of games played

is the predicted probability of team 1 beating team 2

is 1 if team 1 wins, 0 if team 2 wins

is the natural (base e) logarithm

def logloss(true_label, predicted, eps = 1e-15):
    p = np.clip(predicted, eps, 1-eps)   #clip这个函数将将数组中的元素限制在a_min, a_max之间,大于a_max的就使得它等于 a_max,小于a_min,的就使得它等于a_min。
    if true_label ==1:
        return - np.log(p)
    return -np.log(1-p)
print(f'Confident Wrong Prediction: \t\t {logloss(1, 0.01):0.4f}')      #logloss(0, 0.99)
print(f'Confident Correct Prediction: \t\t {logloss(0, 0.01):0.4f}')    #logloss(1, 0.99)
print(f'Non-Confident Wrong Prediction: \t {logloss(1, 0.49):0.4f}')    #logloss(0, 0.51)
print(f'Non-Confident Correct Prediction: \t {logloss(0, 0.49):0.4f}')  #logloss(1, 0.51)

结果如下:

Confident Wrong Prediction:          4.6052
Confident Correct Prediction:        0.0101
Non-Confident Wrong Prediction:      0.7133
Non-Confident Correct Prediction:    0.673

可以看出,过度自信or不自信会产生极大的误差,而比较中庸的评价(0.5左右)的损失相对来说可以接受。

数据介绍

0.分别读取男子和女子的数据

import os
os.getcwd()#获得当前工作目录
os.chdir('D:\\比赛\\')
import numpy as np
import pandas as pd
pd.set_option('display.max_columns', 100)

import matplotlib as mpl
from matplotlib.patches import Circle, Rectangle, Arc
from matplotlib import pyplot as plt
import seaborn as sns
%matplotlib inline

import warnings
warnings.filterwarnings("ignore")
import seaborn as sns
plt.style.use('seaborn-dark-palette')

mypal = plt.rcParams['axes.prop_cycle'].by_key()['color'] # Grab the color pal
import gc

men_folder_path = './kaggle/input/google-cloud-ncaa-march-madness-2020-division-1-mens-tournament/'
women_folder_path = './kaggle/input/google-cloud-ncaa-march-madness-2020-division-1-womens-tournament/'
Mstage1_folder_path = 'MDataFiles_Stage1/'
Wstage1_folder_path = 'WDataFiles_Stage1/'

#读取数据M
Mfolder_path = men_folder_path
Mteam_section1 = pd.read_csv(Mfolder_path+Mstage1_folder_path+'M'+'Teams.csv')
Mseason_section1 = pd.read_csv(Mfolder_path+Mstage1_folder_path+'M'+'Seasons.csv')
Mseed_section1 = pd.read_csv(Mfolder_path+Mstage1_folder_path+'M'+'NCAATourneySeeds.csv')
Mregular_section1 = pd.read_csv(Mfolder_path+Mstage1_folder_path+'M'+'RegularSeasonCompactResults.csv')
Mnacc_section1 = pd.read_csv(Mfolder_path+Mstage1_folder_path+'M'+'NCAATourneyCompactResults.csv')
Mregular_section1['is_regular'] = 1
Mnacc_section1['is_regular'] = 0
Mregular_detail_section1 = pd.read_csv(Mfolder_path+Mstage1_folder_path+'M'+'RegularSeasonDetailedResults.csv')
Mnacc_detail_section1 = pd.read_csv(Mfolder_path+Mstage1_folder_path+'M'+'NCAATourneyDetailedResults.csv')
Mregular_detail_section1['is_regular'] = 1
Mnacc_detail_section1['is_regular'] = 0

#读取数据W
Wfolder_path = women_folder_path
Wprefix = 'M'
Wteam_section1 = pd.read_csv(Wfolder_path+Wstage1_folder_path+'W'+'Teams.csv')
Wseason_section1 = pd.read_csv(Wfolder_path+Wstage1_folder_path+'W'+'Seasons.csv')
Wseed_section1 = pd.read_csv(Wfolder_path+Wstage1_folder_path+'W'+'NCAATourneySeeds.csv')
Wregular_section1 = pd.read_csv(Wfolder_path+Wstage1_folder_path+'W'+'RegularSeasonCompactResults.csv')
Wnacc_section1 = pd.read_csv(Wfolder_path+Wstage1_folder_path+'W'+'NCAATourneyCompactResults.csv')
Wregular_section1['is_regular'] = 1
Wnacc_section1['is_regular'] = 0
Wregular_detail_section1 = pd.read_csv(Wfolder_path+Wstage1_folder_path+'W'+'RegularSeasonDetailedResults.csv')
Wnacc_detail_section1 = pd.read_csv(Wfolder_path+Wstage1_folder_path+'W'+'NCAATourneyDetailedResults.csv')
Wregular_detail_section1['is_regular'] = 1
Wnacc_detail_section1['is_regular'] = 0
  1. Team Data
    包含了队伍的TeamID、TeamName、FirstD1Season、LastD1Season。
Mteam_section1.sort_values('FirstD1Season', ascending = False).head()
  • MTeams.csv and WTeams.csv
  • 球队信息,注意不是每个球队在每个赛季都有比赛,因为比赛数据只包含甲级部分。
  • 球队ID:标识一个大学的一只球队,4位数字,10001999表示男队,30003999表示女队,该ID是不会变的。
  • 球队大学名称的简洁写法。
  • 球队为1级联赛的第一年,最早是1985年,注意这部分只有男队。
  • 球队为1级联赛的最近的一年,如果目前依然是一级,那么该值为2020,同样也只有男队
  • 按照FirstD1Season列排序,我们可以看到D1篮球中的一些最新球队。 比如 新军Merrimack!

2.Seasons Data
包含了赛季、赛季起始日期(Dayzero 也就是比赛的0点 后边可以根据DayNum判断比赛进程)、每个赛季的四大赛区的名称。

Mseason_section1.head()
  • 不同赛季的历史数据,包含一些赛季属性。
  • 赛季表示比赛的年份,注意当前是2020赛季。
  • DayZero表示赛季开始的那一天的日期,结合DayNum可以计算当前日期。
  • RegionW,RegionX,RegionY,RegionZ表示四个分组区域,最后四强就是这4个区域中各自的最后冠军。

3.Tourney Seed Data
包含赛季、NACC锦标赛的种子信息、对应的TeamID。

Mseed_section1.head()

种子:格式为3/4字符组成,前3个如下,例如W01,表示W分区的1号种子,如果有第4位,可能是a/b,进一步区分前3个字符相同的球队。 球队Id。

将种子信息和Team Data合并

Wseed_section1.merge(Wteam_section1, validate='many_to_one').head()
  1. Regular Season Results
  • 常规赛所有比赛信息。
  • 赛季年份。
  • DayNum。
  • 胜利方的Id、分数,失败方的Id、分数。
  • 胜利方是主队、客队,还是在第三方球场。如果获胜团队是主队,则此值为“ H”home。如果获胜团队是客队,则该值为“ A”away。如果在中立的球场上进行比赛,则该值为“ N”neutral。
  • 加时次数,大于等于0.

查看主场获胜的情况和加时赛的情况

Mregular_section1['WLoc'].value_counts()
H    95878
A    49260
N    16414
Mregular_section1['NumOT'].value_counts()
0    155529
1      4989
2       844
3       152
4        32
5         5
6         1

将数据按加时赛数目降序进行展示

Mregular_section1.sort_values('NumOT', ascending=False).head()

查看WScore和Lscore的分布

'''
#bins ---> 箱数
#hist、ked ---> 是否显示箱/密度曲线
#norm_hist ---> 直方图是否按照密度来显示
#rug ---> 是否显示数据分布情况
#vertical ---> 是否水平显示
#color ---> 设置颜色
#label ---> 图例
#axlabel ---> x轴标注
'''
sns.distplot(Mregular_section1['WScore'], label = 'WScore', axlabel = '')
sns.distplot(Mregular_section1['LScore'], label = 'LScore', axlabel = '')
plt.legend()

将常规赛数据和TeamData进行合并
suffixes参数 get 避免写好多rename

#将Mregular_section1与Mteam_section1合并两次  加入TeamName FirstD1Season LastD1Season 等列 后缀W L 区分
Mregular_section1 = Mregular_section1.merge(Mteam_section1,left_on=['WTeamID'],right_on=['TeamID'],validate='many_to_one').drop('TeamID',axis=1)
Mregular_section1 = Mregular_section1.merge(Mteam_section1,left_on=['LTeamID'],right_on=['TeamID'],suffixes=('_W', '_L'),validate='many_to_one').drop('TeamID',axis=1)
#将Wregular_section1与Wteam_section1合并两次  加入TeamName列 后缀W L 区分
Wregular_section1 = Wregular_section1.merge(Wteam_section1,left_on=['WTeamID'],right_on=['TeamID'],validate='many_to_one').drop('TeamID',axis=1)
Wregular_section1 = Wregular_section1.merge(Wteam_section1,left_on=['LTeamID'],right_on=['TeamID'],suffixes=('_W', '_L'),validate='many_to_one').drop('TeamID',axis=1)
Mregular_section1.head()

分别看男子与女子比赛分差的分布

Mregular_section1['Score_Diff'] = Mregular_section1['WScore'] - Mregular_section1['LScore']
Wregular_section1['Score_Diff'] = Wregular_section1['WScore'] - Wregular_section1['LScore']
sns.distplot(a = Mregular_section1["Score_Diff"], bins=40, color="skyblue", hist=True, kde=False, rug=False, label = 'Men')
sns.distplot(a = Wregular_section1["Score_Diff"], bins=40, color="red", hist=True, kde=False, rug=False, label = 'Women')
plt.legend()

对常规赛胜场数进行统计并可视化

#引入counter  对每个队的胜场进行累计
Mregular_section1['counter'] = 1
Wregular_section1['counter'] = 1

plt.subplots(figsize=(6, 4))
Most_MWinTeam = pd.DataFrame(Mregular_section1.groupby(['TeamName_W'])['counter'].count().sort_values(ascending = False)[:10])
Most_MWinTeam = Most_MWinTeam.reset_index()
ax = sns.barplot(x='counter', y='TeamName_W', data = Most_MWinTeam, color='blue', orient='h')
ax.set_title('Most Winning (Regular Season) Mens Teams', fontsize=15)

plt.subplots(figsize=(20, 8))
Most_MLoseTeam = pd.DataFrame(Mregular_section1.groupby(['TeamName_L'])['counter'].count().sort_values(ascending = False)[:20])
Most_MLoseTeam = Most_MLoseTeam.reset_index()
ax = sns.barplot(x='counter', y='TeamName_L', data = Most_MLoseTeam, color='blue', orient='h')
ax.set_title('Most Losing (Regular Season) Mens Teams', fontsize=15)
  • 可以看出比赛具有明显的主场优势。
  • 曾在2009年出现过一次六加时。
  • 无论是胜分还是负分,整体均呈正太分布。
  • 无论男女,比赛的分差主要分布在0-20分,且男子比赛更为集中,可能是实力差距小造成的。
  • 在男子比赛中,Duke胜场最多,且很多强队例如Kansas、North carolina等也有着很高的胜场。而Chicago的负场最多。
  • 在女子比赛中,Connecticut胜场最多。而Air Force的负场最多。
  1. NCAA Season Results
  • NCAA锦标赛。
  • 赛季年份。
  • DayNum。
  • 胜利方的Id、分数,失败方的Id、分数。
  • 胜利方是主队、客队,还是在第三方球场。
  • 加时次数,大于等于0.

均为中立球场

#如果获胜团队是主队,则此值为“ H”。如果获胜团队是客队,则该值为“ A”。如果在中立的球场上进行比赛,则该值为“ N”。
Mnacc_section1['WLoc'].value_counts()
N    2251
Name: WLoc, dtype: int64

加时赛的次数 最多仅有一次三加时的出现

Mnacc_section1['NumOT'].value_counts()
0    2114
1     121
2      15
3       1
Name: NumOT, dtype: int64

该三加时出现在1995年

Mnacc_section1.sort_values('NumOT', ascending=False).head()

NACC中WScore和LScore的分布

sns.distplot(Mnacc_section1['WScore'], label = 'WScore', axlabel = '')
sns.distplot(Mnacc_section1['LScore'], label = 'LScore', axlabel = '')
plt.legend()

将NACC数据和TeamData合并 并得到分差进行可视化 并看一下男女比赛中最大分差

#将Mregular_section1与Mteam_section1合并两次  加入TeamName FirstD1Season LastD1Season 等列 后缀W L 区分
Mnacc_section1 = Mnacc_section1.merge(Mteam_section1,left_on=['WTeamID'],right_on=['TeamID'],validate='many_to_one').drop('TeamID',axis=1)
Mnacc_section1 = Mnacc_section1.merge(Mteam_section1,left_on=['LTeamID'],right_on=['TeamID'],suffixes=('_W', '_L'),validate='many_to_one').drop('TeamID',axis=1)
#将Wregular_section1与Wteam_section1合并两次  加入TeamName列 后缀W L 区分
Wnacc_section1 = Wnacc_section1.merge(Wteam_section1,left_on=['WTeamID'],right_on=['TeamID'],validate='many_to_one').drop('TeamID',axis=1)
Wnacc_section1 = Wnacc_section1.merge(Wteam_section1,left_on=['LTeamID'],right_on=['TeamID'],suffixes=('_W', '_L'),validate='many_to_one').drop('TeamID',axis=1)

Mnacc_section1['Score_Diff'] = Mnacc_section1['WScore'] - Mnacc_section1['LScore']
Wnacc_section1['Score_Diff'] = Wnacc_section1['WScore'] - Wnacc_section1['LScore']

sns.distplot(a = Mnacc_section1["Score_Diff"], color="skyblue", hist=True, kde=False, rug=False, label = 'Men')
sns.distplot(a = Wnacc_section1["Score_Diff"], color="red", hist=True, kde=False, rug=False, label = 'Women')
plt.legend()

Wnacc_section1["Score_Diff"].max()
89

Mnacc_section1["Score_Diff"].max()
58

对NACC的胜场数进行统计并可视化

#引入counter  对每个队的胜场进行累计
Mnacc_section1['counter'] = 1
Wnacc_section1['counter'] = 1

plt.subplots(figsize=(20, 8))
Most_MnaccWinTeam = pd.DataFrame(Mnacc_section1.groupby(['TeamName_W'])['counter'].count().sort_values(ascending = False)[:20])
Most_MnaccWinTeam = Most_MnaccWinTeam.reset_index()
ax = sns.barplot(x='counter', y='TeamName_W', data = Most_MnaccWinTeam, color='blue', orient='h')
ax.set_title('Most Winning (NACC Season) Mens Teams', fontsize=15)

plt.subplots(figsize=(20, 8))
Most_MnaccLoseTeam = pd.DataFrame(Mnacc_section1.groupby(['TeamName_L'])['counter'].count().sort_values(ascending = False)[:20])
Most_MnaccLoseTeam = Most_MnaccLoseTeam.reset_index()
ax = sns.barplot(x='counter', y='TeamName_L', data = Most_MnaccLoseTeam, color='blue', orient='h')
ax.set_title('Most Losing (NACC Season) Mens Teams', fontsize=15)

  • NACC均为中立球场,没有主场优势。
  • NACC曾在1995年出现过一次三加时,双加时也比较罕见。
  • 无论是胜分还是负分,整体均呈正太分布。
  • 无论男女,比赛的分差主要分布在0-20分,且男子比赛更为集中,可能是实力差距小造成的。女子的最大分差为89分,男子最大的的分差为58分。
  • 与常规赛相似,在男子比赛中,Duke胜场最多,且很多强队例如Kansas、North carolina等也有着很高的胜场。而能在负场上有着不错的排名也证明了球队有着不俗的实力,因为你需要进入NACC才有资格输,可以看到Duke、Kansas在负场榜也名列前茅。
  • 在女子比赛中,Connecticut胜场最多。而Stanford、Duke等的负场最多。

6.DetailedResults(Only Men)

  • 基本信息:
  • 自02-03赛季以来的各种比赛的罚球、防守篮板、失误等数据。
  • 与第1部分的CompactResult相比,前8列是一致的,从WTeamId到NumOT,但是这里会有其他很多数据,比如二分球个数、三分球个数、发球个数、犯规次数等等。
  • 最终分数可以由以下方式表示:2×FGM + FGM3 + FTM,即2×进球数+三分球数+罚球数=总分。
  • 这里的信息与第1部分中的比赛信息是严格对应的。
  • MRegularSeasonDetailedResults.csv
  • 03赛季以来所有非NCAA锦标赛以外的比赛的详细信息。
  • MNCAATourneyDetailedResults.csv
  • 03赛季以来所有NCAA锦标赛以外的比赛的详细信息。
  • 详细指标
  • WFGM:投篮命中数。
  • WFGA:未命中数。
  • WFGM3:3分命中数,注意这部分数据是包含在WFGM中的。
  • WFGA3:3分未命中数。
  • WFTM:罚球命中数。
  • WFTA:罚球未命中数。
  • WOR:进攻篮板。
  • WDR:防守篮板。
  • WAst:助攻数。
  • WTO:失误数。
  • WStl:抢断数。
  • WBlk:盖帽数。
  • WPF:个人犯规数。
  • 失败方为用L替换W,意思一致。
Mregular_detail_section1.head(3)

基础数据和细节数据在特征和数量上的差异

print(Mregular_section1.shape)
print(Mregular_detail_section1.shape)
print(Mnacc_section1.shape)
print(Mnacc_detail_section1.shape)
(161552, 17)
(87504, 35)
(2251, 17)
(1115, 35)
  • regular_section1 和 nacc_section1 一致 只含有Season、DayNum、WTeamID、WScore、LTeamID、LScore、WLoc、NumOT
  • regular_detail_section1 和 nacc_detail_section1 一致 且均有以上列
  • 唯一的区别是年份 前者始于1985 后者始于2003

将数据进行合并进行更多的讨论

#对于概况数据
#将常规赛和NCC比赛进行合并  并排序
section1 = Mregular_section1.append(Mnacc_section1, ignore_index=True).sort_values(by=['Season','DayNum'])
#将数据和seed_section1联结  加入输赢球队的赛区种子
#section1 = section1.merge(Mseed_section1,left_on=['Season','WTeamID'],right_on=['Season','TeamID']).drop('TeamID',axis=1)
#section1 = section1.merge(Mseed_section1,left_on=['Season','LTeamID'],right_on=['Season','TeamID'],suffixes=('_W', '_L')).drop('TeamID',axis=1)
#将数据和season_section1进行联结   加入DayZero RegionW RegionX RegionY RegionZ 等列
section1 = section1.merge(Mseason_section1,left_on=['Season'],right_on=['Season'])
#将数据和team_section1进行联结两次   加入TeamName FirstD1Season LastD1Season 等列 后缀W L 区分   前边已经进行过一次操作
#section1 = section1.merge(Mteam_section1,left_on=['WTeamID'],right_on=['TeamID']).drop('TeamID',axis=1)
#section1 = section1.merge(Mteam_section1,left_on=['LTeamID'],right_on=['TeamID'],suffixes=('_W', '_L')).drop('TeamID',axis=1)
section1 = section1.sort_values(by=['Season','DayNum'])
section1.shape

#对于详细数据
section1_detail = Mregular_detail_section1.append(Mnacc_detail_section1, ignore_index=True).sort_values(by=['Season','DayNum'])
#section1_detail = section1_detail.merge(Mseed_section1,left_on=['Season','WTeamID'],right_on=['Season','TeamID']).drop('TeamID',axis=1)
#section1_detail = section1_detail.merge(Mseed_section1,left_on=['Season','LTeamID'],right_on=['Season','TeamID'],suffixes=('_W', '_L')).drop('TeamID',axis=1)
section1_detail = section1_detail.merge(Mseason_section1,left_on=['Season'],right_on=['Season'])
section1_detail = section1_detail.merge(Mteam_section1,left_on=['WTeamID'],right_on=['TeamID']).drop('TeamID',axis=1)
section1_detail = section1_detail.merge(Mteam_section1,left_on=['LTeamID'],right_on=['TeamID'],suffixes=('_W', '_L')).drop('TeamID',axis=1)
section1_detail = section1_detail.sort_values(by=['Season','DayNum'])
section1_detail.shape

Count of Champion and second(冠军、亚军的次数可视化)

##利用DayNum == 154  将每一年的决赛取出
final_round = section1.query('DayNum == 154')
#plt.figure来设置窗口尺寸。
plt.subplots(figsize=(20, 8))
#wspace = 0.2 为子图之间的空间保留的宽度,平均轴宽的一部分
#hspace = 0.2 为子图之间的空间保留的高度,平均轴高度的一部分
plt.subplots_adjust(wspace=0, hspace=.6)

plt.subplot(2,1,1)
#rotation代表lable显示的旋转角度。
plt.xticks(rotation=30)
sns.countplot(x="TeamName_W", data=final_round)

plt.subplot(2,1,2)
plt.xticks(rotation=30)
sns.countplot(x="TeamName_L", data=final_round)

每一年决赛的分数走势

final_round['Score_diff'] = final_round['WScore'] - final_round['LScore']
sns.lineplot(data=final_round[['Season','WScore','LScore','Score_diff']].set_index('Season'))
plt.legend(loc = 1)
  • 冠军数最多的是杜克大学,共获得5次锦标赛冠军,其次是北卡和康涅狄格州,分别获得4次冠军
  • 亚军数最多的是杜克和密歇根大学,均为4次,其次是堪萨斯的3次
  • 近年来决赛的分差较小

对进32强、16强、8强、4强、决赛的次数进行统计并可视化

#根据DayNum确定比赛阶段  根据TeamName_W进行分组 对Season进行累计
team32 = section1.query('DayNum == 136 or DayNum == 137').groupby('TeamName_W')['Season'].count().rename('Count32').reset_index().sort_values(by='Count32',ascending=False).iloc[:10]
team16 = section1.query('DayNum == 138 or DayNum == 139').groupby('TeamName_W')['Season'].count().rename('Count16').reset_index().sort_values(by='Count16',ascending=False).iloc[:10]
team8 = section1.query('DayNum == 143 or DayNum == 144').groupby('TeamName_W')['Season'].count().rename('Count8').reset_index().sort_values(by='Count8',ascending=False).iloc[:10]
team4 = section1.query('DayNum == 145 or DayNum == 146').groupby('TeamName_W')['Season'].count().rename('Count4').reset_index().sort_values(by='Count4',ascending=False).iloc[:10]
team2 = section1.query('DayNum == 152').groupby('TeamName_W')['Season'].count().rename('Count2').reset_index().sort_values(by='Count2',ascending=False).iloc[:10]

plt.subplots(figsize=(20, 10))

plt.subplot(2,3,1)
plt.xticks(rotation=30)
sns.barplot(x="TeamName_W", y='Count32', data=team32)
plt.subplot(2,3,2)
plt.xticks(rotation=30)
sns.barplot(x="TeamName_W", y='Count16', data=team16)
plt.subplot(2,3,3)
plt.xticks(rotation=30)
sns.barplot(x="TeamName_W", y='Count8', data=team8)
plt.subplot(2,3,4)
plt.xticks(rotation=30)
sns.barplot(x="TeamName_W", y='Count4', data=team4)
plt.subplot(2,3,5)
plt.xticks(rotation=30)
sns.barplot(x="TeamName_W", y='Count2', data=team2)
  • 进入32强最多的球队是堪萨斯的32次
  • 进入16强最多的球队是杜克的25次
  • 进入8强最多的球队是肯塔基的17次;
  • 进入4强最多的球队是杜克的12次;
  • 进入决赛最多的球队是杜克的9次;
  • 其中结合之前的分析,杜克大学9次进入决赛,最终获得5次冠军,4次亚军的傲人成绩

看一下每一赛季NACC比赛阶段的平均分差

#section1.groupby(['is_regular','DayNum'])['Season'].count()
tournament = section1.query('DayNum >= 134 and DayNum <= 154')
tournament['ScoreDiv'] = tournament['WScore'] - tournament['LScore']
tournament = tournament.groupby('Season')[['WScore','LScore','ScoreDiv']].mean().reset_index()

plt.subplots(figsize=(20, 5))
sns.barplot(x="Season", y='ScoreDiv', data=tournament)
  • 锦标赛平均分差最大的是1993年
  • 锦标赛平均分差最小的是1985年
  • 近5年平均分差在11分左右
  • 各个赛季之间差异不大

将决赛的数据和种子信息和赛区信息进行合并 探究种子号数和赛区和夺冠之间的关系

  • 种子长度为4的提取中间两位 为3的提取后两位
  • 种子第一位可以获取属于哪个赛区 和Region合并 再取对应列的数据 即为赛区
section1 = section1.merge(Mseed_section1,left_on=['Season','WTeamID'],right_on=['Season','TeamID']).drop('TeamID',axis=1)
section1 = section1.merge(Mseed_section1,left_on=['Season','LTeamID'],right_on=['Season','TeamID'],suffixes=('_W', '_L')).drop('TeamID',axis=1)
final_round = section1.query('DayNum == 154')

'''
其中第一个字符是W,X,Y或Z(标识团队所在的区域)
后两个数字(01、02, ...,15或16)告诉您该区域内的种子。
对于参加比赛的球队,第四个字符(a或b)可以进一步区分种子,因为在参加比赛的比赛中彼此面对的球队将拥有前三个角色相同的种子。
'''
#根据Seed的定义   对其进行切分  取出所在区域  并得到种子号
final_round['Seed_W_num'] = final_round.Seed_W.apply(lambda seed:int(seed[1:]) if len(seed)==3 else int(seed[1:-1]))
final_round['Seed_L_num'] = final_round.Seed_L.apply(lambda seed:int(seed[1:]) if len(seed)==3 else int(seed[1:-1]))
final_round['Seed_W_code'] = final_round.Seed_W.apply(lambda seed:'Region'+seed[0])
final_round['Seed_L_code'] = final_round.Seed_L.apply(lambda seed:'Region'+seed[0])
#根据Seed_L_code确定赛区
final_round['Seed_W_region'] = final_round.apply(lambda row:row[row['Seed_W_code']], axis=1)
final_round['Seed_L_region'] = final_round.apply(lambda row:row[row['Seed_L_code']], axis=1)


plt.subplots(figsize=(20, 10))
plt.subplots_adjust(wspace=0.2, hspace=0.4)
plt.subplot(3,1,1)
sns.countplot(x="Seed_W_num", data=final_round)
plt.subplot(3,1,2)
sns.countplot(x="Seed_W_region", data=final_round)
plt.subplot(3,1,3)
sns.lineplot(x="Season", y="Seed_W_num", estimator=None, data=final_round)
  • 各个分区的1号种子依然是冠军的大热门,共22次获得冠军,且冠军数量与种子号基本线性负相关,这说明赛前的种子指定的还是相当准确的
  • 赛区上看,Midwest获得冠军最多,其次是South,这说明在赛区这一个维度上,实例是不太平衡的,这点跟NBA不太一样,NBA由于存在这工资帽这种机制,使得其各队实力相对会被限制到一个范围内,而大学则是有篮球名校的存在的
  • 从获得冠军的球队种子号的趋势图看,冠军越来越难以出现完全的黑马,基本上以1,2,3号种子中出现,当然了,并不是说3号种子就不是黑马

本节完

下节带来 - 比赛详细统计数据可视化

你可能感兴趣的:(Kaggle赛题-“疯狂的三月”NACC篮球赛预测(1))