本文目录导读:

我来给你一个完整的Python案例,分析足球让球胜平负的分布情况。
数据准备
假设你有类似这样的盘口数据(CSV格式):
match_id,home_team,away_team,handicap,home_odds,draw_odds,away_odds,result 1,曼城,阿森纳,-1.0,1.85,3.60,4.20,H 2,利物浦,切尔西,-0.5,1.95,3.40,3.80,D 3,拜仁,多特,-1.5,2.10,3.80,3.20,A ...
handicap:让球数(负=主让,正=客让,0=平手)result:H=让球主胜,D=让球平,A=让球客胜
完整分析代码
import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
import seaborn as sns
# 中文字体
plt.rcParams['font.sans-serif'] = ['SimHei']
plt.rcParams['axes.unicode_minus'] = False
# 1. 读取数据
df = pd.read_csv('handicap_data.csv')
# 2. 让球盘口分类
def classify_handicap(h):
if h <= -1.5: return '主让1.5+'
elif h == -1.0: return '主让1'
elif h == -0.75: return '主让0.75'
elif h == -0.5: return '主让0.5'
elif h == -0.25: return '主让0.25'
elif h == 0: return '平手'
elif h == 0.25: return '客让0.25'
elif h == 0.5: return '客让0.5'
elif h == 0.75: return '客让0.75'
elif h == 1.0: return '客让1'
else: return '客让1.5+'
df['handicap_cat'] = df['handicap'].apply(classify_handicap)
# 3. 整体分布
print("=== 整体让球胜平负分布 ===")
overall = df['result'].value_counts(normalize=True) * 100
print(overall.round(2))
# 4. 按让球盘口分组统计
print("\n=== 各让球盘口的胜平负分布 ===")
dist = pd.crosstab(df['handicap_cat'], df['result'], normalize='index') * 100
# 调整列顺序
dist = dist.reindex(columns=['H', 'D', 'A']).round(2)
# 加样本数
dist['样本数'] = df.groupby('handicap_cat').size()
print(dist)
# 5. 可视化:堆叠柱状图
fig, axes = plt.subplots(1, 2, figsize=(16, 6))
# 左图:各盘口分布
dist_plot = dist[['H', 'D', 'A']]
dist_plot.plot(kind='bar', stacked=True, ax=axes[0],
color=['#e74c3c', '#95a5a6', '#3498db'], width=0.75)
axes[0].set_title('各让球盘口的胜平负分布', fontsize=14)
axes[0].set_xlabel('让球盘口')
axes[0].set_ylabel('占比 (%)')
axes[0].legend(['主胜', '平', '客胜'])
axes[0].tick_params(axis='x', rotation=45)
# 右图:整体饼图
axes[1].pie(overall.values, labels=['主胜', '平', '客胜'],
autopct='%1.1f%%', colors=['#e74c3c', '#95a5a6', '#3498db'])
axes[1].set_title('整体让球胜平负分布', fontsize=14)
plt.tight_layout()
plt.savefig('handicap_distribution.png', dpi=150)
plt.show()
# 6. 让球方向的宏观统计
df['direction'] = np.where(df['handicap'] < 0, '主让',
np.where(df['handicap'] > 0, '客让', '平手'))
print("\n=== 按让球方向统计 ===")
print(pd.crosstab(df['direction'], df['result'], normalize='index').round(3) * 100)
# 7. 让球数与赛果的相关性
# 将结果编码为数值:H=-1, D=0, A=1
df['result_num'] = df['result'].map({'H': -1, 'D': 0, 'A': 1})
corr = df[['handicap', 'result_num']].corr().iloc[0, 1]
print(f"\n让球数与赛果相关系数: {corr:.3f}")
# 8. 热力图展示
plt.figure(figsize=(10, 6))
sns.heatmap(dist_plot, annot=True, fmt='.1f', cmap='RdYlGn_r',
cbar_kws={'label': '占比 (%)'})'让球盘口 × 赛果 热力图', fontsize=14)
plt.tight_layout()
plt.savefig('handicap_heatmap.png', dpi=150)
plt.show()
进阶分析:赔率隐含概率 vs 实际概率
# 计算隐含概率(去水)
def implied_prob(row):
odds = [row['home_odds'], row['draw_odds'], row['away_odds']]
inv = [1/o for o in odds]
total = sum(inv)
return [i/total for i in inv]
df[['p_h', 'p_d', 'p_a']] = df.apply(
lambda r: pd.Series(implied_prob(r)), axis=1)
# 按盘口对比实际与隐含概率
compare = df.groupby('handicap_cat').agg(
实际主胜=('result', lambda x: (x=='H').mean()),
隐含主胜=('p_h', 'mean'),
实际平=('result', lambda x: (x=='D').mean()),
隐含平=('p_d', 'mean'),
实际客胜=('result', lambda x: (x=='A').mean()),
隐含客胜=('p_a', 'mean'),
样本数=('result', 'size')
).round(3)
print("=== 隐含概率 vs 实际概率 ===")
print(compare)
# 价值判断:实际 - 隐含 > 0 表示被低估
compare['主胜偏差'] = compare['实际主胜'] - compare['隐含主胜']
print("\n各盘口主胜被低估程度:")
print(compare['主胜偏差'].sort_values(ascending=False))
关键结论解读
运行后你会得到几个重要信息:
| 观察点 | 含义 |
|---|---|
| 整体分布 | 让球主胜通常约40-45%,让球平约25-30%,让球客胜约25-30% |
| 深盘(让1.5+) | 主胜概率显著升高,但赔率低 |
| 浅盘(让0.25/0.5) | 分布最接近,价值最需挖掘 |
| 受让盘 | 客胜/平比例上升 |
| 偏差分析 | 实际-隐含 > 0 的盘口存在"价值" |
实战建议
- 样本量要够:每个盘口至少50场才有统计意义
- 分联赛分析:不同联赛让球分布差异大(如德甲大球多、意甲小球多)
- 结合大小球:让球方向+总进球能提升判断精度
- 动态监控:初盘→终盘的变化本身就是信号(升降盘)
需要我针对某个具体环节(比如爬取数据、凯利公式计算、时间序列走势)深入展开吗?