用Python分析:犯规次数会决定胜负吗?
这是一个很有趣的数据分析问题,下面我用一个完整的案例来演示如何用Python分析犯规次数与比赛胜负的关系。

问题拆解
要回答"犯规次数是否决定胜负",我们需要:
- 获取比赛数据(犯规数、得分、胜负)
- 计算犯规数与胜负的相关性
- 可视化对比
- 做统计检验
模拟数据案例
由于没有真实数据,先用模拟数据演示完整流程:
import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
import seaborn as sns
from scipy import stats
plt.rcParams['font.sans-serif'] = ['SimHei']
plt.rcParams['axes.unicode_minus'] = False
np.random.seed(42)
# 模拟100场比赛,每场记录主客队的犯规数和得分
n_games = 200
data = []
for i in range(n_games):
# 犯规数:均值20,标准差4
foul_home = np.random.normal(20, 4)
foul_away = np.random.normal(20, 4)
# 得分与犯规数有一定负相关(犯规多的队伍得分略低)
# 基础得分100 + 犯规惩罚 + 随机波动
score_home = 100 - 0.8 * (foul_home - 20) + np.random.normal(0, 8)
score_away = 100 - 0.8 * (foul_away - 20) + np.random.normal(0, 8)
winner = 'home' if score_home > score_away else 'away'
data.append({
'game_id': i,
'foul_home': foul_home,
'foul_away': foul_away,
'score_home': score_home,
'score_away': score_away,
'winner': winner
})
df = pd.DataFrame(data)
print(df.head())
核心分析
胜负与犯规数的关系
# 计算赢家和输家的犯规数
df['foul_winner'] = np.where(df['winner'] == 'home', df['foul_home'], df['foul_away'])
df['foul_loser'] = np.where(df['winner'] == 'home', df['foul_away'], df['foul_home'])
print("赢家平均犯规数: %.2f" % df['foul_winner'].mean())
print("输家平均犯规数: %.2f" % df['foul_loser'].mean())
# 配对t检验:赢家和输家的犯规数是否有显著差异
t_stat, p_value = stats.ttest_rel(df['foul_winner'], df['foul_loser'])
print(f"配对t检验: t={t_stat:.3f}, p={p_value:.4f}")
相关性分析
# 犯规数差值与分差的关系
df['foul_diff'] = df['foul_home'] - df['foul_away']
df['score_diff'] = df['score_home'] - df['score_away']
corr, p = stats.pearsonr(df['foul_diff'], df['score_diff'])
print(f"犯规差与分差的相关系数: {corr:.3f}, p值: {p:.4f}")
可视化
fig, axes = plt.subplots(1, 3, figsize=(15, 4))
# 图1:赢家 vs 输家 犯规数箱线图
df_melt = df.melt(value_vars=['foul_winner', 'foul_loser'],
var_name='组别', value_name='犯规数')
sns.boxplot(data=df_melt, x='组别', y='犯规数', ax=axes[0])
axes[0].set_title('赢家 vs 输家 犯规数对比')
# 图2:犯规差 vs 分差 散点图
axes[1].scatter(df['foul_diff'], df['score_diff'], alpha=0.5)
axes[1].axhline(0, color='gray', linestyle='--')
axes[1].axvline(0, color='gray', linestyle='--')
axes[1].set_xlabel('犯规差 (主-客)')
axes[1].set_ylabel('分差 (主-客)')
axes[1].set_title(f'犯规差 vs 分差 (r={corr:.2f})')
# 图3:犯规数分组胜率
df['foul_total_bin'] = pd.cut(df['foul_home'] - df['foul_away'],
bins=[-10, -3, 3, 10],
labels=['少犯规', '相当', '多犯规'])
win_rate = df.groupby('foul_total_bin', observed=True).apply(
lambda x: (x['winner'] == 'home').mean())
win_rate.plot(kind='bar', ax=axes[2], color='steelblue')
axes[2].set_title('主队不同犯规情况下的胜率')
axes[2].set_ylabel('主队胜率')
plt.tight_layout()
plt.show()
可能的结果解读
根据模拟数据(假设犯规确实影响得分),你会看到:
| 分析维度 | 预期结果 |
|---|---|
| 赢家平均犯规 | 通常低于输家 |
| 配对t检验 | p < 0.05,差异显著 |
| 相关系数 | 犯规差与分差负相关(犯规越多,分差越吃亏) |
| 胜率对比 | 犯规少的队伍胜率明显更高 |
要谨慎下结论,因为:
- 相关性 ≠ 因果性:犯规多可能是"被迫犯规"(落后时战术犯规),而不是犯规导致输球。
- 需要控制变量:应该考虑球队实力、球员水平等混杂因素。
- 不同运动差异大:
- 篮球:犯规影响明显(罚球、罚下)
- 足球:黄牌红牌影响很大,但普通犯规影响较小
- 冰球:犯规导致的强打机会影响显著
更严谨的做法:
# 用逻辑回归控制其他变量
from sklearn.linear_model import LogisticRegression
X = df[['foul_home', 'foul_away']]
y = (df['winner'] == 'home').astype(int)
model = LogisticRegression().fit(X, y)
print(f"犯规数对主队获胜的影响系数: {model.coef_}")
用真实数据的建议
如果你想用真实数据(如NBA、世界杯),可以:
- NBA: 从
nba_api库获取 - 足球: 从
football-data.orgAPI 获取 - Kaggle: 搜索 "sports match statistics" 数据集
# NBA示例 # pip install nba_api from nba_api.stats.endpoints import leaguegamefinder games = leaguegamefinder.LeagueGameFinder(season_nullable='2023-24').get_data_frames()[0]
一句话总结:犯规次数通常与胜负相关(犯规越多越容易输),但它往往是"结果"而不是"原因",需要结合具体运动、比赛情境和统计方法综合判断。
需要我用某个具体运动(比如NBA、足球世界杯)的真实数据做完整分析吗?