国际足球比赛数据集分析报告(1872-2025)

国际足球比赛数据集分析报告(1872-2025)

项目概述

本项目基于Kaggle国际足球比赛数据集,对1872年至2025年间的国际男子足球比赛进行全面分析。

通过数据清洗、多维度数据分析和数据可视化,揭示足球运动的发展趋势、球队表现、赛事特征和主场优势等关键洞察。

数据规模:51,234场国际足球比赛记录


文章摘要

本研究基于Kaggle国际足球比赛数据集,对1872-2025年间51,234场国际男子足球比赛进行系统性分析。研究采用Python技术栈(Pandas、Matplotlib、Seaborn、Pyecharts),通过数据清洗、多维度分析和可视化展示,深入挖掘足球运动发展规律。主要发现:足球比赛数量呈持续增长趋势,欧洲和南美洲传统强队占据主导地位;友谊赛占比最高(17,196场),世界杯预选赛次之(7,900场);主场优势显著,主场胜率达57.49%,远超客场胜率19.63%;赛事平均进球数稳定在2.6-2.8球/场。研究为理解国际足球发展格局和比赛规律提供了数据支撑。


目录

  1. 数据说明
  2. 环境配置与库导入
  3. 数据读取与基本检查
  4. 数据清洗
  5. 多维度数据分析
  6. 数据可视化
  7. 运行说明
  8. 分析结论

第一章 数据说明

1.1 原始数据集

本项目使用两个原始数据集:

📊 数据集1:all_matches.csv
  • 文件路径input/all_matches.csv
  • 数据规模:51,234 行 × 8 列
  • 时间跨度:1872年 - 2025年
  • 数据内容:国际男子足球比赛结果记录
  • 数据来源Kaggle - All International Football Results
📊 数据集2:countries_names.csv
  • 文件路径input/countries_names.csv
  • 数据规模:289 行 × 3 列
  • 数据内容:国家/地区名称标准化映射表

1.2 核心字段说明

all_matches.csv 核心字段
字段名 数据类型 说明
date Date 比赛日期(若日期未知,默认为12/31;若已知月份则为当月最后一天)
home_team String 主场球队名称
away_team String 客场球队名称
home_score Integer 全场主队得分(含加时赛,不含点球大战)
away_score Integer 全场客队得分(含加时赛,不含点球大战)
tournament String 比赛名称/赛事类型
country String 比赛举办国家名称
neutral Boolean 是否在中立场地进行(False表示在主队或客队国家比赛)
countries_names.csv 核心字段
字段名 数据类型 说明
original_name String 数据集中使用的国家名称(原始名称)
current_name String 现在使用的标准名称
color_code String 国旗颜色代码(十六进制)

1.3 衍生字段说明

在数据清洗过程中,程序会自动生成以下衍生字段:

衍生字段 数据类型 计算方式 说明
year Integer date.dt.year 比赛年份(从日期中提取)
month Integer date.dt.month 比赛月份(从日期中提取,1-12)
total_goals Integer home_score + away_score 比赛总进球数
result String 条件判断 比赛结果(主队胜/客队胜/平局)

1.4 数据特点

  • 完整性:数据集仅包含国家队(至少属于国际足联的球队)
  • 时间跨度:涵盖153年足球历史(1872-2025)
  • 赛事类型:包含友谊赛、世界杯、欧洲杯、亚洲杯等各类国际赛事
  • 地理覆盖:全球200+个国家/地区的比赛记录

第二章 环境配置与库导入

2.1 导入必要的库

python 复制代码
# pandas: 用于数据处理和分析的Python库,提供DataFrame数据结构
import pandas as pd

# numpy: 用于科学计算的Python库,提供多维数组对象和各种数学函数
import numpy as np

# matplotlib.pyplot: 用于绘制静态、动态和交互式可视化的Python库
import matplotlib.pyplot as plt

# seaborn: 基于matplotlib的数据可视化库,提供更美观的统计图表
import seaborn as sns

# pyecharts: 用于生成ECharts图表的Python库,可创建交互式可视化
from pyecharts import options as opts
from pyecharts.charts import Bar, Line, Pie, Map, HeatMap
from pyecharts.globals import ThemeType

# warnings: Python内置警告模块,用于控制警告信息的显示
import warnings

# os: Python内置操作系统接口模块,用于文件和目录操作
import os

# 忽略所有警告信息,避免输出中出现烦人的警告提示
warnings.filterwarnings('ignore')

2.2 设置中文字体和输出目录

python 复制代码
# 设置matplotlib中文字体为SimHei(黑体),解决中文显示乱码问题
plt.rcParams['font.sans-serif'] = ['SimHei']

# 设置坐标轴负号显示为正常负号,而不是方块
plt.rcParams['axes.unicode_minus'] = False

# 创建输出目录,用于保存生成的图表文件
output_dir = 'output'

# os.makedirs: 创建目录,exist_ok=True表示如果目录已存在则不报错
os.makedirs(output_dir, exist_ok=True)

