新闻详情

新闻详情

首页 / 资讯中心 / 详情

用Python分析欧联杯球队数据:Pandas+Matplotlib实战教程

发布时间:2026/8/30 2:09:09
用Python分析欧联杯球队数据:Pandas+Matplotlib实战教程
当你想快速了解两支球队近期的真实状态与其翻遍十几篇战报不如用 Python 写一段脚本把胜率、进球数、失球数全部算出来再画成图。本文就以欧联杯比赛中安德莱赫特与塞萨洛尼基的对决为案例拆解一套从数据获取、清洗、特征构造到可视化对比的完整分析流程。无论你是数据分析初学者还是想给看球增加一点技术含量的球迷这篇教程都能直接上手。1. 背景为什么用 Python 分析欧联杯比赛数据1.1 数据分析在体育场景中的价值足球比赛的结果看似充满偶然性但一支球队的近期状态、攻防表现、主客场差异其实都会在历史数据中留下痕迹。单纯靠印象判断“谁强谁弱”很容易受单场爆冷或集锦误导而数据能帮我们把状态量化成可比较的指标。以欧联杯为例参赛球队来自不同联赛彼此之间的直接交锋记录很少。这个时候数据对比就显得更重要我们可以把两只球队最近 6 场、10 场或整个赛季的比赛数据拉出来计算胜率、场均进球、场均失球、射门转化率等指标。这些指标虽然不能直接决定谁赢谁输但能帮你建立一个相对客观的判断基础。1.2 本文案例安德莱赫特 vs 塞萨洛尼基这篇教程选取的案例是欧联杯比赛中安德莱赫特对阵塞萨洛尼基。安德莱赫特是比利时甲级联赛的传统强队塞萨洛尼基则是希腊足球超级联赛的劲旅。两支球队来自不同联赛体系直接交手样本很少因此更适合用数据分析的方式从各自近期的联赛和欧战表现入手进行对比。你会通过这个案例掌握以下能力获取比赛数据的两种方式公开 API 和本地数据集。用 Pandas 对数据进行清洗和筛选。计算球队近期胜率、场均进球、场均失球等特征。用 Matplotlib 绘制对比图表让结论一目了然。理解数据结论的边界避免过度解读。1.3 前置知识与环境要求本文的代码基于 Python 编写需要你具备最基本的 Python 语法基础比如列表、字典、循环、函数。如果你暂时没学过 Pandas 和 Matplotlib 也没关系文中会对每个关键函数做解释。需要强调的是本文所有示例数据均为模拟数据仅用于演示分析流程。真实场景中你需要接入合法授权的数据源具体方法会在第 3 节详细说明。2. 环境准备与版本说明2.1 运行环境说明本文示例以 Python 3.8 及以上版本为例操作系统使用 Windows 10/11、macOS 或 Linux 均可。不同操作系统在安装第三方库时命令略有差异但核心代码完全一致。如果你使用的是 Anaconda那么 Pandas、Matplotlib 通常已经预装不需要额外安装。如果你使用的是原生 Python则需要通过 pip 完成安装。2.2 安装第三方库需要安装的库有两个pandas用于数据处理和分析。matplotlib用于绘制图表。在命令行中执行以下命令pip install pandas matplotlib如果你使用的是国内网络可以加上国内镜像源加速例如pip install pandas matplotlib -i https://pypi.tuna.tsinghua.edu.cn/simple安装完成后可以通过导入测试确认安装成功python -c import pandas as pd; import matplotlib.pyplot as plt; print(OK)如果输出OK说明环境已经准备完成。2.3 项目目录结构为了方便管理代码和数据建议创建如下目录结构uefa_data_analysis/ ├── data/ │ └── matches.csv # 原始比赛数据 ├── output/ # 图表输出目录 ├── analysis.py # 主分析脚本 └── requirements.txt # 依赖清单requirements.txt的内容如下pandas1.3.0 matplotlib3.4.0创建好目录结构后我们进入数据获取环节。3. 数据获取如何拿到比赛数据3.1 公开 API 获取真实数据获取足球比赛数据最理想的途径是使用官方或第三方开放 API。常见的足球数据 API 包括 football-data.org、API-Football 等。这类接口通常需要注册并申请 API Key免费额度也会有限制。下面以 football-data.org 为例给出一个请求比赛数据的基础思路。注意不同 API 的认证方式和响应结构差别较大以下代码的核心目的是展示对接流程你需要根据实际使用的 API 官方文档调整。import requests # 替换为你的 API Key API_TOKEN your_api_token_here BASE_URL https://api.football-data.org/v4/ headers { X-Auth-Token: API_TOKEN } # 请求指定比赛日的比赛数据 # 具体参数和路径以官方文档为准 response requests.get( f{BASE_URL}/matches, headersheaders, params{ dateFrom: 2024-08-10, dateTo: 2024-08-15 } ) if response.status_code 200: data response.json() print(data.keys()) else: print(f请求失败: {response.status_code})在使用公开 API 时有几个关键点需要注意免费 API 通常有每分钟请求次数限制不要用循环密集请求。必须阅读 API 的服务条款确认数据是否可以用于分析、存储或二次分发。响应字段结构复杂建议先打印出原始 JSON 结构确认字段名后再编写解析逻辑。3.2 使用本地模拟数据做练习对于初次练习的读者我更推荐先用本地模拟数据跑通完整流程。这样做的好处是不需要注册 API Key、不需要处理网络异常可以专注于数据处理本身。模拟数据虽然不能反映真实比赛但数据结构和真实数据相似分析代码以后可以直接复用到真实数据集上。3.3 数据合规与来源选择这里必须强调数据合规问题。足球比赛数据涉及版权和商业授权在使用任何数据源之前建议确认以下几点该数据源的条款是否允许个人学习和分析使用。是否标注了数据来源。是否禁止二次转发或商业化使用。本文的模拟数据仅用于学习流程使用真实数据时请务必遵守上述合规要求。4. 数据清洗与特征构造4.1 读取比赛数据集我们以 Pandas 为工具将比赛数据读取到 DataFrame 中。DataFrame 可以理解为一个二维表格每一行是一场比赛每一列是一个字段。首先我们把模拟比赛数据构造出来。为了贴近场景数据中包含两支球队各自最近的 6 场比赛每场比赛有主队、客队、主队进球、客队进球、赛事类型等字段。import pandas as pd # 模拟数据仅用于演示分析流程不代表真实比赛 data { 比赛日期: [ 2024-07-20, 2024-07-27, 2024-08-03, 2024-08-10, 2024-07-25, 2024-08-02, 2024-08-09, 2024-07-21, 2024-07-28, 2024-08-04, 2024-08-11, 2024-07-19 ], 赛事: [ 比甲, 比甲, 比甲, 欧联杯, 希超, 希超, 欧联杯, 希超, 希超, 希超, 欧联杯, 比甲 ], 主队: [ 安德莱赫特, 安德莱赫特, 安德莱赫特, 安德莱赫特, 塞萨洛尼基, 塞萨洛尼基, 塞萨洛尼基, 塞萨洛尼基, 塞萨洛尼基, 塞萨洛尼基, 安德莱赫特, 安德莱赫特 ], 客队: [ 对手A, 对手B, 对手C, 塞萨洛尼基, 对手X, 对手Y, 安德莱赫特, 对手Z, 对手W, 对手V, 对手U, 对手T ], 主队进球: [2, 1, 3, 1, 1, 0, 1, 2, 2, 3, 0, 1], 客队进球: [1, 1, 0, 2, 0, 1, 2, 1, 0, 1, 2, 0] } df pd.DataFrame(data) df[比赛日期] pd.to_datetime(df[比赛日期]) print(df)输出结果是一张完整的比赛记录表每一行都代表一场比赛。注意第 8 行这是一场安德莱赫特与塞萨洛尼基的直接交锋模拟比分为 1:2。4.2 提取指定球队的比赛记录在分析某支球队的状态时我们需要把所有包含该球队的比赛全部提取出来。判断条件要同时考虑球队出现在主队或客队的情况。下面定义一个函数输入球队名称返回该球队参与的所有比赛def get_team_matches(df, team_name): 从比赛数据中提取指定球队参与的所有比赛。 is_home df[主队] team_name is_away df[客队] team_name return df[is_home | is_away].copy() team_a_matches get_team_matches(df, 安德莱赫特) team_b_matches get_team_matches(df, 塞萨洛尼基) print(安德莱赫特参与的比赛) print(team_a_matches) print(\n塞萨洛尼基参与的比赛) print(team_b_matches)team_a_matches和team_b_matches是两个筛选后的 DataFrame。这里使用了 Pandas 的布尔索引is_home | is_away表示“只要满足其中一个条件就纳入结果”。4.3 计算近期胜率、场均进球、场均失球提取出球队的比赛记录后接下来要计算核心指标。我们需要先确定这场比赛是从主队视角还是客队视角计算进球和失球因此要对每一行进行判断。def calc_team_stats(matches, team_name): 计算指定球队在样本比赛中的胜率、场均进球、场均失球。 total len(matches) if total 0: return {场次: 0, 胜率: 0, 场均进球: 0, 场均失球: 0} wins 0 goals_for 0 goals_against 0 for _, row in matches.iterrows(): if row[主队] team_name: home_goals row[主队进球] away_goals row[客队进球] goals_for home_goals goals_against away_goals if home_goals away_goals: wins 1 else: away_goals row[客队进球] home_goals row[主队进球] goals_for away_goals goals_against home_goals if away_goals home_goals: wins 1 return { 场次: total, 胜率: wins / total, 场均进球: goals_for / total, 场均失球: goals_against / total } stats_a calc_team_stats(team_a_matches, 安德莱赫特) stats_b calc_team_stats(team_b_matches, 塞萨洛尼基) print(安德莱赫特 统计, stats_a) print(塞萨洛尼基 统计, stats_b)这段代码的核心思想是判断球队是主队还是客队然后根据不同的视角累加进球和失球同时统计获胜场次。最终计算胜率和场均数据。4.4 常见数据处理误区一个非常容易踩的坑是忘记区分主客场。如果你直接使用“主队进球”字段来代表某支球队的进球数那么当这支球队打客场时数据就会出错。正确的做法是先判断球队在本场比赛中的角色。另一个常见误区是没有对日期排序。如果需要分析“近期状态”一定先按比赛日期排序再取最近 N 场否则指标会失真。team_a_matches team_a_matches.sort_values(比赛日期, ascendingFalse).head(6) team_b_matches team_b_matches.sort_values(比赛日期, ascendingFalse).head(6)5. 球队状态对比与可视化5.1 使用 Matplotlib 绘制对比图数据计算完成后使用图表能让结论变得更直观。我们先生成一张柱状图对比两支球队的胜率、场均进球和场均失球。import matplotlib.pyplot as plt # 设置中文字体避免图表中文乱码 plt.rcParams[font.sans-serif] [SimHei, Microsoft YaHei, PingFang SC] plt.rcParams[axes.unicode_minus] False labels [胜率, 场均进球, 场均失球] values_a [stats_a[胜率], stats_a[场均进球], stats_a[场均失球]] values_b [stats_b[胜率], stats_b[场均进球], stats_b[场均失球]] x range(len(labels)) width 0.35 fig, ax plt.subplots(figsize(10, 6)) bars_a ax.bar(x, values_a, widthwidth, label安德莱赫特) bars_b ax.bar([i width for i in x], values_b, widthwidth, label塞萨洛尼基) ax.set_ylabel(指标值) ax.set_title(安德莱赫特 vs 塞萨洛尼基 近期数据对比) ax.set_xticks([i width / 2 for i in x]) ax.set_xticklabels(labels) ax.legend() ax.grid(axisy, alpha0.4) # 在柱子上方显示数值 for bar in list(bars_a) list(bars_b): height bar.get_height() ax.annotate(f{height:.2f}, xy(bar.get_x() bar.get_width() / 2, height), xytext(0, 3), textcoordsoffset points, hacenter, vabottom) plt.tight_layout() plt.savefig(output/team_comparison.png, dpi150) plt.show()这段代码中的plt.rcParams用于解决中文乱码问题。如果你运行后仍然出现方框乱码说明系统缺少对应的中文字体可以在font.sans-serif列表里加入你系统中已安装的字体名称。生成图片后你可以直接查看两支球队的指标差距。如果某个指标存在明显差异就可以继续深入分析原因比如对比两队近期的对手实力。5.2 进球与失球趋势分析除了静态统计趋势分析能反映球队状态的变化。下面将球队每场比赛的进球数绘制成折线图观察近期走势。def plot_goals_trend(matches, team_name, title): 绘制指定球队近期比赛的进球趋势。 matches matches.sort_values(比赛日期) goals [] for _, row in matches.iterrows(): if row[主队] team_name: goals.append(row[主队进球]) else: goals.append(row[客队进球]) plt.figure(figsize(10, 5)) plt.plot(range(1, len(goals) 1), goals, markero, labelteam_name) plt.xlabel(比赛序号按时间排序) plt.ylabel(进球数) plt.title(title) plt.xticks(range(1, len(goals) 1)) plt.grid(alpha0.4) plt.tight_layout() plt.savefig(foutput/{team_name}_goals_trend.png, dpi150) plt.show() plot_goals_trend(team_a_matches.sort_values(比赛日期), 安德莱赫特, 安德莱赫特近期进球走势) plot_goals_trend(team_b_matches.sort_values(比赛日期), 塞萨洛尼基, 塞萨洛尼基近期进球走势)趋势图的价值在于观察球队状态是上升、下降还是波动。如果一个球队前期进球多、近期连续低迷说明状态可能正在下滑这一点在分析中比场均数据更直接。5.3 基于统计指标的简单评估思路在真实项目中仅靠胜率和场均进失球是不够的。我们还可以进一步构造更复杂的指标例如预期进球数基于射门次数、射正次数、机会质量等计算的综合指标。主客场权重客场作战会降低球队整体表现可以在评分中乘以一个小于 1 的客场系数。对手强度加权如果球队近期遇到的都是强队那么平局或小负并不代表状态差。这些指标需要在更完善的数据集上计算本文不再展开但思路是明确的用可量化的方式替代主观印象。6. 完整实战安德莱赫特 vs 塞萨洛尼基状态对比6.1 完整代码汇总为了让读者可以一次性运行这里给出合并后的完整脚本。代码包含构造数据、计算指标、绘制对比图、绘制趋势图。# 文件路径analysis.py import pandas as pd import matplotlib.pyplot as plt # 1. 构造模拟数据 data { 比赛日期: [ 2024-07-20, 2024-07-27, 2024-08-03, 2024-08-10, 2024-07-25, 2024-08-02, 2024-08-09, 2024-07-21, 2024-07-28, 2024-08-04, 2024-08-11, 2024-07-19 ], 赛事: [ 比甲, 比甲, 比甲, 欧联杯, 希超, 希超, 欧联杯, 希超, 希超, 希超, 欧联杯, 比甲 ], 主队: [ 安德莱赫特, 安德莱赫特, 安德莱赫特, 安德莱赫特, 塞萨洛尼基, 塞萨洛尼基, 塞萨洛尼基, 塞萨洛尼基, 塞萨洛尼基, 塞萨洛尼基, 安德莱赫特, 安德莱赫特 ], 客队: [ 对手A, 对手B, 对手C, 塞萨洛尼基, 对手X, 对手Y, 安德莱赫特, 对手Z, 对手W, 对手V, 对手U, 对手T ], 主队进球: [2, 1, 3, 1, 1, 0, 1, 2, 2, 3, 0, 1], 客队进球: [1, 1, 0, 2, 0, 1, 2, 1, 0, 1, 2, 0] } df pd.DataFrame(data) df[比赛日期] pd.to_datetime(df[比赛日期]) # 2. 提取球队比赛记录 def get_team_matches(df, team_name): is_home df[主队] team_name is_away df[客队] team_name return df[is_home | is_away].copy() # 3. 计算统计指标 def calc_team_stats(matches, team_name): total len(matches) if total 0: return {场次: 0, 胜率: 0, 场均进球: 0, 场均失球: 0} wins 0 goals_for 0 goals_against 0 for _, row in matches.iterrows(): if row[主队] team_name: goals_for row[主队进球] goals_against row[客队进球] if row[主队进球] row[客队进球]: wins 1 else: goals_for row[客队进球] goals_against row[主队进球] if row[客队进球] row[主队进球]: wins 1 return { 场次: total, 胜率: wins / total, 场均进球: goals_for / total, 场均失球: goals_against / total } team_a_matches get_team_matches(df, 安德莱赫特).sort_values(比赛日期, ascendingFalse).head(10) team_b_matches get_team_matches(df, 塞萨洛尼基).sort_values(比赛日期, ascendingFalse).head(10) stats_a calc_team_stats(team_a_matches, 安德莱赫特) stats_b calc_team_stats(team_b_matches, 塞萨洛尼基) print(安德莱赫特 统计, stats_a) print(塞萨洛尼基 统计, stats_b) # 4. 绘制对比柱状图 plt.rcParams[font.sans-serif] [SimHei, Microsoft YaHei, PingFang SC] plt.rcParams[axes.unicode_minus] False labels [胜率, 场均进球, 场均失球] values_a [stats_a[胜率], stats_a[场均进球], stats_a[场均失球]] values_b [stats_b[胜率], stats_b[场均进球], stats_b[场均失球]] x range(len(labels)) width 0.35 fig, ax plt.subplots(figsize(10, 6)) bars_a ax.bar(x, values_a, widthwidth, label安德莱赫特) bars_b ax.bar([i width for i in x], values_b, widthwidth, label塞萨洛尼基) ax.set_ylabel(指标值) ax.set_title(安德莱赫特 vs 塞萨洛尼基 近期数据对比) ax.set_xticks([i width / 2 for i in x]) ax.set_xticklabels(labels) ax.legend() ax.grid(axisy, alpha0.4) for bar in list(bars_a) list(bars_b): height bar.get_height() ax.annotate(f{height:.2f}, xy(bar.get_x() bar.get_width() / 2, height), xytext(0, 3), textcoordsoffset points, hacenter, vabottom) plt.tight_layout() plt.savefig(team_comparison.png, dpi150) plt.show()运行方式很简单python analysis.py6.2 预期输出说明运行后控制台会输出两支球队的统计字典例如安德莱赫特 统计 {场次: 10, 胜率: 0.6, 场均进球: 1.7, 场均失球: 1.0} 塞萨洛尼基 统计 {场次: 10, 胜率: 0.5, 场均进球: 1.4, 场均失球: 1.1}同时会弹出一张对比柱状图并被保存到当前目录。注意由于数据是模拟的每次运行结果固定但如果你替换为真实数据输出会随之变化。6.3 结果解读以上述模拟结果为例我们可以做如下的分析描述安德莱赫特在胜率和场均进球两项指标上略高于塞萨洛尼基。两支球队的场均失球非常接近说明防守水平差距不大。需要进一步对比最近一场直接交锋的数据。但必须强调这是基于模拟数据的演示结论用在真实比赛场景中会产生误导。分析只能辅助参考比赛结果受临场伤病、战术安排、裁判尺度、场地条件等众多因素影响。7. 常见问题与排查思路在使用本文代码时你可能会遇到以下问题。这里整理了一份排查表格方便快速定位问题现象常见原因解决思路中文显示为方框乱码系统中没有代码里指定的中文字体查看系统已安装字体更换font.sans-serif配置plt.show()不显示图表未安装图形界面或运行环境不支持改用plt.savefig()保存图片后再查看提示ModuleNotFoundError: No module named pandas依赖库未安装执行pip install pandas matplotlib计算结果出现 NaN原始数据存在空值使用df.dropna()或df.fillna()处理API 请求返回 403API Key 无效或未在请求头中添加检查 API 官方文档确认认证方式统计指标与预期不一致主客场进球计算逻辑错误调试时打印每一行的判断结果检查球队角色分支如果你遇到上表未覆盖的问题可以按以下步骤排查打印数据集的dtypes确认日期等字段类型正确。打印筛选后的 DataFrame确认数据范围正确。检查代码中的缩进通常缩进错误会导致逻辑分支混乱。使用print()在每个关键步骤输出中间结果定位出错位置。8. 最佳实践与工程建议8.1 数据合规与版权这是整个项目中最重要的一条。足球比赛数据属于商业数据很多网站禁止未经授权的爬取和二次分发。建议优先使用官方 API 或购买合法数据服务并在代码中保存数据来源和使用条款。即使只是个人学习也应该养成标注数据来源的习惯。8.2 分析结论的使用边界数据分析能帮助你发现规律但无法保证预测准确。不要将本文示例中的模拟结果用于任何形式的投注决策。任何以“稳定预测比赛结果”为卖点的模型都需要经过严格的历史回测和样本外验证普通人很难获得足够可靠的数据来支撑这种模型。8.3 工程化实现建议如果要把这个项目做成定时任务建议考虑以下几点将 API 请求封装成独立模块统一处理限流和异常重试。将分析结果输出为固定格式的 JSON 文件方便其他系统读取。使用日志模块记录每次运行的请求和时间便于追踪问题。将 API Key 存放在环境变量或配置文件中不要硬编码到代码里。为数据源变更做好兼容因为 API 接口升级可能导致字段名变化。9. 总结与下一步学习方向通过本文的实战你已经掌握了一套用 Python 分析足球比赛数据的基础流程从数据获取、清洗、特征计算到可视化对比整个过程并不复杂但每一步都有值得深挖的细节。重点在于理解“球队视角”的进球计算逻辑以及如何用数据描述一支球队的近期状态。下一步你可以从以下方向继续进阶学习更多数据源尝试把不同联赛的数据整合到同一个 DataFrame 中。使用 Scikit-learn 构建机器学习分类模型用历史特征预测比赛结果。引入射门、角球、控球率等进阶数据丰富特征维度。学习时间序列分析更科学地评估球队状态的变化趋势。数据不会告诉你谁一定赢但能帮你在混沌中建立秩序。只要你有足够多的数据、足够严谨的处理逻辑就能从比赛中看到更多普通视角难以察觉的信息。建议你动手跑一遍代码再做一份属于你自己的球队分析报告。
网站建设 高端定制 企业官网