首页/新闻资讯/正文详情

NBA 15-18赛季数据包实战:Python数据分析与Elo等级分计算

发布时间:2026/9/23 14:54:54 来源:云帆数科 栏目:资讯中心
NBA 15-18赛季数据包实战:Python数据分析与Elo等级分计算
简介这份资源面向具备一定Python基础、希望上手真实数据分析项目的高校学生与数据爱好者围绕NBA比赛数据展开提供从数据采集到可视化呈现的完整实践素材。压缩包共14个文件约245KB以11个CSV数据表为主涵盖球队与对手的场均统计、赛程、赛果及杂项数据另含1个Python分析脚本、1份PDF说明文档和1个TXT文件分别用于数据处理、指标解释与辅助说明。资源已积累506人学习下载热度较为稳定。读者可借助现成的多赛季数据表直接练习数据清洗、指标计算与图表绘制结合脚本理解爬取与分析流程并通过Elo等级分定义文档补充评价模型知识适合作为课程作业、练手项目或数据分析入门的参考案例。1. 从一份 15-18 赛季的 NBA 数据包说起它到底能跑出什么很多人第一次做体育数据分析卡住的不是代码而是数据。网上找的 CSV 要么字段残缺要么赛季对不上要么爬下来的表头全是英文缩写看不懂。这份资源包解决的就是这个前置问题它把 2015-2016、2016-2017、2017-2018 三个赛季的 NBA 常规赛数据整理成了可直接读取的 CSV配套一个Analysis_NBA_Data.py分析脚本、一份 Elo 等级分定义的 PDF 说明文档以及一个data目录做数据归档。换句话说你拿到手就能跳过「找数据、洗字段」这两步直接进入 Python 数据分析和可视化环节。它适合三类人正在学 pandas 想找个真实数据集练手的入门者、需要快速验证某个分析思路的从业者、以及想用 NBA 数据做可视化大屏或课程作业的学生。三个赛季的跨度不算长但足够做趋势对比、球队强弱演变、主客场胜率差异这类分析。下面我按「数据长什么样 → 怎么读进来 → 怎么算 → 怎么画 → 坑在哪」的顺序拆一遍。2. 数据包结构拆解三张核心表和两个辅助文件怎么配合2.1 文件清单与字段职责先把包里的东西按功能分个类不然后面读文件容易乱。核心数据分三块赛季结果表、球队场均统计表、对手场均统计表。辅助文件是赛程表和杂项统计表。文件覆盖赛季主要用途2015-2016_results.csv/2016-2017_results.csv/17-18Result.csv15-18每场比赛的胜负结果、比分15-16Team_Per_Game_Stats.csv/16-17Team_Per_Game_Stats.csv15-17球队场均得分、篮板、助攻等15-16Opponent_Per_Game_Stats.csv/16-17Opponent_Per_Game_Stats.csv15-17对手视角的场均数据用于对比16-17Schedule.csv/17-18Schedule.csv16-18赛程、主客场、日期15-16Miscellaneous_Stats.csv/16-17Miscellaneous_Stats.csv15-17杂项统计如犯规、失误等Analysis_NBA_Data.py—主分析脚本Elo等级分定义.pdf—Elo 算法的定义和计算说明注意文件名不统一15-16 和 16-17 赛季用Team_Per_Game_Stats命名17-18 赛季的结果文件叫17-18Result.csv没有对应的 Team Stats 文件。这意味着做跨赛季对比时17-18 赛季只能从结果表和赛程表入手场均统计需要另找或从结果表反推。这是第一个要留意的边界。2.2 用 pandas 批量读取并统一列名文件名不统一直接写死路径会很难维护。我一般用 glob 匹配加字典映射的方式批量读顺便把列名统一成小写下划线格式。import pandas as pd import glob import os # 按赛季分组读取避免文件名不一致导致漏读 season_files { 2015-2016: { results: 2015-2016_results.csv, team_stats: 15-16Team_Per_Game_Stats.csv, opp_stats: 15-16Opponent_Per_Game_Stats.csv, misc: 15-16Miscellaneous_Stats.csv }, 2016-2017: { results: 2016-2017_results.csv, team_stats: 16-17Team_Per_Game_Stats.csv, opp_stats: 16-17Opponent_Per_Game_Stats.csv, misc: 16-17Miscellaneous_Stats.csv }, 2017-2018: { results: 17-18Result.csv, schedule: 17-18Schedule.csv } } def load_season(season, file_map, base_dirdata): 读取单个赛季的所有 CSV统一列名格式 dfs {} for key, fname in file_map.items(): path os.path.join(base_dir, fname) if not os.path.exists(path): print(f[跳过] {season} 的 {key} 文件不存在: {path}) continue df pd.read_csv(path) # 列名统一去空格、转小写、空格换下划线 df.columns [c.strip().lower().replace( , _) for c in df.columns] dfs[key] df print(f[读取] {season} {key}: {df.shape[0]} 行 x {df.shape[1]} 列) return dfs all_data {} for season, fmap in season_files.items(): all_data[season] load_season(season, fmap)这段代码的关键点有三个。第一用字典把赛季和文件映射起来而不是用glob.glob(*.csv)一把梭因为包里混了赛程、杂项、结果多种表全读进来后面还得靠列名猜哪张是哪张。第二os.path.exists做存在性检查17-18 赛季缺 Team Stats 文件时不会直接抛异常中断。第三列名统一处理是必须的原始 CSV 里列名可能带空格或大小写不一致不统一后面df[Team]和df[team]会来回翻车。读完之后建议先看一眼每张表的列名和前几行确认字段含义。比如结果表里通常有date、visitor、home、visitor_pts、home_pts这类字段球队统计表里是team、fg%、3p%、reb、ast等。字段名对不上时以实际读出来的为准不要照搬记忆。3. 从胜负表到 Elo 等级分核心计算逻辑与脚本参数3.1 Elo 算法的输入输出与 K 值选择包里那份Elo等级分定义.pdf是整个分析的理论核心。Elo 原本用于国际象棋核心思想是每场比赛后根据预期胜率和实际结果调整双方分数。预期胜率公式是E_home 1 / (1 10^((R_away - R_home HFA) / 400))其中R_home和R_away是主客队当前等级分HFA是主场优势加成400 是缩放常数。赛后更新R_home_new R_home K * (S_home - E_home)S_home是实际结果胜 1 负 0K是更新幅度。K 值越大分数波动越剧烈。NBA 一个赛季 82 场我一般把 K 设在 20 左右主场优势 HFA 设在 100 分上下。这两个参数没有绝对标准但 K 太大排名会抖K 太小赛季末排名又太黏。3.2 用结果表跑一遍 Elo 并输出排名下面这段代码从结果表出发按日期排序逐场更新最后输出赛季末排名。def compute_elo(results_df, k20, hfa100, init_rating1500): 基于比赛结果计算 Elo 等级分 results_df: 需包含 date, home, visitor, home_pts, visitor_pts 列 k: 更新系数控制单场波动 hfa: 主场优势加成 init_rating: 初始分 ratings {} # 按日期排序确保按时间顺序更新 df results_df.sort_values(date).reset_index(dropTrue) for _, row in df.iterrows(): home row[home] away row[visitor] # 首次出现的球队给初始分 ratings.setdefault(home, init_rating) ratings.setdefault(away, init_rating) r_home ratings[home] r_away ratings[away] # 预期胜率 e_home 1 / (1 10 ** ((r_away - r_home hfa) / 400)) # 实际结果 s_home 1.0 if row[home_pts] row[visitor_pts] else 0.0 # 更新分数 ratings[home] r_home k * (s_home - e_home) ratings[away] r_away k * ((1 - s_home) - (1 - e_home)) # 转成 DataFrame 并按分数降序 elo_df pd.DataFrame(list(ratings.items()), columns[team, elo]) elo_df elo_df.sort_values(elo, ascendingFalse).reset_index(dropTrue) elo_df[rank] elo_df.index 1 return elo_df # 对 2015-2016 赛季跑一遍 elo_1516 compute_elo(all_data[2015-2016][results]) print(elo_1516.head(10))逻辑说明sort_values(date)保证按时间顺序更新如果日期格式不统一比如有的带时间有的不带先用pd.to_datetime转一下。setdefault处理首次出现的球队避免 KeyError。更新公式里客队的实际结果是1 - s_home预期胜率是1 - e_home两者相减就是客队的调整量。参数方面k20适合常规赛如果做季后赛可以调到 30 让波动更大。hfa100是经验值你可以跑几组不同 HFA 看排名变化如果主场胜率明显高于 55%HFA 可以往上加。init_rating1500是通用起点所有球队从同一起跑线开始赛季初几场波动大是正常的。跑完之后可以拿排名和实际战绩对比如果某支球队 Elo 排名和胜率排名差很多说明它赢的比赛含金量高对手强这本身就是个有意思的分析点。4. 可视化落地用 matplotlib 和 seaborn 出四张能看的图4.1 球队场均得分分布与主客场差异数据可视化不是把数字画出来就完事得让图能回答问题。第一张图我一般做球队场均得分的分布顺便对比主客场。import matplotlib.pyplot as plt import seaborn as sns # 设置中文字体避免中文乱码 plt.rcParams[font.sans-serif] [SimHei] plt.rcParams[axes.unicode_minus] False def plot_team_scoring(team_stats_df, top_n15): 画球队场均得分横向条形图 df team_stats_df.copy() # 找到得分列不同赛季列名可能不同 pts_col [c for c in df.columns if pts in c or points in c] team_col [c for c in df.columns if team in c] if not pts_col or not team_col: print(未找到得分或球队列请检查列名) return df df[[team_col[0], pts_col[0]]].dropna() df.columns [team, pts] df df.sort_values(pts, ascendingFalse).head(top_n) fig, ax plt.subplots(figsize(10, 6)) sns.barplot(datadf, xpts, yteam, axax, paletteviridis) ax.set_title(f球队场均得分 Top {top_n}) ax.set_xlabel(场均得分) ax.set_ylabel(球队) plt.tight_layout() plt.savefig(team_scoring.png, dpi150) plt.show() plot_team_scoring(all_data[2015-2016][team_stats])这段代码里pts_col和team_col用列表推导找列是因为不同赛季 CSV 的列名可能有细微差异写死df[pts]容易 KeyError。dropna去掉缺失值head(top_n)只画前 15 名避免图太挤。paletteviridis是配色方案换成coolwarm或Blues也行。保存用dpi150够清晰又不至于文件太大。4.2 Elo 排名随时间变化的折线图第二张图做 Elo 分数的时间序列能看出哪支球队赛季中段发力、哪支后程崩盘。这需要在计算 Elo 时把每场比赛后的分数存下来而不是只存最终值。def compute_elo_history(results_df, k20, hfa100, init_rating1500): 计算 Elo 并记录每场比赛后的分数变化 ratings {} history [] # 每场比赛后记录一次 df results_df.sort_values(date).reset_index(dropTrue) for _, row in df.iterrows(): home, away row[home], row[visitor] ratings.setdefault(home, init_rating) ratings.setdefault(away, init_rating) r_home, r_away ratings[home], ratings[away] e_home 1 / (1 10 ** ((r_away - r_home hfa) / 400)) s_home 1.0 if row[home_pts] row[visitor_pts] else 0.0 ratings[home] r_home k * (s_home - e_home) ratings[away] r_away k * ((1 - s_home) - (1 - e_home)) history.append({ date: row[date], team: home, elo: ratings[home] }) history.append({ date: row[date], team: away, elo: ratings[away] }) return pd.DataFrame(history) hist_df compute_elo_history(all_data[2015-2016][results]) # 选几支球队画折线 focus_teams [Golden State Warriors, Cleveland Cavaliers, San Antonio Spurs] focus_df hist_df[hist_df[team].isin(focus_teams)] fig, ax plt.subplots(figsize(12, 6)) for team in focus_teams: team_df focus_df[focus_df[team] team].sort_values(date) ax.plot(team_df[date], team_df[elo], labelteam, linewidth1.5) ax.set_title(2015-2016 赛季 Elo 等级分变化) ax.set_xlabel(日期) ax.set_ylabel(Elo 分数) ax.legend() plt.xticks(rotation45) plt.tight_layout() plt.savefig(elo_trend.png, dpi150) plt.show()注意focus_teams里的队名必须和 CSV 里的写法完全一致大小写、缩写都要对。如果 CSV 里是GSW而不是Golden State Warriors这里就得改。日期列如果是字符串ax.plot也能画但排序可能不对建议先pd.to_datetime转成日期类型。另外两张图可以做主客场胜率对比的热力图、球队得分与失分的散点图。思路类似核心是先把数据整理成x和y两列再交给 seaborn。不要一上来就追求花哨先把每张图要回答的问题写清楚再选图型。5. 避坑与排查跑这份数据包时最容易翻车的五个地方5.1 日期格式不统一导致排序错乱现象Elo 计算出来的排名和实际战绩对不上强队分数偏低。原因结果表里date列可能是2015-10-27和10/27/2015混着来字符串排序会把10/27/2015排到2015-10-27前面。解决读进来后统一df[date] pd.to_datetime(df[date], errorscoerce)再sort_values(date)。errorscoerce把解析失败的变成 NaT后面可以dropna掉。5.2 队名写法不一致导致 Elo 分数分裂现象同一支球队在排名表里出现两次分数各算各的。原因CSV 里有的写New York Knicks有的写NY Knickssetdefault会当成两支球队。解决先跑一遍df[home].unique()和df[visitor].unique()把队名列表打印出来人工核对或者建一个映射字典统一缩写。这个坑不排查后面所有分析都是错的。5.3 17-18 赛季缺 Team Stats 文件现象想画三个赛季的场均得分对比发现 17-18 赛季没有Team_Per_Game_Stats.csv。原因资源包本身就没放这个文件只有结果表和赛程表。解决要么只用 15-17 两个赛季做对比要么从17-18Result.csv里按球队分组算场均得分。后者需要先确认结果表里有home_pts和visitor_pts两列然后groupby球队求均值。5.4 列名带空格或特殊字符现象df[FG%]报 KeyError。原因原始 CSV 列名可能是FG%或fg %或Field Goal Percentage。解决读进来先print(df.columns.tolist())看一眼然后用df.columns [c.strip().lower().replace( , _) for c in df.columns]统一。百分号列名建议改成fg_pct这种避免后续引用时转义麻烦。5.5 matplotlib 中文显示为方块现象图标题和轴标签里的中文全是方框。原因matplotlib 默认字体不含中文。解决plt.rcParams[font.sans-serif] [SimHei]Windows 下一般有 SimHeiMac 下换成[Arial Unicode MS]Linux 下如果没装中文字体需要先安装或换成英文标签。另外plt.rcParams[axes.unicode_minus] False解决负号显示问题。6. 进阶技巧把 Elo 和场均数据交叉验证判断排名可信度跑完 Elo 和场均统计后我习惯做一件事把两个排名放在一起对比。如果某支球队 Elo 排第 3 但场均得分排第 12说明它赢球靠的是防守或关键球而不是火力压制。这种交叉验证能帮你判断数据包的分析结果是否自洽。具体做法是先拿到 Elo 排名表elo_df再从team_stats里算出场均得分排名然后按球队名 merge。def cross_validate(elo_df, team_stats_df): 交叉对比 Elo 排名和场均得分排名 df team_stats_df.copy() pts_col [c for c in df.columns if pts in c][0] team_col [c for c in df.columns if team in c][0] df df[[team_col, pts_col]].dropna() df.columns [team, pts] df[pts_rank] df[pts].rank(ascendingFalse) merged elo_df.merge(df, onteam, howinner) merged[rank_diff] merged[rank] - merged[pts_rank] # 差异大的球队值得单独看 merged[abs_diff] merged[rank_diff].abs() return merged.sort_values(abs_diff, ascendingFalse) result cross_validate(elo_1516, all_data[2015-2016][team_stats]) print(result[[team, elo, rank, pts, pts_rank, rank_diff]].head(10))rank_diff为正说明 Elo 排名比得分排名靠后得分强但赢球少为负说明 Elo 排名更靠前得分一般但能赢。abs_diff排序后前几支球队就是最值得深挖的对象。这个技巧不复杂但能让你从「跑出结果」进阶到「解释结果」。从那以后我每次拿到新的赛季数据包都会先跑一遍队名一致性检查和日期格式检查再开始算 Elo。这两个检查花不了五分钟但能省掉后面几小时的排查。希望帮到你。本文还有配套的精品资源点击获取

