本文目录导读:

在足球数据分析中,进攻三区(Final Third)传球成功率是衡量球队在对手半场最后30米区域创造机会、控制比赛能力的关键指标。
需要注意的是,标准的Python库(如pandas、numpy)本身并不包含足球数据,要计算这个指标,你需要数据源(例如StatsBomb、Opta、Kaggle数据集)以及相应的列名。
下面我提供两种最常见的Python实现场景:基于你已有的DataFrame(假设有事件数据) 和 基于StatsBomb公开数据(最经典的案例)。
假设你有一个结构化的传球事件数据集(通用案例)
假设你的DataFrame(df)包含以下列:
x:传球起始点的X坐标(0-100,从己方底线到对方底线)。y:传球起始点的Y坐标(0-100)。outcome:传球结果(Complete或Incomplete/Successful或Unsuccessful)。team:球队名称。
计算逻辑: 进攻三区通常指对方球门前的最后30米,如果球场标准长度为100米(X轴0-100),那么X坐标大于70(即100-30=70)的区域即为进攻三区。
import pandas as pd
# 假设 df 是你的原始数据框
# df = pd.read_csv('your_passing_data.csv')
# 筛选成功/不成功的传球数据(请根据你的列名调整)
# 假设成功传球标记为 'Complete'
successful_passes = df[df['outcome'] == 'Complete']
total_passes = df # 所有尝试的传球
# 筛选在进攻三区(X坐标 > 70)的传球
final_third_successful = successful_passes[successful_passes['x'] > 70]
final_third_total = total_passes[total_passes['x'] > 70]
# 计算成功率(注意防止除零错误)
total_count = len(final_third_total)
success_count = len(final_third_successful)
if total_count > 0:
success_rate = (success_count / total_count) * 100
print(f"进攻三区传球总次数: {total_count}")
print(f"进攻三区成功传球次数: {success_count}")
print(f"进攻三区传球成功率: {success_rate:.2f}%")
else:
print("该球队在进攻三区没有尝试传球。")
# --- 进阶:如果你需要按队伍(Team)分组计算 ---
# result = df[df['x'] > 70].groupby('team')['outcome'].agg(
# total='count',
# successful=lambda x: (x == 'Complete').sum()
# )
# result['success_rate'] = (result['successful'] / result['total']) * 100
# print(result)
使用 StatsBomb 公开数据(最专业的案例)
StatsBomb 提供了免费的公开数据集(如2020年欧洲杯、梅西生涯数据等),其坐标x的范围是0-120,并且数据以JSON格式存储。
核心难点: StatsBomb 的数据中,location字段是一个列表 [x, y]。
案例:分析某支球队的进攻三区传球成功率
import json
import pandas as pd
from pandas.io.json import json_normalize # 或者使用 pd.json_normalize
# 1. 加载数据(假设你下载了 match 和 events 的JSON)
# 这里以一个示例为简化,通常你需要先解析事件文件
with open('events.json', 'r') as f:
events = json.load(f)
# 2. 使用 pd.json_normalize 将嵌套的JSON转换为DataFrame
df = pd.json_normalize(events, sep='_')
# 3. 筛选出传球事件
passes = df[df['type_name'] == 'Pass']
# 4. 提取传球起点坐标(StatsBomb的x坐标范围是0-120)
# 注意:反序列化后可能需要先处理缺失值
passes['start_x'] = passes['location'].apply(lambda x: x[0] if x is not None else None)
# 5. 定义进攻三区(120码/米的标准球场,最后20%为进攻三区,即120-24=96)
# 更精确的标准:最后30米是惯例,但StatsBomb通常用 x > 80 或 x > 96 可调整。
# 国际惯例中,如果球门在x=120,那么进攻三区为 x > 80 的区域(最后1/3)。
THRESHOLD = 80 # 或者 96/100,取决于分析口径
final_third_passes = passes[passes['start_x'] > THRESHOLD]
# 6. 计算成功率
# 传球成功与否在pass.outcome_name中,不成功会有'Incomplete',如果成功则为空
# 策略:先统计总数,再统计没有“outcome”的即为成功。
attempts = len(final_third_passes)
successful = final_third_passes[final_third_passes['pass_outcome_name'].isna()] # 如果成功,outcome名称通常是NaN
success_rate = (len(successful) / attempts) * 100 if attempts > 0 else 0
print(f"StatsBomb数据 — 进攻三区传球次数: {attempts}")
print(f"StatsBomb数据 — 进攻三区成功率: {success_rate:.2f}%")
补充:如果只是求一道算法题/函数题(Python逻辑)
有时候这可能是一道面试或算法题,如果你已经有一个包含传球方向和结果的列表或元组,核心计算就是一个聚合求和:
# 假设你有一个列表 deff: [x坐标, 是否成功(1/0)]
data = [
(75, 1), # 成功
(82, 0), # 失败
(69, 1), # 未进三区(x<70),忽略
(90, 1)
]
# 过滤进攻三区
final_third = [(x, y) for x, y in data if x > 70]
# 计算
total = len(final_third)
success = sum(y for x, y in final_third)
print(f"成功率: {success/total:.2%}")
- 坐标定义:进攻三区的判定完全取决于你的坐标范围。最常用的是
x > 70(如果球场长度为100)。 - 数据清洗:确保传球事件中包含了实际的传球起始点坐标。
- 分母分子:分子是成功传球次数,分母是所有尝试的传球次数(包括被断、失误),不包含定位球中的越位。
注意: 上述代码假设了列名,你需要根据实际下载的数据集调整列名(如type.id、location等),如果你有具体的CSV文件列头或具体数据集,请发给我,我可以为你精确调整代码。