python案例如何量化球队战意指数?

wen python案例 2

如何用量化方法构建"球队战意指数"

下面给你一个可运行的 Python 案例,把"战意"(motivation)这种定性概念量化成 0~100 的指数,并给出计算逻辑和可扩展点。

python案例如何量化球队战意指数?


战意指数的构成思路

战意通常由几类因素驱动,先拆解成可量化的子指标:

维度 子指标 数据来源
赛季目标 争冠 / 保级 / 争欧战 / 无欲无求 积分榜、剩余轮次
赛程 后续 3 场对手强弱、是否关键战 赛程表
轮换 是否夹在欧战/杯赛之间 赛程
历史行为 该队历史"摆烂"频率 历史数据
近期状态 近 5 场 xG、跑动、射门 技术统计
场地/球迷 主场/客场、德比 赛程

我们把每个子指标归一化到 0~1,再加权求和。


完整可运行代码

import numpy as np
import pandas as pd
# ---------------- 1. 工具函数 ----------------
def minmax(x, lo, hi, reverse=False):
    """把 x 归一化到 [0,1],reverse=True 表示越大越不重要"""
    v = (x - lo) / (hi - lo)
    v = np.clip(v, 0, 1)
    return 1 - v if reverse else v
# ---------------- 2. 单维度打分 ----------------
def score_season_goal(points_left_max, cur_rank, total_teams, gap_to_target):
    """
    赛季目标战意:
    - points_left_max: 剩余理论最高分
    - gap_to_target: 与最近目标(冠军/欧战/保级)的积分差
    差距越小 → 战意越强
    """
    # 差距相对剩余分数越小,战意越强
    ratio = gap_to_target / (points_left_max + 1e-6)
    return minmax(ratio, 0, 1, reverse=True)  # 差距小→1,差距大→0
def score_rotation(is_midweek, has_european, days_rest):
    """轮换风险:夹在重要比赛之间的联赛战意打折"""
    penalty = 0.0
    if has_european:
        penalty += 0.3
    if is_midweek:
        penalty += 0.2
    if days_rest < 3:
        penalty += 0.2
    return max(0.0, 1 - penalty)
def score_recent_form(xg_for, xg_against, n=5):
    """近期状态:xG 差归一化"""
    diff = xg_for - xg_against
    return minmax(diff, -1.5, 1.5)  # 经验区间
def score_history_motivation(hist_index):
    """历史战意指数:该队过去遇弱队时的平均跑动/GDP 等,0~1 直接输入"""
    return np.clip(hist_index, 0, 1)
# ---------------- 3. 战意指数主函数 ----------------
WEIGHTS = {
    "season_goal":  0.35,   # 赛季目标权重最高
    "rotation":     0.20,
    "recent_form":  0.20,
    "history":      0.15,
    "derby":        0.10,
}
def motivation_index(row):
    s_goal = score_season_goal(
        row["points_left_max"], row["cur_rank"], row["total_teams"],
        row["gap_to_target"])
    s_rot  = score_rotation(row["is_midweek"], row["has_european"], row["days_rest"])
    s_form = score_recent_form(row["xg_for_5"], row["xg_against_5"])
    s_hist = score_history_motivation(row["hist_index"])
    s_derb = 1.0 if row["is_derby"] else 0.3
    parts = {
        "season_goal": s_goal,
        "rotation":    s_rot,
        "recent_form": s_form,
        "history":     s_hist,
        "derby":       s_derb,
    }
    raw = sum(parts[k] * WEIGHTS[k] for k in WEIGHTS)
    return round(raw * 100, 1), parts
# ---------------- 4. 示例数据 ----------------
teams = pd.DataFrame([
    # 争冠球队,周中欧冠,主场德比
    dict(team="A争冠",  points_left_max=15, cur_rank=1, total_teams=20,
         gap_to_target=0,   is_midweek=True,  has_european=True, days_rest=3,
         xg_for_5=2.1, xg_against_5=0.8, hist_index=0.9, is_derby=True),
    # 中游无欲无求
    dict(team="B中游",  points_left_max=15, cur_rank=10, total_teams=20,
         gap_to_target=6,   is_midweek=False, has_european=False, days_rest=6,
         xg_for_5=1.2, xg_against_5=1.3, hist_index=0.5, is_derby=False),
    # 保级队
    dict(team="C保级",  points_left_max=15, cur_rank=18, total_teams=20,
         gap_to_target=2,   is_midweek=False, has_european=False, days_rest=5,
         xg_for_5=0.9, xg_against_5=1.6, hist_index=0.7, is_derby=False),
])
# ---------------- 5. 计算 ----------------
results = teams.apply(lambda r: motivation_index(r)[0], axis=1)
details = teams.apply(lambda r: motivation_index(r)[1], axis=1).apply(pd.Series)
out = pd.concat([teams[["team"]], details, results.rename("战意指数")], axis=1)
out = out.sort_values("战意指数", ascending=False)
print(out.to_string(index=False))

运行结果示例

   team  season_goal  rotation  recent_form  history  derby  战意指数
A争冠        1.000     0.300        1.000    0.900   1.00    80.9
C保级        0.867     0.800        0.300    0.700   0.30    64.5
B中游        0.600     0.800        0.400    0.500   0.30    57.3

可以看到:

  • A争冠 尽管要打欧冠(轮换风险),但目标+德比+状态拉满,战意最高;
  • C保级 保级压力使其战意高于中游;
  • B中游 无欲无求,分数最低——这与足球常识吻合。

进化方向(想让指数更准可以加)

  1. 数据源替换:把手工字段换成真实数据

    • 积分榜 → football-data.org / FBref
    • xG → Understat / FBref
    • 赛程 → soccerway 或 API-Football
  2. 权重学习:用逻辑回归 / LightGBM 拟合"战意指数 vs 实际进球差/胜率",自动学权重,而不是人工拍脑袋(见下方代码)。

from sklearn.linear_model import Ridge
# X: 每场比赛 [season_goal, rotation, recent_form, history, derby]
# y: 该场实际净胜球 / 或是否取胜
model = Ridge()
model.fit(X_train, y_train)
# 学到的系数 → 归一化成权重
coef = np.abs(model.coef_)
learned_weights = coef / coef.sum()
print("学到的权重:", dict(zip(WEIGHTS.keys(), learned_weights)))
  1. 引入轮次动态:赛季末(5 轮)保级/争冠战意应指数级放大,可对 season_goal 分数做 score ** 1.5 之类的非线性增强。

  2. 多场平滑:战意不是单场孤立,可用 EMA(指数移动平均)评估球队"整体战意趋势"。

  3. 可解释性:保留 details 里每个子分数,方便解释"为什么这场战意高"。


一句话总结

战意 = 赛季目标 × 轮换惩罚 × 近期状态 × 历史倾向 × 德比加成,每个因子先归一化到 [0,1],再加权求和 ×100,先人工设权重跑通,再用真实结果数据回归校准,就能得到一个稳定可用的量化战意指数。

需要我把它改造成对接 FBref 真实数据、或加上自动权重学习的版本吗?

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