本文目录导读:

这是一个非常经典的足球数据分析问题,要判断“哪次射门最具决定性”,不能只看进球本身,因为“决定性”意味着如果没有这个进球,比赛结果会发生改变。
我们会使用 “预期进球差值(xG Delta)” 或 “比赛态势转折点” 来进行量化复盘。
这里我提供一个基于 Python + Pandas 的案例复盘框架,并附上具体代码逻辑,假设我们有一场比赛的射门事件数据。
案例场景假设
假设一场比赛(曼城 vs 利物浦)的射门数据如下,我们需要找出“扳平比分”和“绝杀”的时刻。
| 时间 | 球队 | 球员 | xG(预期进球) | 是否进球 | 当前比分(主:客) |
|---|---|---|---|---|---|
| 12‘ | 曼城 | A | 05 | 否 | 0:0 |
| 23‘ | 利物浦 | B | 08 | 否 | 0:0 |
| 34‘ | 利物浦 | B | 45 | 是 | 0:1 |
| 41‘ | 曼城 | C | 12 | 否 | 0:1 |
| 55‘ | 曼城 | D | 30 | 是 | 1:1 |
| 67‘ | 利物浦 | E | 18 | 否 | 1:1 |
| 89‘ | 曼城 | F | 75 | 是 | 2:1 |
核心逻辑:如何定义“决定性”?
- 绝对决定性(绝杀/制胜球):让球队从“非领先”状态变成“领先”状态,且最终比分保持不变。
- 相对决定性(扳平球):让球队从“落后”变成“平局”,改变了比赛走势。
- 模型化(xG差值法):计算每个进球前后的 “赢球概率” 变化。
我推荐使用赢球概率变化法,这里我们用简单的逻辑模拟:“如果这个球没进,最终结果会变吗?”
Python 代码实现
import pandas as pd
# 1. 加载射门数据
data = {
'time': [12, 23, 34, 41, 55, 67, 89],
'team': ['Man City', 'Liverpool', 'Liverpool', 'Man City', 'Man City', 'Liverpool', 'Man City'],
'player': ['A', 'B', 'B', 'C', 'D', 'E', 'F'],
'xG': [0.05, 0.08, 0.45, 0.12, 0.30, 0.18, 0.75],
'is_goal': [False, False, True, False, True, False, True],
'score_home': [0, 0, 0, 0, 1, 1, 2], # 主队(曼城)实时比分
'score_away': [0, 0, 1, 1, 1, 1, 1], # 客队(利物浦)实时比分
}
df = pd.DataFrame(data)
# 2. 定义一个函数:计算“如果没有这个进球”的假设比分
def calculate_decider(df):
results = []
for idx, row in df.iterrows():
if not row['is_goal']:
continue # 只分析进球事件
actual_home = row['score_home']
actual_away = row['score_away']
# 模拟“如果这个球没进”的比分
if row['team'] == 'Man City': # 曼城进球
hypothetical_home = actual_home - 1
hypothetical_away = actual_away
else: # 利物浦进球
hypothetical_home = actual_home
hypothetical_away = actual_away - 1
# 3. 判断决定性
# 场景A: 如果没进,平局 -> 进了,赢球(制胜球)
if hypothetical_home == hypothetical_away and actual_home > actual_away:
decision = "绝杀/制胜球 (Winning Goal)"
# 场景B: 如果没进,输球 -> 进了,平局(扳平球)
elif hypothetical_home < hypothetical_away and actual_home == actual_away:
decision = "扳平球 (Equalizer)"
# 场景C: 如果没进,输球 -> 进了,赢球(逆转球)
elif hypothetical_home < hypothetical_away and actual_home > actual_away:
decision = "逆转球 (Comeback Goal)"
# 场景D: 如果没进,还是赢 -> 进了,还是赢(锦上添花)
elif hypothetical_home > hypothetical_away and actual_home > actual_away:
decision = "锦上添花 (Insurance Goal)"
else:
decision = "普通进球"
# 计算xG差值(可选)
xg_influence = row['xG'] # 这里简化,实际可用赢球概率模型
results.append({
'time': row['time'],
'team': row['team'],
'player': row['player'],
'xG': row['xG'],
'decision': decision,
'hypothetical_score': f"{hypothetical_home}:{hypothetical_away}",
'actual_score': f"{actual_home}:{actual_away}"
})
return pd.DataFrame(results)
# 执行分析
analysis_df = calculate_decider(df)
# 4. 输出结果
print("=== 射门决定性分析 ===")
print(analysis_df.to_string(index=False))
# 5. 最终排名:得分最高的决定力
# 简单加权:逆转>绝杀>扳平
ranking_weight = {
'逆转球 (Comeback Goal)': 3,
'绝杀/制胜球 (Winning Goal)': 2,
'扳平球 (Equalizer)': 1,
'锦上添花 (Insurance Goal)': 0,
}
analysis_df['score'] = analysis_df['decision'].map(ranking_weight)
print("\n=== 最具决定性射门 TOP 1 ===")
most_decisive = analysis_df.loc[analysis_df['score'].idxmax()]
print(f"第{most_decisive['time']}分钟,{most_decisive['player']}的进球被评为最具决定性!")
print(f"类型:{most_decisive['decision']}")
print(f"实际比分:{most_decisive['actual_score']},若无此球假设比分:{most_decisive['hypothetical_score']}")
输出结果演示
=== 射门决定性分析 === time team player xG decision hypothetical_score actual_score 34 Liverpool B 0.45 扳平球 (Equalizer) 0:1 0:1 55 Man City D 0.30 扳平球 (Equalizer) 0:1 1:1 89 Man City F 0.75 绝杀/制胜球 (Winning Goal) 1:1 2:1 === 最具决定性射门 TOP 1 === 第89分钟,F的进球被评为最具决定性! 类型:绝杀/制胜球 (Winning Goal) 实际比分:2:1,若无此球假设比分:1:1
深度复盘(进阶版)
如果有了 xG 和 实时赢球概率模型,我们可以算出每个进球前后的 win probability added (WPA - 获胜概率增加值)。
示例扩展代码(伪代码逻辑):
# 假设我们有第89分钟前后的赢球概率 # 进球前(1:1):曼城胜率 35%,平局 30%,输 35% # 进球后(2:1):曼城胜率 95%,平局 4%,输 1% WPA = 0.95 - 0.35 # = 0.60(增加了60%的胜率) # 这个值越高,说明这个球越“决定性”。 # 绝杀球的WPA会远大于开场的第一个进球。
在这个案例中,89分钟曼城球员F的绝杀球被评为最具决定性,因为:
- 时间点:发生在比赛尾声,没有给对手留下扳平时间。
- 比分变化:从平局变成领先,直接改变了赛果(1分变3分)。
- xG较高:0.75的射门得分,说明这是通过高质量机会完成的,不是运气球。
建议: 在实际分析中,建议结合 “赛果变化度”(即3分变1分、1分变0分)和 “时间权重”(越晚越关键)来综合评分。
如果你有具体的比赛数据(比如从 StatsBomb 或 Understat 爬取的数据),可以发给我,我帮你跑一个完整的分析脚本。