第三章 数据读取与基本检查

3.1 读取原始数据

python 复制代码
# pd.read_csv(): 读取CSV文件为DataFrame
# 'input/all_matches.csv': 原始比赛数据文件路径
# 包含51,234场国际足球比赛记录(1872-2025年)
matches_df = pd.read_csv('input/all_matches.csv')

# 读取国家名称映射表
# 包含289个国家的名称标准化映射
# original_name: 原始名称, current_name: 标准名称, color_code: 国旗颜色
countries_df = pd.read_csv('input/countries_names.csv')

print(f"比赛数据文件加载成功: {matches_df.shape}")
print(f"国家映射文件加载成功: {countries_df.shape}")

3.2 数据基本检查

python 复制代码
# shape属性返回DataFrame的维度(行数,列数)
print("原始数据形状:", matches_df.shape)

# columns属性返回DataFrame的所有列名
print("\n数据列名:")
print(matches_df.columns.tolist())

# head(): 显示前5行数据,用于快速查看数据结构
print("\n前5行数据:")
print(matches_df.head())

# isnull(): 检测缺失值,返回布尔型DataFrame
# sum(): 统计每列的缺失值数量(True=1, False=0)
print("\n缺失值统计:")
print(matches_df.isnull().sum())

# info(): 显示DataFrame的详细信息,包括数据类型和非空值数量
print("\n数据类型信息:")
print(matches_df.info())

数据结构说明

  • date: 比赛日期
  • home_team: 主队名称
  • away_team: 客队名称
  • home_score: 主队得分
  • away_score: 客队得分
  • tournament: 赛事类型
  • country: 比赛举办国家
  • neutral: 是否中立场地

第四章 数据清洗

4.1 处理日期字段

python 复制代码
# pd.to_datetime(): 将字符串或其他格式转换为datetime对象
# errors='coerce': 如果转换失败,设置为NaT(Not a Time)而不是报错
matches_df['date'] = pd.to_datetime(matches_df['date'], errors='coerce')

# 检查转换后的日期字段
print(f"日期转换完成,日期范围: {matches_df['date'].min()} 至 {matches_df['date'].max()}")

4.2 球队名称标准化

python 复制代码
# dict(): 创建字典,用于映射原始名称到标准名称
# zip(): 将两个列表配对,形成键值对
country_map = dict(zip(countries_df['original_name'], countries_df['current_name']))

# replace(): 使用映射字典替换DataFrame中的值
matches_df['home_team'] = matches_df['home_team'].replace(country_map)
matches_df['away_team'] = matches_df['away_team'].replace(country_map)

print(f"球队名称标准化完成,共映射 {len(country_map)} 个国家/地区")

4.3 异常值处理

python 复制代码
# 得分异常值过滤(合理得分范围0-30)
# & 是逻辑与运算符,确保两个条件同时满足
matches_df = matches_df[(matches_df['home_score'] >= 0) & (matches_df['home_score'] <= 30)]
matches_df = matches_df[(matches_df['away_score'] >= 0) & (matches_df['away_score'] <= 30)]

print(f"异常值过滤完成,剩余 {len(matches_df)} 条记录")

4.4 新增衍生字段

python 复制代码
# dt.year: 从datetime对象提取年份(如2023)
matches_df['year'] = matches_df['date'].dt.year

# dt.month: 从datetime对象提取月份(1-12)
matches_df['month'] = matches_df['date'].dt.month

# 计算总进球数 = 主队得分 + 客队得分
matches_df['total_goals'] = matches_df['home_score'] + matches_df['away_score']

# 判断比赛结果
# np.where(): 条件函数,类似Excel的IF函数,支持嵌套
matches_df['result'] = np.where(
    matches_df['home_score'] > matches_df['away_score'], 
    '主队胜',
    np.where(
        matches_df['home_score'] < matches_df['away_score'], 
        '客队胜', 
        '平局'
    )
)

print("新增字段: year, month, total_goals, result")

4.5 数据去重

python 复制代码
# drop_duplicates(): 删除重复行
# subset: 指定用于判断重复的列列表
original_count = len(matches_df)
matches_df = matches_df.drop_duplicates(subset=['date', 'home_team', 'away_team', 'tournament'])
removed_count = original_count - len(matches_df)

print(f"去重完成,去除 {removed_count} 条重复记录")

第五章 多维度数据分析

5.1 时间维度分析

5.1.1 年度比赛数量趋势
python 复制代码
# groupby('year'): 按年份分组
# size(): 计算每组的行数(比赛数量)
# reset_index(): 将分组结果转换为DataFrame,并重置索引
# name='match_count': 为统计结果列命名
yearly_matches = matches_df.groupby('year').size().reset_index(name='match_count')

# 年度总进球数统计
yearly_goals = matches_df.groupby('year')['total_goals'].sum().reset_index(name='total_goals')

