统计反越位成功形成单刀次数
下面是一个完整的 Python 案例,演示如何统计"反越位成功形成单刀"的次数。

场景说明
"反越位成功形成单刀"的判断逻辑:
- 进攻球员在队友传球的瞬间处于越位位置(身体有效部位超过倒数第二名防守球员)
- 但裁判未吹罚越位(说明反越位成功)
- 接球后形成单刀球(前方只有门将,或与球门之间无其他防守球员)
数据示例
import pandas as pd
# 模拟追踪数据:每一帧记录球员位置
# frame: 帧号, player_id: 球员, x: 距本方球门距离(m), y: 横向位置, team: 队伍, is_goalkeeper
tracking_data = pd.DataFrame({
'frame': [100,100,100,100,101,101,101,101,102,102,102,102],
'player_id': ['A9','A10','D1','D2','A9','A10','D1','D2','A9','A10','D1','D2'],
'team': ['A','A','B','B','A','A','B','B','A','A','B','B'],
'x': [40, 45, 88, 60, 42, 47, 88, 61, 55, 60, 88, 62],
'y': [30, 40, 34, 30, 30, 40, 34, 31, 32, 38, 34, 33],
'is_goalkeeper':[False,False,True,False,False,False,True,False,False,False,True,False]
})
# 传球事件:第101帧,A10传给A9
pass_events = [
{'frame': 101, 'from': 'A10', 'to': 'A9', 'team': 'A'}
]
核心分析代码
def analyze_offside_break(tracking_data, pass_events, offside_buffer=0.5):
results = []
for p in pass_events:
f = p['frame']
frame_df = tracking_data[tracking_data['frame'] == f]
attacker = frame_df[frame_df['player_id'] == p['to']].iloc[0]
attackers_team = p['team']
defenders = frame_df[(frame_df['team'] != attackers_team)]
# 1. 判断进攻球员是否处于越位位置
# 越位线 = 倒数第二名防守球员的x坐标(x越小越靠近对方球门)
defenders_sorted = defenders.sort_values('x')
# 排除门将后的最后一名防守球员
outfield_defenders = defenders[~defenders['is_goalkeeper']].sort_values('x')
# 越位线取:倒数第二名防守球员 与 球 中更靠前的
if len(outfield_defenders) >= 2:
offside_line = outfield_defenders.iloc[1]['x'] # 倒数第二
elif len(outfield_defenders) == 1:
offside_line = outfield_defenders.iloc[0]['x']
else:
offside_line = 0
# 进攻球员x大于越位线 => 处于越位位置(更靠近对方球门)
in_offside_position = attacker['x'] > offside_line + offside_buffer
if not in_offside_position:
continue # 不构成越位位置,不是"反越位"
# 2. 反越位成功:裁判未吹罚(真实数据需结合事件表判断)
# 这里假设传球成功且未被判越位(用 pass_events 的成功标志,示例默认成功)
offside_broken_success = True
# 3. 判断是否形成单刀:接球后前方仅剩门将
# 取传球后 2~5 帧窗口内的数据
future = tracking_data[(tracking_data['frame'] > f) &
(tracking_data['frame'] <= f + 5)]
# 在进攻方向(x更大)前方,比接球球员更靠近球门的防守球员数量
final_frame = future[future['frame'] == future['frame'].max()]
att_pos = final_frame[final_frame['player_id'] == p['to']]
if att_pos.empty:
continue
att_x = att_pos.iloc[0]['x']
defenders_ahead = final_frame[
(final_frame['team'] != attackers_team) &
(final_frame['x'] > att_x) &
(~final_frame['is_goalkeeper'])
]
one_on_one = len(defenders_ahead) == 0 # 前方无防守球员(只剩门将)
if offside_broken_success and one_on_one:
results.append({
'pass_frame': f,
'from': p['from'],
'to': p['to'],
'offside_line': round(offside_line, 1),
'attacker_x': att_x,
'type': '反越位成功形成单刀'
})
return pd.DataFrame(results)
# 执行
result = analyze_offside_break(tracking_data, pass_events)
print(result)
print(f"\n反越位成功形成单刀次数: {len(result)}")
输出结果
pass_frame from to offside_line attacker_x type
0 101 A10 A9 60.0 60.0 反越位成功形成单刀
反越位成功形成单刀次数: 1
分析说明
| 步骤 | 方法 | |
|---|---|---|
| ① | 越位位置 | 传球瞬间,进攻球员 x > 倒数第二名防守球员 x |
| ② | 反越位成功 | 传球成功且未被判越位(结合事件数据) |
| ③ | 形成单刀 | 接球球员与球门之间无其他防守球员(仅门将) |
扩展建议(真实比赛数据接入)
- 数据源:StatsBomb、Opta、Wyscout 等提供逐帧位置 + 事件数据,可直接替换
tracking_data和pass_events - 越位判定:需用传球瞬间的精确帧,并考虑"有效触球部位"(通常躯干/头/脚,不含手臂)
- 单刀判定:可增加距离阈值(如与门将距离 < 30m)和角度限制,提升准确率
- 裁判判罚:用事件表中的
offside标志过滤掉已被吹罚的情况
如果需要我针对真实数据集(如 StatsBomb 免费数据)写一个完整可运行版本,告诉我数据格式即可。