相关推荐

搞定httpwww:3个性能优化点让你代码跑通
搞定httpwww:3个性能优化点让你代码跑通

搞定httpwww:3个性能优化点让你代码跑通 复制来的 httpwww 相关代码,是不是经常报错?别急,这通常是环境配置或底层原理没搞懂。 面试中被问到 HTTP 性能优化,很多人只会背“加缓存”,其实细节才决定成败。 今天拆解… · 2026/9/23 14:54:48

富士X-T5中文手册高效查阅指南:从PDF到可检索参数体系
富士X-T5中文手册高效查阅指南:从PDF到可检索参数体系

简介:《FUJIFILM富士X-T5系列中文手册》是面向富士X-T5无反相机用户的操作指南,适合刚入手新机的新手快速上手,也为进阶摄影师提供深入的技术参考。手册从相机部件讲起,逐一说明对焦棒、快门速度与感光度拨盘、STILL/MOVIE模式拨盘… · 2026/9/23 14:54:35

财管综述炸雷[特殊字符]财务指标、模型公式堆砌,盲审模板重到离谱!
财管综述炸雷[特殊字符]财务指标、模型公式堆砌,盲审模板重到离谱!

财务管理、公司理财、企业绩效分析、投融资管理方向的同学全员破防! 财务管理文献综述,是经管类最容易“公式模板同质化、指标解读千篇一律”的重灾区! 综述高频覆盖:杜邦分析体系、企业盈利能力、偿债能力、营运能力、投融资决… · 2026/9/23 14:54:35