# 年度平均进球数统计
yearly_avg_goals = matches_df.groupby('year')['total_goals'].mean().reset_index(name='avg_goals')
5.1.2 月度比赛分布
python 复制代码
# 按月份分组统计比赛数量
monthly_matches = matches_df.groupby('month').size().reset_index(name='match_count')

# 定义月份名称列表(中文)
month_names = ['1月', '2月', '3月', '4月', '5月', '6月', '7月', '8月', '9月', '10月', '11月', '12月']

# zip(range(1,13), month_names): 将月份数字和名称配对
# dict(): 转换为字典,如{1: '1月', 2: '2月', ...}
# map(): 将month列的值映射为对应的中文月份名称
monthly_matches['month_name'] = monthly_matches['month'].map(dict(zip(range(1,13), month_names)))

5.2 球队表现分析

5.2.1 球队胜场统计
python 复制代码
# 筛选主队获胜的比赛,按主队分组统计胜场数
home_wins = matches_df[matches_df['result'] == '主队胜'].groupby('home_team').size().reset_index(name='home_wins')

# 筛选客队获胜的比赛,按客队分组统计胜场数
away_wins = matches_df[matches_df['result'] == '客队胜'].groupby('away_team').size().reset_index(name='away_wins')

# pd.merge(): 合并两个DataFrame
# left_on='home_team': 左表的连接键(主队名)
# right_on='away_team': 右表的连接键(客队名)
# how='outer': 外连接,保留所有球队
team_wins = pd.merge(home_wins, away_wins, left_on='home_team', right_on='away_team', how='outer')

# fillna(): 用指定值填充NaN(缺失值)
team_wins['team'] = team_wins['home_team'].fillna(team_wins['away_team'])
team_wins['total_wins'] = team_wins['home_wins'].fillna(0) + team_wins['away_wins'].fillna(0)

# 选择需要的列,按胜场数降序排列,取前20名
team_wins = team_wins[['team', 'total_wins']].sort_values('total_wins', ascending=False).head(20)
5.2.2 进球能力分析
python 复制代码
# 按主队分组,统计主场总进球数
home_goals = matches_df.groupby('home_team')['home_score'].sum().reset_index(name='home_goals')

# 按客队分组,统计客场总进球数
away_goals = matches_df.groupby('away_team')['away_score'].sum().reset_index(name='away_goals')

# 合并主客场进球数据
team_goals = pd.merge(home_goals, away_goals, left_on='home_team', right_on='away_team', how='outer')
team_goals['team'] = team_goals['home_team'].fillna(team_goals['away_team'])
team_goals['total_goals'] = team_goals['home_goals'].fillna(0) + team_goals['away_goals'].fillna(0)
team_goals = team_goals[['team', 'total_goals']].sort_values('total_goals', ascending=False).head(20)

5.3 赛事类型分析

5.3.1 赛事数量统计
python 复制代码
# value_counts(): 统计每个唯一值的出现次数,自动按降序排列
# reset_index(): 转换为DataFrame
# head(10): 取前10名
tournament_counts = matches_df['tournament'].value_counts().reset_index().head(10)
tournament_counts.columns = ['tournament', 'match_count']

# agg(): 聚合函数,可同时计算多个统计量
# 对total_goals列同时计算sum(总和)、mean(平均值)、count(计数)
tournament_goals = matches_df.groupby('tournament').agg({
    'total_goals': ['sum', 'mean', 'count']
}).round(2)

tournament_goals.columns = ['total_goals', 'avg_goals_per_match', 'match_count']
tournament_goals = tournament_goals.sort_values('match_count', ascending=False).head(10)
5.3.2 赛事进球效率
python 复制代码
# agg(): 聚合函数,可同时计算多个统计量
# 对total_goals列同时计算sum(总和)、mean(平均值)、count(计数)
tournament_goals = matches_df.groupby('tournament').agg({
    'total_goals': ['sum', 'mean', 'count']
}).round(2)

tournament_goals.columns = ['total_goals', 'avg_goals_per_match', 'match_count']
tournament_goals = tournament_goals.sort_values('match_count', ascending=False).head(10)

5.4 主场优势分析

5.4.1 主客场胜率对比
python 复制代码
# len(): 返回DataFrame的行数(总比赛数)
total_matches = len(matches_df)

# 计算主队胜率(主队获胜的比赛数 / 总比赛数 * 100)
home_win_rate = len(matches_df[matches_df['result'] == '主队胜']) / total_matches * 100

# 计算客队胜率(客队获胜的比赛数 / 总比赛数 * 100)
away_win_rate = len(matches_df[matches_df['result'] == '客队胜']) / total_matches * 100

# 计算平局率(平局的比赛数 / 总比赛数 * 100)
draw_rate = len(matches_df[matches_df['result'] == '平局']) / total_matches * 100
5.4.2 中立场地对比分析
python 复制代码
# 中立场地vs非中立场地对比
# groupby('neutral'): 按是否中立场地分组(True/False)
# value_counts(normalize=True): 计算比例(归一化,总和为1)
# unstack(): 将多级索引转换为列,形成透视表
neutral_stats = matches_df.groupby('neutral')['result'].value_counts(normalize=True).unstack() * 100
neutral_stats.columns = ['客队胜率(%)', '平局率(%)', '主队胜率(%)']

