统计交叉跑位造成威胁次数
下面给你一个可落地的统计脚本方案,适用于足球/篮球等团队运动的比赛数据(事件日志/坐标追踪)。

先定义"交叉跑位造成威胁"
需要量化标准,建议如下(可按项目调整):
| 条件 | 说明 |
|---|---|
| 交叉跑位 | 两名同队球员在时间窗 T(如3秒)内跑动轨迹相交/换位 |
| 造成威胁 | 交叉后 W 秒(如5秒)内出现:射门、关键传球、进入禁区、对方失位、犯规等 |
| 计数 | 每次有效交叉+后续威胁 = 1次 |
数据输入格式(示例 CSV)
frame,time,player_id,team,x,y 1,0.0,P1,A,30.0,40.0 1,0.0,P2,A,32.0,42.0 2,0.2,P1,A,30.5,40.2 2,0.2,P2,A,31.8,41.7 ...
事件表:
time,type,team,player 12.4,shot,A,P3 12.6,key_pass,A,P1
核心脚本(Python)
import pandas as pd
import numpy as np
from itertools import combinations
# ============ 参数 ============
CROSS_WINDOW = 3.0 # 交叉判定时间窗(秒)
THREAT_WINDOW = 5.0 # 交叉后多久内算威胁
MIN_DIST = 1.5 # 两球员最近距离阈值(米) 视为"交叉"
SAME_TEAM_ONLY = True
# ============ 读取数据 ============
tracks = pd.read_csv("tracking.csv") # frame,time,player_id,team,x,y
events = pd.read_csv("events.csv") # time,type,team,player
THREAT_TYPES = {"shot", "key_pass", "penalty_area_entry", "foul_won", "goal"}
# ============ 1. 检测交叉跑位 ============
def detect_crossings(tracks):
crossings = []
for (team, pid1, pid2), grp in tracks.groupby(
["team", "player_id", "player_id"] # 占位,下面重算
):
pass
# 更清晰写法:按队伍分组,遍历同队球员对
for team, team_df in tracks.groupby("team"):
players = team_df["player_id"].unique()
for p1, p2 in combinations(players, 2):
d1 = team_df[team_df.player_id == p1].sort_values("time")
d2 = team_df[team_df.player_id == p2].sort_values("time")
merged = pd.merge_asof(
d1, d2, on="time", suffixes=("_1", "_2"),
tolerance=0.2, direction="nearest"
).dropna(subset=["x_2", "y_2"])
if merged.empty:
continue
merged["dist"] = np.hypot(
merged["x_1"] - merged["x_2"],
merged["y_1"] - merged["y_2"]
)
# 找距离小于阈值的时刻 = 交叉发生点
close = merged[merged["dist"] < MIN_DIST]
if close.empty:
continue
# 合并相近时刻为一组"交叉事件"
close = close.sort_values("time").copy()
close["gap"] = close["time"].diff().fillna(999)
close["group"] = (close["gap"] > CROSS_WINDOW).cumsum()
for _, g in close.groupby("group"):
crossings.append({
"team": team,
"p1": p1, "p2": p2,
"time": g["time"].iloc[0],
"x": (g["x_1"].mean() + g["x_2"].mean()) / 2,
"y": (g["y_1"].mean() + g["y_2"].mean()) / 2,
})
return pd.DataFrame(crossings)
# ============ 2. 判定交叉后是否造成威胁 ============
def tag_threat(crossings, events):
events = events[events["type"].isin(THREAT_TYPES)].copy()
results = []
for _, c in crossings.iterrows():
window = events[
(events["team"] == c["team"]) &
(events["time"] >= c["time"]) &
(events["time"] <= c["time"] + THREAT_WINDOW)
]
c2 = c.copy()
c2["threat"] = not window.empty
c2["threat_type"] = ",".join(window["type"].tolist())
c2["threat_time"] = window["time"].iloc[0] if not window.empty else None
results.append(c2)
return pd.DataFrame(results)
# ============ 3. 执行 ============
crossings = detect_crossings(tracks)
result = tag_threat(crossings, events)
threat_count = result["threat"].sum()
total_cross = len(result)
print(f"总交叉跑位次数: {total_cross}")
print(f"造成威胁次数: {threat_count}")
print(f"转化率: {threat_count / total_cross:.1%}" if total_cross else "无交叉")
result.to_csv("crossing_threats.csv", index=False)
# 按球员统计贡献
if not result.empty:
print("\n— 球员参与威胁交叉排行 —")
long_df = result[result.threat].melt(
id_vars=["time", "threat_type"],
value_vars=["p1", "p2"],
value_name="player"
)
print(long_df["player"].value_counts())
运行 & 输出
python crossing_threat.py
输出示例:
总交叉跑位次数: 27
造成威胁次数: 8
转化率: 29.6%
— 球员参与威胁交叉排行 —
P7 3
P11 3
P4 2
P9 2
生成 crossing_threats.csv,含每次威胁交叉的时间、位置、球员、威胁类型。
调优建议
| 参数 | 建议值 | 用途 |
|---|---|---|
CROSS_WINDOW |
2–4 秒 | 太短会拆分,太长会合并 |
THREAT_WINDOW |
3–8 秒 | 反映"交叉后立即威胁" |
MIN_DIST |
0–2.0 米 | 依追踪精度调整 |
| 威胁类型 | 按项目自定义 | 加"吸引防守""致对方犯规"等 |
进阶方向
- 用速度方向夹角判定"真正换位",避免只是靠近:
v1 = np.array([dx1, dy1]); v2 = np.array([dx2, dy2]) dot = (v1 @ v2) / (np.linalg.norm(v1)*np.linalg.norm(v2) + 1e-6) # dot < -0.5 表示相向交叉
- 预期威胁值 (xT) 替代硬性"射门/传球",用概率图加权。
- 可视化:用 mplsoccer 画出交叉点和威胁点轨迹热图。
- 对接真实数据:StatsBomb、Metrica、SkillCorner 的格式可直接适配。
需要我帮你适配具体数据格式(如 StatsBomb 事件或 SkillCorner 追踪),或者加上可视化热图吗?告诉我你的数据来源即可。
版权声明:NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
足球直播
足球直播
足球直播
篮球直播
篮球直播
篮球直播
NBA直播
NBA直播
NBA直播
CBA直播
CBA直播
CBA直播
亚冠直播
亚冠直播
亚冠直播
欧冠直播
欧冠直播
欧冠直播
英超直播
英超直播
英超直播
西甲直播
西甲直播
西甲直播
中超直播
中超直播
中超直播
足球直播
足球直播
足球直播
篮球直播
篮球直播
篮球直播
NBA直播
NBA直播
NBA直播
CBA直播
CBA直播
CBA直播
亚冠直播
亚冠直播
亚冠直播
欧冠直播
欧冠直播
欧冠直播
英超直播
英超直播
英超直播
西甲直播
西甲直播
西甲直播
中超直播
中超直播
中超直播
足球直播
足球直播
足球直播
篮球直播
篮球直播
篮球直播
NBA直播
NBA直播
NBA直播
CBA直播
CBA直播
CBA直播
亚冠直播
亚冠直播
亚冠直播
欧冠直播
欧冠直播
欧冠直播
英超直播
英超直播
英超直播
西甲直播
西甲直播
西甲直播
中超直播
中超直播
中超直播
足球直播
足球直播
足球直播
篮球直播
篮球直播
篮球直播
NBA直播
NBA直播
NBA直播
CBA直播
CBA直播
CBA直播
亚冠直播
亚冠直播
亚冠直播
欧冠直播
欧冠直播
欧冠直播
英超直播
英超直播
英超直播
西甲直播
西甲直播
西甲直播
中超直播
中超直播
中超直播
曼联直播
曼联直播
曼联直播
日职联直播
日职联直播
日职联直播
澳超直播
澳超直播
澳超直播
阿森纳直播
阿森纳直播
阿森纳直播
足球直播
足球直播
足球直播
NBA直播
NBA直播
NBA直播
曼联直播
曼联直播
曼联直播
日职联直播
日职联直播
日职联直播
澳超直播
澳超直播
澳超直播
阿森纳直播
阿森纳直播
阿森纳直播
足球直播
足球直播
足球直播
NBA直播
NBA直播
NBA直播
曼联直播
曼联直播
曼联直播
日职联直播
日职联直播
日职联直播
澳超直播
澳超直播
澳超直播
阿森纳直播
阿森纳直播
阿森纳直播
足球直播
足球直播
足球直播
NBA直播
NBA直播
NBA直播
曼联直播
曼联直播
曼联直播
日职联直播
日职联直播
日职联直播
澳超直播
澳超直播
澳超直播
阿森纳直播
阿森纳直播
阿森纳直播
足球直播
足球直播
足球直播
NBA直播
NBA直播
NBA直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
足球直播
足球直播
足球直播
足球直播
足球直播
英超直播
英超直播
英超直播
英超直播
英超直播
篮球直播
篮球直播
篮球直播
篮球直播
篮球直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
中超直播
中超直播
中超直播
中超直播
中超直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
足球直播
足球直播
足球直播
足球直播
足球直播
英超直播
英超直播
英超直播
英超直播
英超直播
篮球直播
篮球直播
篮球直播
篮球直播
篮球直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
中超直播
中超直播
中超直播
中超直播
中超直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播