VINS-Mono框架拆解与相机IMU标定实战指南
VINS-Mono框架拆解与相机IMU标定实战指南

简介:这套PPT基于VSLAM与VINS-Mono框架介绍整理,面向计算机视觉初学者、SLAM方向研究生或需要做技术分享的开发者,帮助快速理解视觉同时定位与建图的核心概念及VINS-Mono的模块化实现。内容从VSLAM的前后端划分入手,覆盖传感器数据… · 2026/9/23 15:37:09

基于Python的学生校园消费行为分析与聚类建模实战
基于Python的学生校园消费行为分析与聚类建模实战

简介:面向高校学生与编程初学者的校园消费行为分析项目,紧密贴合期末大作业与课程设计场景。项目围绕学生校园消费数据展开,涵盖数据预处理、特征提取、行为分析、模型构建与可视化等完整流程;多个脚本按任务拆分,自带… · 2026/9/23 15:37:09

MASM 汇编语言 ANTLR4 语法解析:asmMASM 语法文件结构与实战示例详解
MASM 汇编语言 ANTLR4 语法解析:asmMASM 语法文件结构与实战示例详解

编程语言编译器开发工具 【免费下载链接】grammars-v4 Grammars written for ANTLR v4; expectation that the grammars are free of actions. 项目地址: https://gitcode.com/gh_mirrors/gr/grammars-v4 点击查看 免费下载 本文基于 grammars-v4 仓库中 asm/asmMA… · 2026/9/23 15:37:09