分析结果

  • 主场胜率: 57.49%
  • 客场胜率: 19.63%
  • 平局率: 22.88%

第六章 数据可视化

6.1 图表1:年度比赛数量趋势图(Line)

Matplotlib实现

python 复制代码
# figure(): 创建新图表
# figsize=(14, 7): 设置图表大小为14英寸宽,7英寸高
plt.figure(figsize=(14, 7))

# plot(): 绘制折线图
# color='#1f77b4': 设置线条颜色为蓝色
# linewidth=2: 设置线宽为2
# marker='o': 设置数据点标记为圆形
# markersize=3: 设置标记大小为3
plt.plot(yearly_matches['year'], yearly_matches['match_count'], 
         color='#1f77b4', linewidth=2, marker='o', markersize=3)

# title(): 设置图表标题
plt.title('1872-2025年国际足球比赛数量趋势', fontsize=16, fontweight='bold')

# xlabel(), ylabel(): 设置坐标轴标签
plt.xlabel('年份', fontsize=12)
plt.ylabel('比赛数量', fontsize=12)

# grid(): 显示网格线
plt.grid(True, alpha=0.3)

# xticks(): 设置x轴刻度
plt.xticks(range(1870, 2030, 10), rotation=45)

# tight_layout(): 自动调整子图参数,使图表紧凑
plt.tight_layout()

# savefig(): 保存图表
plt.savefig(f'{output_dir}/yearly_match_trend.png', dpi=300, bbox_inches='tight')

# close(): 关闭图表,释放内存
plt.close()

Pyecharts实现

python 复制代码
# Line(): 创建折线图对象
# init_opts: 初始化配置
# ThemeType.LIGHT: 使用浅色主题
# width="1400px": 图表宽度1400像素
# height="700px": 图表高度700像素
line = (
    Line(init_opts=opts.InitOpts(theme=ThemeType.LIGHT, width="1400px", height="700px"))
    
    # add_xaxis(): 添加x轴数据
    .add_xaxis(yearly_matches['year'].astype(str).tolist())
    
    # add_yaxis(): 添加y轴数据
    # markpoint_opts: 标记点配置,显示最大值
    .add_yaxis("比赛数量", yearly_matches['match_count'].tolist(), 
               markpoint_opts=opts.MarkPointOpts(data=[opts.MarkPointItem(type_="max")]))
    
    # set_global_opts(): 设置全局配置
    .set_global_opts(
        title_opts=opts.TitleOpts(
            title="1872-2025年国际足球比赛数量趋势", 
            subtitle="数据来源:Kaggle国际足球比赛数据集"
        ),
        xaxis_opts=opts.AxisOpts(
            axislabel_opts=opts.LabelOpts(rotate=-45)
        ),
        yaxis_opts=opts.AxisOpts(name="比赛数量"),
        datazoom_opts=[opts.DataZoomOpts(range_start=80, range_end=100)]
    )
)

# render(): 渲染为HTML文件
line.render(f'{output_dir}/yearly_match_trend_pyecharts.html')

6.2 图表2:月度比赛分布柱状图(Bar)

Matplotlib实现

python 复制代码
plt.figure(figsize=(12, 6))

# barplot(): 绘制柱状图
# x='month_name': x轴为月份名称
# y='match_count': y轴为比赛数量
# palette='viridis': 使用viridis配色方案
sns.barplot(x='month_name', y='match_count', data=monthly_matches, palette='viridis')

plt.title('国际足球比赛月度分布', fontsize=16, fontweight='bold')
plt.xlabel('月份', fontsize=12)
plt.ylabel('比赛数量', fontsize=12)
plt.grid(True, alpha=0.3, axis='y')
plt.tight_layout()
plt.savefig(f'{output_dir}/monthly_match_distribution.png', dpi=300, bbox_inches='tight')
plt.close()

Pyecharts实现

python 复制代码
bar = (
    Bar(init_opts=opts.InitOpts(theme=ThemeType.LIGHT, width="1200px", height="600px"))
    .add_xaxis(monthly_matches['month_name'].tolist())
    .add_yaxis("比赛数量", monthly_matches['match_count'].tolist(), color='#ff7f0e')
    .set_global_opts(
        title_opts=opts.TitleOpts(
            title="国际足球比赛月度分布", 
            subtitle="数据来源:Kaggle国际足球比赛数据集"
        ),
        xaxis_opts=opts.AxisOpts(axislabel_opts=opts.LabelOpts(rotate=0)),
        yaxis_opts=opts.AxisOpts(name="比赛数量")
    )
)
bar.render(f'{output_dir}/monthly_match_distribution_pyecharts.html')

