背景
目前,NBA常规赛的球队排名按胜率从高到底依次排列。这样的排名方式非常简单,容易被大众普遍接受。但是,由于NBA常规赛赛制的非对称循环的特点,这样的排名规则并非完全公平,因此本文将尝试建立更合理的NBA常规赛的球队排名的数学模型。
常规赛赛制
NBA总共有30支球队,分为东部联盟,西部联盟,各15支球队。每个联盟各有3个赛区,每个赛区5支球队。基于球队所在城市之间的距离,82场常规赛的安排情况如下:
- 和不同联盟的球队打2场比赛,主客场各1场(总计30场);
- 和同赛区的球队打4场,各2个主场(总计16场);
- 和同联盟不同赛区的4支球队打3场(总计12场)6支球队打4场(合计24场);这其中打3场的球队是与这支球队上赛季战绩相差较大的,而且球队距离较远,由电脑编排的。
在NBA常规赛赛制中,每个球队间相互比赛的次数并不相同,往往存在有队伍因不幸多次遇到较强的队伍而输掉比赛,而碰到弱队的场次又很少,根据传统排名,该队伍就会被错误地排在排行榜的下游。
理论依据
排名的目的是根据比赛成绩排出反映各队真实水平的一个顺序,由于NBA常规赛赛制的非对称循环的特点,按胜率从高到底依次排列方法缺乏一定的公平性。因此,本文尝试建立能够处理不同场次的权重的NBA常规赛的球队排名的数学模型。
因此本文将根据竞争图法和层次分析法的原理,通过python建立NBA常规赛的球队排名的数学模型。
数据
30个球队编号
2018-2019赛程整理
代码
import numpy as np
import os
import pandas as pd
from pandas import Series,DataFrame
def score(df):
num = np.zeros([30,30]) # 比赛场次矩阵
score = np.zeros([30,30]) # 累计得分矩阵
win_score = np.zeros([30,30]) # 胜利得分矩阵
# 根据每场比赛,填充上面3个矩阵
for i in range(len(df)):
x = df['客队'][i]-1
y = df['主队'][i]-1
num[x][y] += 1 # 每一轮比赛增加一场比赛场数
num[y][x] += 1
a = int(df['比分'][i].split('-')[1])-int(df['比分'][i].split('-')[0])
if(df['赛果'][i]=='胜'):
win_score[y][x] += 1 # 增加胜者场数
win_score[x][y] -= 1
score[y][x] += a # 增加累计赢了多少分
score[x][y] -= a
if(df['赛果'][i]=='负'):
win_score[y][x] -= 1
win_score[x][y] += 1
score[y][x] += a
score[x][y] -= a
for i in range(len(score)):
for j in range(len(score)):
if (win_score[i][j]>0):
win_score[i][j] = 2 * win_score[i][j] # 净胜一场计2分
if(score[i][j]/num[i][j] > 15): # 平均每场赢球15分以上,增加胜利得分1分
win_score[i][j] += 1
for i in range(len(score)):
for j in range(len(score)):
if(i == j):
win_score[i][j] = 1 # 自己跟自己记为1
if (win_score[i][j] < 0):
win_score[i][j] = 1 / win_score[j][i] # 负数时为正数的倒数
if (win_score[i][j] == 0): # 其他情况即为两球队互有胜负,记为1
win_score[i][j] = 1
return win_score
def normalization(data): # 归一化矩阵
_range = np.max(data) - np.min(data)
return (data - np.min(data)) / _range
def final_score(win_score0): # 得到最终得分矩阵
for i in range(len(win_score0)):
win_score0[:,i] = normalization(win_score0[:,i])
final_score = np.zeros(30)
for i in range(len(final_score)):
final_score[i] = win_score0[i,:].sum()
return final_score
def rank(df0,final_score): # 根据最终得分矩阵,按序号给球队排名
df_fianl = df0
df_fianl['排名'] = 0
rank = np.argsort(final_score)
for i in range(0,len(rank)):
df_fianl['排名'][rank[i]] = 30-i
return df_fianl
if __name__ == '__main__':
# 导入数据
file=os.path.dirname(os.path.abspath('__file__'))#返回代码所在目录
data_file=os.path.join(file,'2018-2019赛程整理.csv')#返回数据所在文件位置
df=pd.read_csv(data_file,engine='python',encoding='gbk')
data_file=os.path.join(file,'30个球队编号.csv')#返回数据所在文件位置
df0=pd.read_csv(data_file,engine='python',encoding='gbk')
#得到分数矩阵
win_score = score(df)
#得到球队最终分数
final_score = final_score(win_score)
#得到球队最终排名
df_fianl = rank(df0,final_score)
#导出最终排位表格
if(os.path.exists(os.path.join(file,'层次分析法2019.csv'))):#如果输出结果表格已经存在,则删除
os.remove(os.path.join(file,'层次分析法2019.csv'))
df_fianl.to_csv('层次分析法2019.csv',index=False,encoding='gbk') #去掉index,输出表格csv