Presto Release 0.61 技术解析:VALUES 表值构造函数、Cassandra/S3 连接器增强与核心缺陷修复
Presto Release 0.61 技术解析:VALUES 表值构造函数、Cassandra/S3 连接器增强与核心缺陷修复

Presto Release 0.61 技术解析:VALUES 表值构造函数、Cassandra/S3 连接器增强与核心缺陷修复 【免费下载链接】presto The official home of the Presto distributed SQL query engine for big data 项目地址: https://gitcode.com/gh_mirrors/pre/presto P… · 2026/9/23 15:37:09

从蜘蛛到海星:连锁生意如何摆脱救火式管理,长出自主繁衍能力
从蜘蛛到海星:连锁生意如何摆脱救火式管理,长出自主繁衍能力

之前和一个做连锁小吃的朋友聊天,他四十多家门店,每天凌晨一点还捧着手机盯群:哪家店原料报损超标了、哪个员工又在朋友圈发情绪了、哪家店的卫生检查没过,桩桩件件都要他拍板。他跟我说了一句话,我印象特别深&#xf… · 2026/9/23 15:37:09

DeepSeek 接入 Excel 实战:公式生成、VBA 脚本与批量图表
DeepSeek 接入 Excel 实战:公式生成、VBA 脚本与批量图表