6.3 图表3:球队胜场Top20饼图(Pie)

Matplotlib实现

python 复制代码
plt.figure(figsize=(12, 12))

# colors: 使用tab20颜色映射生成20种不同颜色
# plt.cm.tab20: matplotlib的tab20颜色映射
# np.linspace(0, 1, 20): 生成0到1之间均匀分布的20个数
colors = plt.cm.tab20(np.linspace(0, 1, 20))

# pie(): 绘制饼图
# team_wins['total_wins']: 饼图各扇区的大小(胜场数)
# labels=team_wins['team']: 各扇区的标签(球队名)
# autopct='%1.1f%%': 显示百分比,保留1位小数
# startangle=90: 起始角度为90度
# shadow=True: 显示阴影效果
# explode: 突出显示前5个扇区
plt.pie(team_wins['total_wins'], labels=team_wins['team'], autopct='%1.1f%%', 
        startangle=90, shadow=True, explode=[0.05]*5 + [0]*15, colors=colors)

plt.title('国际足球球队胜场Top20分布', fontsize=16, fontweight='bold')
plt.axis('equal')
plt.tight_layout()
plt.savefig(f'{output_dir}/team_wins_pie.png', dpi=300, bbox_inches='tight')
plt.close()


Pyecharts实现

python 复制代码
# zip(): 将球队名和胜场数配对
pie_data = list(zip(team_wins['team'].tolist(), team_wins['total_wins'].tolist()))

pie = (
    Pie(init_opts=opts.InitOpts(theme=ThemeType.LIGHT, width="1200px", height="1200px"))
    .add("", pie_data,
         radius=["30%", "75%"],  # 内外半径,形成环形饼图
         center=["50%", "50%"],  # 中心位置
         rosetype="radius")  # 玫瑰图类型
    .set_global_opts(
        title_opts=opts.TitleOpts(
            title="国际足球球队胜场Top20分布", 
            subtitle="数据来源:Kaggle国际足球比赛数据集"
        ),
        legend_opts=opts.LegendOpts(
            orient="vertical",
            pos_top="15%",
            pos_left="2%"
        )
    )
    .set_series_opts(
        label_opts=opts.LabelOpts(formatter="{b}: {c} ({d}%)")
    )
)
pie.render(f'{output_dir}/team_wins_pie_pyecharts.html')

6.4 图表4:球队进球能力对比图(Horizontal Bar)

Matplotlib实现

python 复制代码
plt.figure(figsize=(12, 10))

# barplot(): 绘制柱状图
# x='total_goals': x轴为总进球数(水平柱状图)
# y='team': y轴为球队名
# palette='coolwarm': 使用coolwarm配色方案
sns.barplot(x='total_goals', y='team', data=team_goals, palette='coolwarm')

plt.title('国际足球球队总进球数Top20', fontsize=16, fontweight='bold')
plt.xlabel('总进球数', fontsize=12)
plt.ylabel('球队', fontsize=12)
plt.grid(True, alpha=0.3, axis='x')
plt.tight_layout()
plt.savefig(f'{output_dir}/team_goals_bar.png', dpi=300, bbox_inches='tight')
plt.close()

Pyecharts实现

python 复制代码
hbar = (
    Bar(init_opts=opts.InitOpts(theme=ThemeType.LIGHT, width="1200px", height="1000px"))
    .add_xaxis(team_goals['team'].tolist())
    .add_yaxis("总进球数", team_goals['total_goals'].tolist(), color='#2ca02c')
    .reversal_axis()  # 反转坐标轴,形成水平柱状图
    .set_global_opts(
        title_opts=opts.TitleOpts(
            title="国际足球球队总进球数Top20", 
            subtitle="数据来源:Kaggle国际足球比赛数据集"
        ),
        xaxis_opts=opts.AxisOpts(name="总进球数"),
        yaxis_opts=opts.AxisOpts(axislabel_opts=opts.LabelOpts(rotate=0))
    )
    .set_series_opts(label_opts=opts.LabelOpts(position="right"))
)
hbar.render(f'{output_dir}/team_goals_hbar_pyecharts.html')

6.5 图表5:赛事类型分布直方图(Histogram)

Matplotlib实现

python 复制代码
plt.figure(figsize=(14, 7))

# hist(): 绘制直方图
# bins=20: 分成20个区间
# color='#d62728': 红色
# alpha=0.7: 透明度0.7
# edgecolor='black': 边框颜色黑色
plt.hist(matches_df['tournament'], bins=20, color='#d62728', alpha=0.7, edgecolor='black')

plt.title('国际足球赛事类型分布', fontsize=16, fontweight='bold')
plt.xlabel('赛事类型', fontsize=12)
plt.ylabel('赛事数量', fontsize=12)
plt.xticks(rotation=90)
plt.grid(True, alpha=0.3, axis='y')
plt.tight_layout()
plt.savefig(f'{output_dir}/tournament_histogram.png', dpi=300, bbox_inches='tight')
plt.close()

