本文目录导读:

统计头球争顶成功率(Aerial Duels Won Rate)是足球数据分析中非常实用的一个指标,下面我提供一个基于Python的实用脚本,它能够处理比赛事件数据并输出头球争顶的成功率。
这个脚本会覆盖数据清洗、特征计算和结果输出三个方面,你可以直接复制或根据你的数据格式进行调整。
前提与数据结构
在大多数体育数据供应商(如Opta、StatsBomb)的数据中,头球争顶通常分为两类事件:
- 争顶成功:球员在空中对抗中赢得球权。
- 争顶失败:球员在空中对抗中失去球权(或未触到球)。
假设你的数据格式为(列名):
player:球员姓名event_type:事件类型("Aerial"或"Duel")duel_type:对抗类型("Aerial")outcome:结果("Won"或"Lost")minute:比赛时间
Python 脚本
import pandas as pd
import numpy as np
def calculate_aerial_duel_success_rate(df):
"""
计算头球争顶成功率(针对球员和球队)。
参数:
df (pd.DataFrame): 包含头球争顶事件的数据框。
返回:
pd.DataFrame: 包含头部争顶成功率的汇总数据。
"""
# 1. 数据清洗与筛选
# 只保留头球争顶事件(这里假设 duel_type == 'Aerial',或 event_type == 'Aerial')
aerial_df = df[
(df['event_type'] == 'Aerial') |
((df['duel_type'] == 'Aerial') & (df['event_type'] == 'Duel'))
].copy()
# 如果没有任何头球事件,返回空
if aerial_df.empty:
print("警告:数据中未找到头球争顶事件。")
return pd.DataFrame()
# 标准化结果列(处理大小写和不同表达方式)
aerial_df['outcome_normalized'] = aerial_df['outcome'].str.strip().str.lower()
aerial_df['is_won'] = np.where(aerial_df['outcome_normalized'].isin(['won', 'success', 'win', '1', 'true']), 1, 0)
# 2. 按球员统计
player_stats = aerial_df.groupby('player').agg(
总争顶次数=('is_won', 'count'),
成功次数=('is_won', 'sum')
).reset_index()
player_stats['成功率(%)'] = (player_stats['成功次数'] / player_stats['总争顶次数'] * 100).round(2)
player_stats = player_stats.sort_values('成功率(%)', ascending=False)
# 3. 按球队统计(如果数据中有球队列)
if 'team' in aerial_df.columns:
team_stats = aerial_df.groupby('team').agg(
总争顶次数=('is_won', 'count'),
成功次数=('is_won', 'sum')
).reset_index()
team_stats['成功率(%)'] = (team_stats['成功次数'] / team_stats['总争顶次数'] * 100).round(2)
team_stats = team_stats.sort_values('成功率(%)', ascending=False)
return player_stats, team_stats
else:
return player_stats
# --- 使用示例 ---
if __name__ == "__main__":
# 模拟一份虚拟数据(实际使用中请替换为你的数据源,如CSV或API)
data = {
'player': ['球员A', '球员B', '球员A', '球员C', '球员B', '球员A', '球员C', '球员A', '球员B', '球员B'],
'team': ['主队', '主队', '主队', '客队', '客队', '客队', '主队', '客队', '主队', '客队'],
'event_type': ['Aerial', 'Duel', 'Aerial', 'Aerial', 'Aerial', 'Duel', 'Aerial', 'Aerial', 'Aerial', 'Aerial'],
'duel_type': ['Aerial', 'Aerial', 'Aerial', 'Aerial', 'Aerial', 'Aerial', 'Aerial', 'Aerial', 'Aerial', 'Aerial'],
'outcome': ['Won', 'Lost', 'Lost', 'Won', 'Won', 'Won', 'Lost', 'Won', 'Lost', 'Won'], # Won=1, Lost=0
'minute': [10, 23, 45, 67, 70, 80, 85, 88, 90, 91]
}
df = pd.DataFrame(data)
print("原始数据:")
print(df)
print("\n")
# 计算成功率
result = calculate_aerial_duel_success_rate(df)
# 输出结果
if isinstance(result, tuple):
player_result, team_result = result
print("球员头球争顶成功率:")
print(player_result)
print("\n球队头球争顶成功率:")
print(team_result)
else:
print("头球争顶成功率:")
print(result)
脚本解释与扩展
- 筛选逻辑:脚本通过
event_type或duel_type来判断是否为头球争顶,如果数据源不同,只需修改这里的条件即可。 - 结果标准化:现实中不同数据源对“成功”的定义可能不同(如
Won,Succ),脚本已将常见的关键词统一映射为0和1。 - 统计维度:
- 球员维度:关注个人对抗能力。
- 球队维度(可选):关注整体高空对抗强度。
- 过滤条件扩展:你可以轻松增加额外参数,只统计禁区内的头球”或“只统计特定时间段”。
df = df[df['area'] == 'Defensive third']
- 百分比安全性:如果某球员争顶次数为0,
round()和sort_values会自动处理,但建议在输出前检查是否为空。
如果数据来自 CSV 文件
将脚本中的模拟数据部分替换为:
df = pd.read_csv('your_match_events.csv') # 你的CSV路径
如果数据是 JSON(如 StatsBomb):
df = pd.json_normalize(data, record_path='events') # 根据实际结构调整
进阶建议(可选增强)
如果你想统计“特定位置”(如中后卫)或“特定时刻”(如下半场最后15分钟),可以在函数中增加参数:
def calculate_aerial_duel_success_rate(df, position_filter=None, minute_filter=None):
# ... 在筛选后增加:
if position_filter:
df = df[df['position'] == position_filter]
if minute_filter:
df = df[df['minute'] >= minute_filter[0] & df['minute'] <= minute_filter[1]]