python案例如何量化主队球迷人数影响?

wen python案例 3

Python案例:量化主队球迷人数对比赛结果的影响

下面用一个完整的案例,演示如何用Python量化"主队球迷人数"对比赛结果(如主队胜率、净胜球)的影响。

python案例如何量化主队球迷人数影响?

问题定义

研究问题:主队球迷人数是否影响主队胜率/净胜球?

关键挑战

  1. 球迷人数与球队实力相关(强队吸引更多球迷)→ 需要控制混淆变量
  2. 球迷人数本身是内生的 → 可用工具变量(如球场容量、天气)

数据构造(模拟示例)

import numpy as np
import pandas as pd
import statsmodels.api as sm
import statsmodels.formula.api as smf
import matplotlib.pyplot as plt
import seaborn as sns
np.random.seed(42)
n = 1000
# 模拟:球队实力(1-10)
team_strength = np.random.uniform(1, 10, n)
away_strength = np.random.uniform(1, 10, n)
# 球迷人数:受球队实力 + 对手吸引力 + 随机因素影响
attendance = (20000 + 3000 * team_strength 
              + 1500 * away_strength 
              + np.random.normal(0, 4000, n)).clip(5000, 80000)
# 净胜球:受双方实力差 + 球迷人数(主场优势)影响
goal_diff = (0.3 * (team_strength - away_strength) 
             + 0.00002 * attendance          # 球迷效应
             + np.random.normal(0, 1, n))
df = pd.DataFrame({
    'attendance': attendance,
    'team_strength': team_strength,
    'away_strength': away_strength,
    'goal_diff': goal_diff
})
df['win'] = (df['goal_diff'] > 0).astype(int)
df.head()

基础分析

相关性可视化

fig, axes = plt.subplots(1, 2, figsize=(12, 4))
sns.regplot(data=df, x='attendance', y='goal_diff', ax=axes[0], 
            scatter_kws={'alpha':0.3})
axes[0].set_title('球迷人数 vs 净胜球')
sns.boxplot(data=df, x='win', y='attendance', ax=axes[1])
axes[1].set_title('胜/负场次的球迷人数分布')
plt.tight_layout()
plt.show()

简单回归(有偏)

model_simple = smf.ols('goal_diff ~ attendance', data=df).fit()
print(model_simple.summary().tables[1])

⚠️ 这个系数是有偏的,因为没控制球队实力。

控制混淆变量

# 加入双方实力作为控制变量
model_ctrl = smf.ols(
    'goal_diff ~ attendance + team_strength + away_strength', 
    data=df
).fit()
print(model_ctrl.summary().tables[1])
# 球迷人数的边际效应
print(f"每增加1名球迷,净胜球变化: {model_ctrl.params['attendance']:.8f}")
print(f"每增加1万名球迷,净胜球变化: {model_ctrl.params['attendance']*10000:.4f}")

量化"效果大小"

# 用标准化系数比较不同变量影响
from scipy import stats
df_std = df[['goal_diff','attendance','team_strength','away_strength']].apply(
    stats.zscore)
model_std = smf.ols(
    'goal_diff ~ attendance + team_strength + away_strength', 
    data=df_std
).fit()
print(model_std.params)

解释:若 attendance 标准化系数为 0.15,意味着球迷人数每增加1个标准差,净胜球增加 0.15 个标准差。

进阶:工具变量法(IV / 2SLS)

因为球迷人数是内生的(可能反过来受球队表现影响),用球场容量作为工具变量:

from linearmodels.iv import IV2SLS
# 假设球场容量是外生的工具变量
df['stadium_capacity'] = (df['attendance'] + 
                          np.random.normal(0, 3000, n)).clip(10000, 90000)
iv_model = IV2SLS.from_formula(
    'goal_diff ~ 1 + team_strength + away_strength + [attendance ~ stadium_capacity]',
    data=df
).fit()
print(iv_model.summary)

非线性/异质性分析

# 分位数回归:看球迷人数对不同水平比赛的影响
import statsmodels.quantreg as qr
for q in [0.25, 0.5, 0.75]:
    mod = qr.QuantReg('goal_diff ~ attendance + team_strength + away_strength', 
                      data=df).fit(q=q)
    print(f"Q{q}: attendance coef = {mod.params['attendance']:.8f}")
# 分组:强弱队球迷效应是否不同?
df['strong_team'] = (df['team_strength'] > df['team_strength'].median()).astype(int)
for g in [0, 1]:
    sub = df[df['strong_team'] == g]
    m = smf.ols('goal_diff ~ attendance + away_strength', data=sub).fit()
    print(f"强队={g}: 球迷系数={m.params['attendance']:.8f}, p={m.pvalues['attendance']:.4f}")

结果汇总表

def report(model, name):
    return {
        '模型': name,
        'attendance系数': model.params['attendance'],
        '标准误': model.bse['attendance'],
        'p值': model.pvalues['attendance'],
        'R²': model.rsquared
    }
results = pd.DataFrame([
    report(model_simple, '仅球迷人数'),
    report(model_ctrl, '控制球队实力'),
])
print(results)

关键结论与注意事项

事项 说明
内生性 球迷人数与球队实力高度相关,必须控制
因果识别 建议用 IV(球场容量、天气)或断点回归
效应量级 通常很小,1万球迷 ≈ 0.05~0.2 净胜球
异质性 弱队/关键比赛的球迷效应可能更强
数据来源 Kaggle 有 European Soccer Database 可直接练习

推荐真实数据集

# Kaggle 命令
# kaggle datasets download -d hugomathien/soccer
# 用欧洲足球数据库做同样的分析
import sqlite3
conn = sqlite3.connect('database.sqlite')
matches = pd.read_sql("SELECT * FROM Match WHERE attendance IS NOT NULL", conn)

核心思路总结

  1. 📊 描述性统计 + 可视化
  2. 🔧 多元回归控制混淆变量
  3. 🎯 工具变量法处理内生性
  4. 🔍 异质性/分位数分析
  5. 📈 将系数翻译成业务可解释的效应量

需要我针对真实数据集因果推断方法(DID、RDD)展开进一步代码吗?

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