Seaborn实现

python 复制代码
plt.figure(figsize=(14, 7))

# histplot(): Seaborn的直方图函数
# weights='match_count': 使用match_count作为权重
sns.histplot(data=tournament_counts, x='tournament', weights='match_count', 
             bins=10, color='#9467bd')

plt.title('国际足球主要赛事数量分布(Top10)', fontsize=16, fontweight='bold')
plt.xlabel('赛事类型', fontsize=12)
plt.ylabel('比赛数量', fontsize=12)
plt.xticks(rotation=45, ha='right')
plt.grid(True, alpha=0.3, axis='y')
plt.tight_layout()
plt.savefig(f'{output_dir}/top10_tournament_histogram.png', dpi=300, bbox_inches='tight')
plt.close()

6.6 图表6:主客场胜率对比图(Grouped Bar)

Matplotlib实现

python 复制代码
plt.figure(figsize=(10, 6))

# 定义比赛结果和对应胜率
results = ['主队胜', '客队胜', '平局']
rates = [home_win_rate, away_win_rate, draw_rate]
colors = ['#ff9999', '#66b3ff', '#99ff99']

# np.arange(): 创建等差数组,用于x轴位置
x = np.arange(len(results))

# width: 柱子的宽度
width = 0.6

# bar(): 绘制柱状图
plt.bar(x, rates, width, color=colors, alpha=0.8, edgecolor='black')

plt.title('c', fontsize=16, fontweight='bold')
plt.xlabel('比赛结果', fontsize=12)
plt.ylabel('胜率(%)', fontsize=12)
plt.xticks(x, results)
plt.grid(True, alpha=0.3, axis='y')

# 添加数值标签
for i, v in enumerate(rates):
    plt.text(i, v + 0.5, f'{v:.2f}%', ha='center', va='bottom', fontsize=11)

plt.tight_layout()
plt.savefig(f'{output_dir}/home_away_win_rate.png', dpi=300, bbox_inches='tight')
plt.close()

Pyecharts实现

python 复制代码
group_bar = (
    Bar(init_opts=opts.InitOpts(theme=ThemeType.LIGHT, width="1000px", height="600px"))
    .add_xaxis(results)
    .add_yaxis("胜率(%)", rates, color=['#ff9999', '#66b3ff', '#99ff99'])
    .set_global_opts(
        title_opts=opts.TitleOpts(
            title="国际足球比赛主客场胜率对比", 
            subtitle="数据来源:Kaggle国际足球比赛数据集"
        ),
        yaxis_opts=opts.AxisOpts(name="胜率(%)", max_=50)
    )
    .set_series_opts(
        label_opts=opts.LabelOpts(formatter="{c}%", position="top")
    )
)
group_bar.render(f'{output_dir}/home_away_win_rate_pyecharts.html')

6.7 图表7:年度平均进球数趋势图(Dual Axis Line)

Matplotlib实现

python 复制代码
fig, ax1 = plt.subplots(figsize=(14, 7))

# 主坐标轴:比赛数量
ax1.plot(yearly_matches['year'], yearly_matches['match_count'], 
         'b-', linewidth=2, label='比赛数量')
ax1.set_xlabel('年份', fontsize=12)
ax1.set_ylabel('比赛数量', fontsize=12, color='b')
ax1.tick_params(axis='y', labelcolor='b')
ax1.grid(True, alpha=0.3)

# 次坐标轴:平均进球数
# twinx(): 创建共享x轴的第二个y轴
ax2 = ax1.twinx()
ax2.plot(yearly_avg_goals['year'], yearly_avg_goals['avg_goals'], 
         'r-', linewidth=2, label='平均进球数')
ax2.set_ylabel('平均进球数/场', fontsize=12, color='r')
ax2.tick_params(axis='y', labelcolor='r')

# 添加图例
lines1, labels1 = ax1.get_legend_handles_labels()
lines2, labels2 = ax2.get_legend_handles_labels()
ax1.legend(lines1 + lines2, labels1 + labels2, loc='upper left')

plt.title('1872-2025年比赛数量与平均进球数趋势', fontsize=16, fontweight='bold')
plt.xticks(range(1870, 2030, 10), rotation=45)
plt.tight_layout()
plt.savefig(f'{output_dir}/yearly_goals_dual_axis.png', dpi=300, bbox_inches='tight')
plt.close()

6.8 图表8:国家比赛数量热力图(HeatMap)

python 复制代码
# 统计各国举办比赛数量(Top30)
country_match_count = matches_df['country'].value_counts().reset_index().head(30)
country_match_count.columns = ['country', 'match_count']

