本文目录导读:

数据准备
1 需要的字段
| 字段 | 说明 |
|---|---|
| player_id | 球员唯一标识 |
| player_name | 球员姓名 |
| club | 俱乐部 |
| country | 国家队 |
| match_date | 比赛日期 |
| competition | 赛事类型(联赛/杯赛/国家队) |
| minutes_played | 出场分钟 |
| travel_distance_km | 飞行距离(可选) |
| days_rest | 距上场比赛天数 |
| rating | 赛后评分 |
| goals/assists | 进球/助攻 |
| injury_flag | 是否受伤 |
2 数据来源建议
- Transfermarkt:伤病、出场记录
- FBref / Understat:进阶数据(xG、xA、评分)
- Football-Lineups:首发阵容
- API-Football:赛程与国家队征召
分析框架(Python 脚本)
import pandas as pd
import numpy as np
from scipy import stats
import matplotlib.pyplot as plt
import seaborn as sns
# ========== 1. 读取数据 ==========
df = pd.read_csv("player_matches.csv", parse_dates=["match_date"])
# ========== 2. 标记国家队比赛周 ==========
# FIFA国际比赛日通常为3月、6月、9月、10月、11月
fifa_windows = [
("2024-03-18", "2024-03-26"),
("2024-06-03", "2024-06-11"),
("2024-09-02", "2024-09-10"),
("2024-10-07", "2024-10-15"),
("2024-11-11", "2024-11-19"),
]
def is_fifa_window(date):
for start, end in fifa_windows:
if pd.Timestamp(start) <= date <= pd.Timestamp(end):
return True
return False
df["is_fifa_window"] = df["match_date"].apply(is_fifa_window)
# ========== 3. 标记"后遗症期" ==========
# 定义:国家队比赛周结束后 0-14 天内的俱乐部比赛
def days_since_fifa(date):
for start, end in fifa_windows:
end_dt = pd.Timestamp(end)
if date > end_dt:
delta = (date - end_dt).days
if delta <= 14:
return delta
return np.nan
df["days_post_fifa"] = df["match_date"].apply(days_since_fifa)
df["post_fifa"] = df["days_post_fifa"].notna()
# ========== 4. 分组对比 ==========
# 只保留俱乐部比赛
club_df = df[df["competition"] != "国家队"].copy()
# 分组:后遗症期 vs 正常期
group_post = club_df[club_df["post_fifa"]]
group_normal = club_df[~club_df["post_fifa"]]
# ========== 5. 核心指标对比 ==========
metrics = ["rating", "goals", "assists", "minutes_played"]
results = []
for m in metrics:
t_stat, p_val = stats.ttest_ind(
group_post[m].dropna(), group_normal[m].dropna(), equal_var=False
)
results.append({
"指标": m,
"后遗症期均值": group_post[m].mean(),
"正常期均值": group_normal[m].mean(),
"差异": group_post[m].mean() - group_normal[m].mean(),
"p值": p_val,
"显著性": "***" if p_val < 0.01 else "**" if p_val < 0.05 else "*" if p_val < 0.1 else "ns"
})
result_df = pd.DataFrame(results)
print(result_df)
# ========== 6. 伤病风险分析 ==========
injury_rate_post = group_post["injury_flag"].mean()
injury_rate_normal = group_normal["injury_flag"].mean()
print(f"\n后遗症期伤病率: {injury_rate_post:.2%}")
print(f"正常期伤病率: {injury_rate_normal:.2%}")
print(f"相对风险 (RR): {injury_rate_post / injury_rate_normal:.2f}")
# ========== 7. 按飞行距离分层 ==========
if "travel_distance_km" in club_df.columns:
club_df["travel_bin"] = pd.cut(
club_df["travel_distance_km"],
bins=[0, 2000, 5000, 10000, 20000],
labels=["短途", "中途", "长途", "超长途"]
)
travel_analysis = club_df.groupby("travel_bin")["rating"].agg(["mean", "count"])
print("\n按飞行距离的评分:")
print(travel_analysis)
# ========== 8. 可视化 ==========
fig, axes = plt.subplots(1, 3, figsize=(18, 5))
# 评分分布
sns.boxplot(data=club_df, x="post_fifa", y="rating", ax=axes[0])
axes[0].set_title("后遗症期 vs 正常期评分分布")
axes[0].set_xticklabels(["正常期", "后遗症期"])
# 按天数衰减
daily = club_df[club_df["post_fifa"]].groupby("days_post_fifa")["rating"].mean()
axes[1].plot(daily.index, daily.values, marker="o")
axes[1].set_title("国家队比赛后天数 vs 评分")
axes[1].set_xlabel("距国家队比赛结束天数")
axes[1].set_ylabel("平均评分")
# 伤病率对比
axes[2].bar(["正常期", "后遗症期"], [injury_rate_normal, injury_rate_post], color=["steelblue", "crimson"])
axes[2].set_title("伤病率对比")
axes[2].set_ylabel("伤病率")
plt.tight_layout()
plt.savefig("fifa_virus_analysis.png", dpi=150)
plt.show()
进阶分析维度
1 球员分层
# 按年龄分层
club_df["age_group"] = pd.cut(club_df["age"], bins=[0, 23, 28, 32, 40],
labels=["U23", "24-28", "29-32", "33+"])
age_analysis = club_df.groupby(["age_group", "post_fifa"])["rating"].mean().unstack()
print(age_analysis)
# 按出场时间分层
club_df["minutes_load"] = pd.cut(club_df["minutes_played"],
bins=[0, 45, 90, 180],
labels=["低", "中", "高"])
2 俱乐部影响
# 哪些俱乐部受影响最大
club_impact = club_df.groupby(["club", "post_fifa"])["rating"].mean().unstack()
club_impact["diff"] = club_impact[True] - club_impact[False]
club_impact = club_impact.sort_values("diff")
print(club_impact.head(10)) # 受影响最大的
3 因果推断(DID 双重差分)
# 如果有对照组(无国脚的俱乐部),可用DID
# treated: 有国脚回归的球员
# control: 无国脚的球员
# time: 后遗症期 vs 正常期
import statsmodels.formula.api as smf
model = smf.ols("rating ~ post_fifa * treated + C(player_id) + C(match_date)",
data=club_df).fit()
print(model.summary())
结论输出模板
def generate_report(result_df, injury_rate_post, injury_rate_normal):
report = f"""
╔══════════════════════════════════════════════╗
║ 国家队比赛日后遗症分析报告 ║
╠══════════════════════════════════════════════╣
║ 1. 评分变化: {result_df.loc[0, '差异']:+.3f} (p={result_df.loc[0, 'p值']:.3f})
║ 2. 进球变化: {result_df.loc[1, '差异']:+.3f}
║ 3. 伤病率: {injury_rate_normal:.1%} → {injury_rate_post:.1%}
║ 4. 相对风险: {injury_rate_post/injury_rate_normal:.2f}x
╚══════════════════════════════════════════════╝
"""
return report
print(generate_report(result_df, injury_rate_post, injury_rate_normal))
实用建议
| 角色 | 行动 |
|---|---|
| 教练 | 后遗症期前2场考虑轮换,尤其是长途飞行球员 |
| 体能教练 | 监测CK值、睡眠质量,制定恢复计划 |
| 分析师 | 用xG链、跑动数据补充评分,避免单一指标偏差 |
| 投注者 | 关注"国脚大户"球队在后遗症期的让球盘口 |
| FM/游戏玩家 | 国际比赛日后避免让核心球员首发 |
注意事项
- 样本量:单个国际比赛日窗口样本有限,建议跨赛季合并
- 混杂因素:对手强度、主客场、赛程密度需控制
- 选择偏差:能踢国家队的球员本身实力更强,需用DID或匹配法
- 定义敏感:”后遗症期“天数(7天/14天)会影响结果,建议做敏感性分析
- 位置差异:门将受影响小,边锋/中场受影响大
如果你有具体的数据集(CSV/Excel),我可以帮你直接跑这个脚本并解读结果,也可以针对某个联赛(英超/中超)或某支球队做定制分析,需要我展开哪一部分?