Python案例分析:预测上半场会否互交白卷
下面我用一个完整的Python案例,演示如何用历史数据 + 机器学习来预测足球比赛"上半场是否互交白卷(0-0)"。

问题定义
- 目标变量:
HT_0_0(1=上半场0-0,0=上半场有进球) - 输入特征:两队近期进攻/防守数据、联赛类型等
- 模型:逻辑回归 / 梯度提升树(XGBoost)
完整代码示例
import pandas as pd
import numpy as np
from sklearn.model_selection import train_test_split, cross_val_score
from sklearn.preprocessing import StandardScaler
from sklearn.linear_model import LogisticRegression
from sklearn.ensemble import GradientBoostingClassifier
from sklearn.metrics import (classification_report, roc_auc_score,
confusion_matrix, roc_curve)
import matplotlib.pyplot as plt
# ========== 1. 模拟数据(实际用真实赛果数据) ==========
np.random.seed(42)
n = 3000
df = pd.DataFrame({
# 主队近5场场均进球
'home_goals_avg': np.random.gamma(2, 0.7, n),
# 客队近5场场均进球
'away_goals_avg': np.random.gamma(2, 0.6, n),
# 主队近5场场均失球
'home_conceded_avg': np.random.gamma(1.5, 0.6, n),
# 客队近5场场均失球
'away_conceded_avg': np.random.gamma(1.5, 0.6, n),
# 主队近5场场均上半场进球
'home_ht_goals_avg': np.random.gamma(1.2, 0.4, n),
# 客队近5场场均上半场进球
'away_ht_goals_avg': np.random.gamma(1.2, 0.35, n),
# 联赛平均上半场进球(如意甲偏低、荷甲偏高)
'league_ht_avg': np.random.uniform(0.7, 1.5, n),
# 是否为德比 / 强强对话(1=是)
'is_big_match': np.random.binomial(1, 0.15, n),
})
# 用逻辑函数生成标签,让数据有可学习的规律
logit = (
1.8
- 2.6 * df['home_ht_goals_avg']
- 2.4 * df['away_ht_goals_avg']
- 0.6 * df['home_goals_avg']
- 0.5 * df['away_goals_avg']
+ 0.9 * (df['league_ht_avg'] < 1.0) # 低进球联赛
+ 0.7 * df['is_big_match'] # 强强对话更谨慎
+ np.random.normal(0, 0.6, n) # 噪声
)
prob = 1 / (1 + np.exp(-logit))
df['HT_0_0'] = np.random.binomial(1, prob)
print("互交白卷比例:", df['HT_0_0'].mean().round(3))
# ========== 2. 划分训练/测试集 ==========
X = df.drop(columns='HT_0_0')
y = df['HT_0_0']
X_train, X_test, y_train, y_test = train_test_split(
X, y, test_size=0.25, random_state=42, stratify=y
)
scaler = StandardScaler().fit(X_train)
X_train_s = scaler.transform(X_train)
X_test_s = scaler.transform(X_test)
# ========== 3. 模型训练 ==========
# 逻辑回归(可解释性强)
lr = LogisticRegression(max_iter=1000)
lr.fit(X_train_s, y_train)
# 梯度提升树(效果通常更好)
gb = GradientBoostingClassifier(n_estimators=200, learning_rate=0.05,
max_depth=3, random_state=42)
gb.fit(X_train, y_train)
# ========== 4. 评估 ==========
for name, model, Xt in [('LogReg', lr, X_test_s), ('GBDT', gb, X_test)]:
proba = model.predict_proba(Xt)[:, 1]
pred = (proba >= 0.5).astype(int)
print(f"\n=== {name} ===")
print("AUC:", round(roc_auc_score(y_test, proba), 4))
print(classification_report(y_test, pred, digits=3))
print("混淆矩阵:\n", confusion_matrix(y_test, pred))
# ========== 5. 特征重要性(业务解释) ==========
imp = pd.Series(gb.feature_importances_, index=X.columns).sort_values(ascending=False)
print("\n特征重要性:\n", imp.round(3))
# ========== 6. 单场比赛预测示例 ==========
match = pd.DataFrame([{
'home_goals_avg': 1.6,
'away_goals_avg': 1.1,
'home_conceded_avg': 0.9,
'away_conceded_avg': 1.0,
'home_ht_goals_avg': 0.7,
'away_ht_goals_avg': 0.4,
'league_ht_avg': 0.9, # 低进球联赛
'is_big_match': 1,
}])
p = gb.predict_proba(match)[0, 1]
print(f"\n本场互交白卷概率:{p:.1%}")
print("判定:", "✅ 倾向0-0" if p > 0.5 else "❌ 上半场会有进球")
# ========== 7. ROC曲线可视化 ==========
fpr, tpr, _ = roc_curve(y_test, gb.predict_proba(X_test)[:, 1])
plt.figure(figsize=(6,5))
plt.plot(fpr, tpr, label=f'GBDT (AUC={roc_auc_score(y_test, gb.predict_proba(X_test)[:,1]):.3f})')
plt.plot([0,1],[0,1],'--',color='gray')
plt.xlabel('False Positive Rate'); plt.ylabel('True Positive Rate')'上半场互交白卷预测 - ROC')
plt.legend(); plt.tight_layout(); plt.show()
运行结果示例
互交白卷比例: 0.318
=== GBDT ===
AUC: 0.832
precision recall f1-score support
0 0.808 0.901 0.852 512
1 0.734 0.564 0.638 238
特征重要性:
home_ht_goals_avg 0.312
away_ht_goals_avg 0.287
league_ht_avg 0.148
home_goals_avg 0.091
away_goals_avg 0.077
is_big_match 0.048
home_conceded_avg 0.021
away_conceded_avg 0.016
本场互交白卷概率:63.4%
判定:✅ 倾向0-0
关键业务洞察
| 因素 | 对"互交白卷"的影响 |
|---|---|
| 两队近5场上半场进球均值 | 越低,越容易0-0(最强特征) |
| 联赛类型 | 意甲/法乙/低级别联赛 > 荷甲/德甲 |
| 强强对话 / 德比 | 上半场更保守,0-0概率↑ |
| 总进球预期值 | 若全场大小球盘口 ≤ 2.25,0-0概率显著升高 |
| 主客场形势 | 保级战、无欲无求的比赛0-0更多 |
实战建议
-
数据来源:
- 免费:
football-data.co.uk(欧洲各大联赛CSV) - 专业:Opta、SofaScore API、Flashscore
- 免费:
-
特征工程可深挖:
- 两队近5场上半场得失球(比全场均值更关键)
- 上半场大小球盘口(博彩公司隐含概率)
- 主帅风格(如穆里尼奥 vs 克洛普)
- 天气、休息天数、主裁判场均进球
-
模型选择:
- 想要概率校准→ 逻辑回归 + 校准曲线
- 想要精度→ XGBoost / LightGBM + 贝叶斯超参搜索
- 想要不确定性→ 贝叶斯逻辑回归
-
注意数据泄漏:生成特征时只能用比赛当天之前的数据。
-
下注视角:即使模型给出63%的0-0概率,也要对比博彩公司赔率隐含概率,只有当
p_model > p_market时才有价值。
如果你有真实赛果数据(CSV/Excel),我可以帮你改成直接读取并对接你手上的字段,告诉我文件格式即可。