# 定义大洲映射字典
continents = {
    'England': 'Europe', 'Scotland': 'Europe', 'Germany': 'Europe', 
    'France': 'Europe', 'Italy': 'Europe', 'Spain': 'Europe',
    'Netherlands': 'Europe', 'Belgium': 'Europe', 'Sweden': 'Europe', 
    'Norway': 'Europe', 'Portugal': 'Europe', 'Denmark': 'Europe',
    'Switzerland': 'Europe', 'Austria': 'Europe', 'Russia': 'Europe',
    'Poland': 'Europe', 'Czech Republic': 'Europe', 'Hungary': 'Europe',
    'Turkey': 'Europe', 'Greece': 'Europe',
    'Brazil': 'South America', 'Argentina': 'South America', 
    'Uruguay': 'South America', 'Chile': 'South America',
    'United States': 'North America', 'Mexico': 'North America', 
    'Canada': 'North America',
    'China': 'Asia', 'Japan': 'Asia', 'South Korea': 'Asia',
    'Australia': 'Oceania', 'New Zealand': 'Oceania',
    'South Africa': 'Africa', 'Egypt': 'Africa', 'Nigeria': 'Africa'
}

# map(): 根据国家映射到大洲
country_match_count['continent'] = country_match_count['country'].map(continents).fillna('Other')

# 按大洲和国家分组统计
heatmap_data = country_match_count.groupby(['continent', 'country'])['match_count'].sum().reset_index()
heatmap_data = heatmap_data.sort_values('match_count', ascending=False)

# 转换为热力图格式
heatmap_list = []
for _, row in heatmap_data.iterrows():
    heatmap_list.append([row['continent'], row['country'], int(row['match_count'])])

# 获取唯一的大洲和国家列表
x_axis = list(set([x[0] for x in heatmap_list]))
y_axis = list(set([x[1] for x in heatmap_list]))

# Pyecharts热力图实现
heatmap = (
    HeatMap(init_opts=opts.InitOpts(theme=ThemeType.LIGHT, width="1200px", height="800px"))
    .add_xaxis(x_axis)
    .add_yaxis("比赛数量", y_axis, heatmap_list)
    .set_global_opts(
        title_opts=opts.TitleOpts(
            title="各国举办国际足球比赛数量热力图(Top30)", 
            subtitle="数据来源:Kaggle国际足球比赛数据集"
        ),
        visualmap_opts=opts.VisualMapOpts(
            min_=0, 
            max_=heatmap_data['match_count'].max()
        ),
        xaxis_opts=opts.AxisOpts(axislabel_opts=opts.LabelOpts(rotate=-45))
    )
)
heatmap.render(f'{output_dir}/country_match_heatmap.html')

6.9 图表9:比赛结果分布散点图(Scatter)

python 复制代码
# 筛选近50年数据避免图表过于密集
recent_matches = matches_df[matches_df['year'] >= 1975]

plt.figure(figsize=(12, 10))

# scatterplot(): 绘制散点图
# x='home_score': x轴为主队得分
# y='away_score': y轴为客队得分
# alpha=0.6: 透明度0.6,可以看到重叠的点
# s=10: 点的大小为10
# hue='neutral': 根据是否中立场地着色
sns.scatterplot(x='home_score', y='away_score', data=recent_matches, 
                alpha=0.6, s=10, hue='neutral', palette='coolwarm', legend='full')

plt.title('近50年国际足球比赛主客场得分分布', fontsize=16, fontweight='bold')
plt.xlabel('主队得分', fontsize=12)
plt.ylabel('客队得分', fontsize=12)
plt.grid(True, alpha=0.3)
plt.legend(title='中立场地', loc='upper right')
plt.tight_layout()
plt.savefig(f'{output_dir}/match_score_scatter.png', dpi=300, bbox_inches='tight')
plt.close()

6.10 图表10:赛事进球效率对比图(Box Plot)

python 复制代码
# 筛选Top5赛事类型
top5_tournaments = tournament_counts['tournament'].head(5).tolist()

# isin(): 判断是否在列表中
tournament_goals_data = matches_df[matches_df['tournament'].isin(top5_tournaments)]

plt.figure(figsize=(14, 7))

# boxplot(): 绘制箱线图
# palette='Set2': 使用Set2配色方案
sns.boxplot(x='tournament', y='total_goals', data=tournament_goals_data, palette='Set2')

plt.title('Top5赛事进球数分布对比', fontsize=16, fontweight='bold')
plt.xlabel('赛事类型', fontsize=12)
plt.ylabel('总进球数/场', fontsize=12)
plt.xticks(rotation=45, ha='right')
plt.grid(True, alpha=0.3, axis='y')
plt.tight_layout()
plt.savefig(f'{output_dir}/tournament_goals_boxplot.png', dpi=300, bbox_inches='tight')
plt.close()

第七章 运行说明

7.1 项目结构

复制代码
足球比赛/
├── input/                          # 输入数据目录
│   ├── all_matches.csv            # 比赛数据(51,234条记录)
│   └── countries_names.csv        # 国家名称映射表
├── output/                         # 输出图表目录
│   ├── yearly_match_trend.png
│   ├── yearly_match_trend_pyecharts.html
│   └── ...                        # 其他图表文件
├── app.py                         # 主程序文件
├── README.md                      # 项目说明文档
└── .venv/                         # Python虚拟环境