简介:这份资源围绕DeepSeek与Excel的协同应用展开,面向具备一定Excel基础、日常数据处理与分析任务较重的职场人士,帮助解决数据清洗繁琐、复杂公式编写困难、图表制作与可视化门槛高等痛点。内容涵盖DeepSeek的技术架构解析、API Key获取与E… · 2026/9/23 15:37:03

3招搞定手机怎么下载微信面试难题实战项目解析
3招搞定手机怎么下载微信面试难题实战项目解析

3招搞定手机怎么下载微信面试难题实战项目解析 面试被问“手机怎么下载微信”背后的原理,90%的人答不上来。别笑,这看似弱智的问题,实则是考察你对移动应用分发机制、安全校验及网络协议理解的试金石。我带过不少校招新人,他们背了八股文,却连一个A… · 2026/9/23 0:00:03

你有新短消息请注意查收:3个新手避坑指南搞定消息系统选型
你有新短消息请注意查收:3个新手避坑指南搞定消息系统选型

你有新短消息请注意查收:3个新手避坑指南搞定消息系统选型 面试被问“高并发下如何保证消息不丢失”,你张口就是“用Redis”,结果面试官追问“如果Redis宕机了怎么办”,你瞬间卡壳。这种场景太常见了,很多新手在背八股文时,只记住了技术名词… · 2026/9/23 0:00:29

Win7无线热点配置工具源码解析:解决API失效的3个实战技巧
Win7无线热点配置工具源码解析:解决API失效的3个实战技巧

Win7无线热点配置工具源码解析:解决API失效的3个实战技巧 Win7无线热点配置工具在Win10/11上跑不动?不是你的问题,是版本升级后 API 全变了。很多老项目里的 netsh wlan… · 2026/9/23 0:00:36

了解更多?预约专属演示

我们的顾问将为您一对一讲解产品与方案

企业微信二维码