实用脚本统计交叉跑位造成威胁几次?

wen 实用脚本 4

统计交叉跑位造成威胁次数

下面给你一个可落地的统计方案 + Python 脚本模板,核心思路是:先定义“交叉跑位”和“威胁”的判定规则,再用事件数据或追踪数据自动统计。

实用脚本统计交叉跑位造成威胁几次?


先明确判定规则(关键)

什么是交叉跑位?

常见三种定义,选一种或组合:

类型 定义 数据需求
A. 位置交叉 两名同队球员在短时间内(如2秒内)跑动轨迹相交 追踪数据(x,y,t)
B. 方向交叉 两人跑动方向夹角 > 90°且路径接近 追踪数据
C. 事件型 传球后接球人穿越防守/队友跑位 事件数据

什么是“造成威胁”?

  • 交叉后 3~5 秒内 出现以下之一:
    • 射门
    • 关键传球 / 助攻
    • 进入禁区
    • xG 提升超过阈值(如 +0.05)
    • 对方失位 / 犯规

Python 脚本模板(基于追踪数据)

import pandas as pd
import numpy as np
from itertools import combinations
# ========== 参数配置 ==========
CROSS_WINDOW = 2.0      # 交叉判定时间窗(秒)
THREAT_WINDOW = 5.0     # 交叉后威胁判定时间窗(秒)
MIN_DIST = 1.5          # 轨迹相交距离阈值(米)
DIR_ANGLE_THRESH = 90   # 方向夹角阈值(度)
XG_GAIN_THRESH = 0.05   # xG提升阈值
# ========== 读取数据 ==========
# tracking: columns = [frame, time, player_id, team, x, y]
tracking = pd.read_csv("tracking.csv")
def get_player_track(df, pid):
    return df[df.player_id == pid].sort_values("time").reset_index(drop=True)
def paths_cross(t1, t2, window=CROSS_WINDOW, min_dist=MIN_DIST):
    """判断两条轨迹在时间窗内是否相交"""
    t_start = max(t1.time.min(), t2.time.min())
    t_end   = min(t1.time.max(), t2.time.max())
    for t in np.arange(t_start, t_end, 0.1):
        p1 = t1.iloc[(t1.time - t).abs().argmin()]
        p2 = t2.iloc[(t2.time - t).abs().argmin()]
        if abs(p1.time - t) > 0.2 or abs(p2.time - t) > 0.2:
            continue
        d = np.hypot(p1.x - p2.x, p1.y - p2.y)
        if d < min_dist:
            return True, t
    return False, None
def directions_opposite(t1, t2, t_cross):
    """判断交叉瞬间两人方向是否对向/交叉"""
    def vel(track, t):
        i = (track.time - t).abs().argmin()
        if i == 0 or i >= len(track) - 1:
            return None
        dt = track.time.iloc[i+1] - track.time.iloc[i-1]
        if dt == 0: return None
        vx = (track.x.iloc[i+1] - track.x.iloc[i-1]) / dt
        vy = (track.y.iloc[i+1] - track.y.iloc[i-1]) / dt
        return np.array([vx, vy])
    v1, v2 = vel(t1, t_cross), vel(t2, t_cross)
    if v1 is None or v2 is None: return False
    cos = np.dot(v1, v2) / (np.linalg.norm(v1)*np.linalg.norm(v2) + 1e-9)
    angle = np.degrees(np.arccos(np.clip(cos, -1, 1)))
    return angle > DIR_ANGLE_THRESH
# ========== 主流程 ==========
results = []
for team in tracking.team.unique():
    team_df = tracking[tracking.team == team]
    players = team_df.player_id.unique()
    for p1, p2 in combinations(players, 2):
        t1, t2 = get_player_track(team_df, p1), get_player_track(team_df, p2)
        crossed, t_cross = paths_cross(t1, t2)
        if not crossed:
            continue
        if not directions_opposite(t1, t2, t_cross):
            continue
        # 检查交叉后威胁窗口内是否有威胁事件
        threat = threats[
            (threats.team == team) &
            (threats.time >= t_cross) &
            (threats.time <= t_cross + THREAT_WINDOW)
        ]
        is_threat = (
            (threat.event.isin(["shot","key_pass","assist","penalty_area_entry"])).any()
            or (threat.xG.diff().max() > XG_GAIN_THRESH)
        )
        results.append({
            "team": team,
            "players": f"{p1}-{p2}",
            "time": round(t_cross, 1),
            "threat": is_threat
        })
df_res = pd.DataFrame(results)
print("总交叉跑位次数:", len(df_res))
print("造成威胁次数:", df_res.threat.sum())
print("\n按球队统计:")
print(df_res.groupby("team").threat.agg(["count","sum"]))

如果没有追踪数据(只有事件数据)

用简化版规则:传球事件中,接球人在传球瞬间与另一队友位置交叉,且之后5秒内有射门/关键传球

# events: [time, team, player, event, x, y, end_x, end_y]
cross_events = []
for _, pass_ in events[events.event == "pass"].iterrows():
    t = pass_.time
    teammates = events[
        (events.team == pass_.team) &
        (events.time.between(t-1, t+1)) &
        (events.player != pass_.player)
    ]
    for _, mate in teammates.iterrows():
        if abs(mate.x - pass_.end_x) < 2 and abs(mate.y - pass_.end_y) < 2:
            # 视为跑位交汇点
            follow = events[
                (events.team == pass_.team) &
                (events.time.between(t, t+5)) &
                (events.event.isin(["shot","key_pass","assist"]))
            ]
            cross_events.append({
                "time": t,
                "players": f"{pass_.player}-{mate.player}",
                "threat": len(follow) > 0
            })
df = pd.DataFrame(cross_events).drop_duplicates(subset=["time","players"])
print(f"交叉跑位形成威胁 {df.threat.sum()} 次 / 共 {len(df)} 次")

输出示例

总交叉跑位次数: 47
造成威胁次数: 12
按球队统计:
      count  sum
teamA    28    8
teamB    19    4

可扩展优化方向

  1. 加入防守球员:只有当交叉导致防守错位才算威胁。
  2. xT / xG 模型:用位置威胁值替代简单事件。
  3. 可视化:用 mplsoccer 画出交叉轨迹与后续威胁。
  4. 防误判:加最小间隔时间,排除摆渡、回追。

需要我根据你手上的具体数据格式(StatsBomb / 自家追踪 / Opta)改写成可直接跑的版本吗?告诉我列名和数据来源即可。

抱歉,评论功能暂时关闭!