7.2 环境要求

  • Python 3.12+
  • pandas
  • numpy
  • matplotlib
  • seaborn
  • pyecharts

7.3 运行方式

使用虚拟环境运行

bash 复制代码
# Windows
.venv\Scripts\python app.py

# macOS/Linux
source .venv/bin/python app.py

7.4 输出结果

程序运行后会生成以下文件:

文件名 类型 说明
yearly_match_trend.png PNG 年度比赛数量趋势图(Matplotlib)
yearly_match_trend_pyecharts.html HTML 年度比赛数量趋势图(Pyecharts)
monthly_match_distribution.png PNG 月度比赛分布柱状图(Seaborn)
monthly_match_distribution_pyecharts.html HTML 月度比赛分布柱状图(Pyecharts)
team_wins_pie.png PNG 球队胜场Top20饼图(Matplotlib)
team_wins_pie_pyecharts.html HTML 球队胜场Top20饼图(Pyecharts)
team_goals_bar.png PNG 球队进球能力对比图(Seaborn)
team_goals_hbar_pyecharts.html HTML 球队进球能力对比图(Pyecharts)
tournament_histogram.png PNG 赛事类型分布直方图(Matplotlib)
top10_tournament_histogram.png PNG Top10赛事分布(Seaborn)
home_away_win_rate.png PNG 主客场胜率对比图(Matplotlib)
home_away_win_rate_pyecharts.html HTML 主客场胜率对比图(Pyecharts)
yearly_goals_dual_axis.png PNG 年度平均进球数趋势图(双轴)
country_match_heatmap.html HTML 国家比赛数量热力图(Pyecharts)
match_score_scatter.png PNG 比赛结果分布散点图(Seaborn)
tournament_goals_boxplot.png PNG 赛事进球效率对比图(箱线图)

第八章 分析结论

8.1 足球运动发展趋势

  • 国际足球比赛数量持续增长,反映足球运动的全球化
  • 20世纪后期以来增长加速,与电视转播和商业化发展相关

8.2 球队竞争力格局

  • 传统足球强国(英格兰、德国、巴西、阿根廷、瑞典等)保持领先
  • 胜场数和进球数高度相关,进攻能力决定竞争力
  • 欧洲和南美洲球队占据主导地位

8.3 赛事体系特征

  • 友谊赛占比最高(17,196场),是国家队交流的主要形式
  • 世界杯预选赛(7,900场)和欧洲杯预选赛(2,824场)数量庞大
  • 正式赛事的平均进球数相对稳定(2.6-2.8球/场)

8.4 主场优势验证

  • 主场胜率(57.49%)显著高于客场胜率(19.63%)
  • 主场优势可能来自:球迷支持、场地熟悉、减少旅途疲劳
  • 中立场地比赛结果更加均衡

8.5 比赛得分规律

  • 常见比分集中在低比分区域(0-3球)
  • 大比分比赛较为罕见
  • 中立场地比赛的得分分布更加分散

技术亮点

数据处理能力

  • 使用Pandas进行大规模数据处理(50,000+行)
  • 数据清洗流程完整(缺失值、异常值、重复值处理)
  • 高效的数据分组和聚合操作

可视化技术

  • 同时使用Matplotlib、Seaborn和Pyecharts三大可视化库
  • 静态图表和交互式图表相结合
  • 中文字体正确显示处理

代码结构

  • 模块化设计,功能分区明确
  • 详细的代码注释,便于理解和维护
  • 逐行注释解释每个参数的作用

参考资料

  • 数据来源:Kaggle国际足球比赛数据集
  • 时间跨度:1872年-2025年
  • 数据规模:51,234场比赛记录
  • 分析工具:Python + Pandas + Matplotlib + Seaborn + Pyecharts

相关推荐
V1ncent Chen3 小时前
SQL大师之路 13 聚合函数和分组
数据库·sql·mysql·数据分析
海天一色y4 小时前
基于Inception v3的CIFAR-100图像分类实战:从迁移学习到性能优化
分类·数据挖掘·迁移学习
川爻7 小时前
Superstore Sales Dataset数据分析(兼数据分析步骤学习)
学习·数据挖掘·数据分析
龙腾AI白云7 小时前
数字孪生国内外发展现状
数据分析·flask·virtualenv·fastapi
爱玩亚索的程序员8 小时前
算法入门(三)学会用matplotlib画图
算法·matplotlib
城数派8 小时前
1901-2024年中国1km逐月潜在蒸散发数据集
数据分析
七夜zippoe8 小时前
Elasticsearch全文搜索与数据分析实战指南
大数据·python·elasticsearch·数据分析·全文搜索
hans汉斯8 小时前
基于区块链和语义增强的科研诚信智能管控平台
人工智能·算法·yolo·数据挖掘·区块链·汉斯出版社