使用Python分析世界杯比赛数据:以捷克1-1战平南非为例
简介
足球比赛的数据分析已成为现代体育的重要组成部分。本文将通过一个具体案例——2026年世界杯A组第二轮捷克1-1战平南非的比赛,教你如何使用Python进行比赛数据收集、处理和分析。你将学习到从网页抓取比赛数据、清理数据、进行可视化分析的基本流程,并了解如何利用数据得出有趣的结论。
即使你不是体育迷,这个教程也能帮你掌握数据处理的实用技能,这些技能可以应用于各种领域的数据分析任务。
前置准备
在开始之前,请确保你已经准备好以下环境和工具:
- Python环境:建议安装Python 3.8或更高版本
- 代码编辑器:机械键盘 可以提升编码效率,推荐使用VS Code或PyCharm
- 必要的Python库:
- requests:用于网络请求
- beautifulsoup4:用于解析HTML
- pandas:数据处理和分析
- matplotlib:数据可视化
- seaborn:更美观的统计图表
安装这些库的命令:
pip install requests beautifulsoup4 pandas matplotlib seaborn
- 可靠的网络连接:用于获取在线数据
- 数据存储:确保有足够的空间存储处理后的数据
如果你需要一台性能良好的笔记本电脑来运行这些代码,可以考虑购买具备足够内存和处理能力的型号。
步骤一:获取比赛基础数据
首先,我们需要获取比赛的基本信息。这里我们模拟从体育网站获取数据的过程,实际应用中可能需要根据具体网站调整解析逻辑。
import requests
from bs4 import BeautifulSoup
import pandas as pd
import json
from datetime import datetime
def fetch_match_basic_info():
"""获取比赛基础信息"""
# 模拟比赛数据(实际应用中会从网站解析)
match_data = {
"tournament": "2026 FIFA World Cup",
"group": "A",
"round": "Group Stage - Matchday 2",
"date": "2025-06-19",
"time": "02:00 (UTC+8)",
"venue": "MetLife Stadium, New Jersey",
"attendance": 65432,
"referee": "Michael Oliver (England)",
"weather": "Clear, 22°C"
}
# 比赛基本信息
match_info = {
"home_team": "Czech Republic",
"away_team": "South Africa",
"final_score": "1-1",
"home_score": 1,
"away_score": 1,
"home_scorers": ["Patrik Schick 38'"],
"away_scorers": ["Percy Tau 65'"],
"competition_stage": "Group Stage"
}
return {**match_data, **match_info}
# 获取并打印比赛信息
match_info = fetch_match_basic_info()
print("比赛基本信息:")
for key, value in match_info.items():
print(f"{key}: {value}")
步骤二:获取详细比赛统计数据
接下来,我们获取更详细的比赛统计,包括控球率、射门次数、传球成功率等关键指标。
def fetch_match_statistics():
"""获取比赛详细统计数据"""
stats_data = {
"home_team_stats": {
"team": "Czech Republic",
"possession": 52.3,
"total_shots": 14,
"shots_on_target": 5,
"shots_off_target": 9,
"blocked_shots": 3,
"corner_kicks": 7,
"offsides": 2,
"fouls": 12,
"yellow_cards": 2,
"red_cards": 0,
"passes": 487,
"pass_accuracy": 84.2,
"crosses": 18,
"cross_accuracy": 33.3,
"duels_won": 62,
"aerial_duels_won": 15,
"tackles": 18,
"interceptions": 10
},
"away_team_stats": {
"team": "South Africa",
"possession": 47.7,
"total_shots": 10,
"shots_on_target": 4,
"shots_off_target": 6,
"blocked_shots": 2,
"corner_kicks": 4,
"offsides": 3,
"fouls": 15,
"yellow_cards": 3,
"red_cards": 0,
"passes": 423,
"pass_accuracy": 81.8,
"crosses": 12,
"cross_accuracy": 25.0,
"duels_won": 58,
"aerial_duels_won": 18,
"tackles": 21,
"interceptions": 8
},
"key_stats": {
"expected_goals": {"Czech Republic": 1.42, "South Africa": 1.18},
"big_chances": {"Czech Republic": 3, "South Africa": 2},
"save_percentage": {"Czech Republic": 75.0, "South Africa": 80.0}
}
}
return stats_data
# 获取统计数据
match_stats = fetch_match_statistics()
# 将数据转换为DataFrame便于分析
home_stats = pd.DataFrame([match_stats["home_team_stats"]])
away_stats = pd.DataFrame([match_stats["away_team_stats"]])
print("\n比赛统计数据:")
print("=" * 50)
print("捷克队统计:")
for key, value in match_stats["home_team_stats"].items():
if key != "team":
print(f"{key}: {value}")
print("\n南非队统计:")
for key, value in match_stats["away_team_stats"].items():
if key != "team":
print(f"{key}: {value}")
步骤三:数据清洗与预处理
实际获取的数据可能包含缺失值或异常值,我们需要进行清洗和预处理。
def clean_and_preprocess_data(stats_data):
"""清洗和预处理比赛数据"""
# 创建比较DataFrame
comparison_df = pd.DataFrame({
'Statistic': list(stats_data['home_team_stats'].keys())[1:], # 排除team字段
'Czech Republic': list(stats_data['home_team_stats'].values())[1:],
'South Africa': list(stats_data['away_team_stats'].values())[1:]
})
# 添加差异列
comparison_df['Difference'] = comparison_df['Czech Republic'] - comparison_df['South Africa']
# 添加优势方标记
comparison_df['Advantage'] = comparison_df['Difference'].apply(
lambda x: 'Czech Republic' if x > 0 else 'South Africa' if x < 0 else 'Even'
)
# 计算百分比类型数据的标准化值
percentage_stats = ['possession', 'pass_accuracy', 'cross_accuracy', 'save_percentage']
for stat in percentage_stats:
if stat in comparison_df['Statistic'].values:
idx = comparison_df[comparison_df['Statistic'] == stat].index[0]
comparison_df.loc[idx, 'Czech Republic'] = f"{comparison_df.loc[idx, 'Czech Republic']}%"
comparison_df.loc[idx, 'South Africa'] = f"{comparison_df.loc[idx, 'South Africa']}%"
return comparison_df
# 清洗数据
comparison_df = clean_and_preprocess_data(match_stats)
print("\n数据对比表:")
print(comparison_df.to_string(index=False))
步骤四:数据可视化分析
使用图表可以更直观地展示比赛数据。我们将创建多个可视化图表来分析比赛。
import matplotlib.pyplot as plt
import seaborn as sns
def create_match_visualizations(stats_data):
"""创建比赛数据可视化图表"""
# 设置中文字体支持
plt.rcParams['font.sans-serif'] = ['SimHei', 'Arial Unicode MS', 'DejaVu Sans']
plt.rcParams['axes.unicode_minus'] = False
# 创建图表
fig, axes = plt.subplots(2, 2, figsize=(15, 12))
# 1. 基础统计对比条形图
basic_stats = ['total_shots', 'shots_on_target', 'corner_kicks', 'fouls']
home_values = [stats_data['home_team_stats'][stat] for stat in basic_stats]
away_values = [stats_data['away_team_stats'][stat] for stat in basic_stats]
x = range(len(basic_stats))
width = 0.35
axes[0, 0].bar([i - width/2 for i in x], home_values, width, label='捷克', color='#D32F2F')
axes[0, 0].bar([i + width/2 for i in x], away_values, width, label='南非', color='#1B5E20')
axes[0, 0].set_xlabel('统计项目')
axes[0, 0].set_ylabel('数量')
axes[0, 0].set_title('基础比赛统计对比')
axes[0, 0].set_xticks(x)
axes[0, 0].set_xticklabels(['总射门', '射正', '角球', '犯规'])
axes[0, 0].legend()
axes[0, 0].grid(True, alpha=0.3)
# 2. 控球率饼图
possession_data = [stats_data['home_team_stats']['possession'],
stats_data['away_team_stats']['possession']]
labels = ['捷克', '南非']
colors = ['#D32F2F', '#1B5E20']
axes[0, 1].pie(possession_data, labels=labels, colors=colors, autopct='%1.1f%%',
startangle=90, textprops={'fontsize': 12})
axes[0, 1].set_title('控球率对比')
# 3. 传球成功率对比
pass_stats = ['passes', 'pass_accuracy']
pass_data = {
'捷克': [stats_data['home_team_stats']['passes'],
stats_data['home_team_stats']['pass_accuracy']],
'南非': [stats_data['away_team_stats']['passes'],
stats_data['away_team_stats']['pass_accuracy']]
}
pass_df = pd.DataFrame(pass_data, index=['传球次数', '传球成功率(%)'])
pass_df.plot(kind='bar', ax=axes[1, 0], color=['#D32F2F', '#1B5E20'])
axes[1, 0].set_title('传球数据对比')
axes[1, 0].set_ylabel('数值')
axes[1, 0].tick_params(axis='x', rotation=0)
axes[1, 0].grid(True, alpha=0.3)
# 4. 预期进球(xG)对比
xg_data = [stats_data['key_stats']['expected_goals']['Czech Republic'],
stats_data['key_stats']['expected_goals']['South Africa']]
bars = axes[1, 1].bar(labels, xg_data, color=colors)
axes[1, 1].set_title('预期进球(xG)对比')
axes[1, 1].set_ylabel('xG值')
axes[1, 1].grid(True, alpha=0.3)
# 添加数值标签
for bar, value in zip(bars, xg_data):
height = bar.get_height()
axes[1, 1].text(bar.get_x() + bar.get_width()/2., height + 0.05,
f'{value:.2f}', ha='center', va='bottom', fontsize=12)
plt.tight_layout()
plt.savefig('match_analysis.png', dpi=300, bbox_inches='tight')
plt.show()
print("\n可视化图表已保存为 'match_analysis.png'")
# 创建可视化
create_match_visualizations(match_stats)
步骤五:使用机器学习进行比赛模式分析
我们可以使用简单的聚类分析来识别比赛中的不同阶段和模式。
“`python
from sklearn.cluster import KMeans
from sklearn.preprocessing import StandardScaler
import numpy as np
def analyze_match_patterns():
“”“分析比赛模式”“”
# 模拟比赛时间段数据(每5分钟为一个时间段)
time_periods = list(range(0, 95, 5)) # 0-90分钟,每5分钟
# 模拟每个时间段的事件数据
np.random.seed(42) # 保证结果可重现
# 捷克队每个时间段的进攻活动
czech_attack = np.random.poisson(2.5, len(time_periods))
czech_attack[7] = 4 # 第35-40分钟(进球时段)进攻活动增加
czech_attack[8] = 3 # 第40-45分钟
# 南非队每个时间段的进攻活动
south_africa_attack = np.random.poisson(1.8, len(time_periods))
south_africa_attack[12] = 4 # 第60-65分钟(进球时段)进攻活动增加
south_africa_attack[13] = 3 # 第65-70分钟
# 创建数据矩阵
X = np.column_stack([czech_attack, south_africa_attack])
# 标准化数据
scaler = StandardScaler()
X_scaled = scaler.fit_transform(X)
# 使用K-means聚类分析
kmeans = KMeans(n_clusters=3, random_state=42, n_init=10)
clusters = kmeans.fit_predict(X_scaled)
# 可视化聚类结果
plt.figure(figsize=(12, 6))
# 按聚类着色
colors = ['red', 'blue', 'green']
for i in range(3):
mask = clusters == i
plt.scatter(np.array(time_periods)[mask], X[mask, 0],
color=colors[i], label=f'阶段 {i+1}', alpha=0.7, s=100)
plt.scatter(np.array(time_periods)[mask], X[mask, 1],
color=colors[i], alpha=0.7, s=100, marker='x')
plt.xlabel('比赛时间(分钟)')
plt.ylabel('进攻活动指数')
plt.title('比赛模式聚类分析')
plt.legend(['捷克进攻', '南非进攻'])
plt.grid(True, alpha=0.3)
# 添加进球时间标记
plt.axvline(x=38, color='gray', linestyle='--', alpha=0.5)
plt.text(38, max(X[:, 0]), '捷克进球', rotation=90, alpha=0.7)
plt.axvline(x=65, color='gray', linestyle='--', alpha=0.5)
plt.text(65, max(X[:, 0]), '南非进球', rotation=90, alpha=0.7)
plt.tight_layout()
plt.savefig('match_patterns.png', dpi=300)
plt.show()
# 分析聚类结果
print("\n比赛阶段分析:")
for i in range(3):
cluster_mask = clusters == i
cluster_times = np.array(time_periods)[cluster_mask]
if len(cluster_times) > 0:
print(f"\n