国际足球比赛数据集分析报告(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

相关推荐
风流 少年17 小时前
数据可视化:matplotlib、pyecharts、panda、seaborn
数据挖掘·数据分析·matplotlib
最爱老式锅包肉20 小时前
《HarmonyOS技术精讲-ArkGraphics 2D(方舟2D图形服务)》第九篇:数据可视化——柱状图与饼图的绘制实战
华为·信息可视化·harmonyos
SelectDB1 天前
云数仓费钱?用 SelectDB Serverless 三步配出秒级弹性,最高降本 70%
数据库·云原生·数据分析
backendbeacon1 天前
Cytoscape 3.10 下载安装教程 专业生物科学数据分析与蛋白互作网络可视化软件下载安装步骤
其他·数据挖掘·数据分析
fai厅的秃头姐!1 天前
Matplotlib
matplotlib
一键生成网站1 天前
政务国企数据可视化大屏AI工具对比与信创安全选型指南
人工智能·信息可视化·政务·
歪歪歪比巴卜1 天前
2026年多平台评论舆情分析工具推荐
数据分析·舆情分析·评论分析
babe小鑫1 天前
2026二本数字经济专业学数据分析的价值
数据挖掘·数据分析
得闲喝茶1 天前
Excel——数据透析表
数据库·经验分享·笔记·其他·信息可视化·excel
本王是暴君1 天前
AutoMem:把 Agent Memory 变成可训练的记忆管理技能
人工智能·算法·数据挖掘