Python案例:统计落叶球射门成功率
下面我用一个完整的Python案例来演示如何统计"落叶球"(Knuckleball)射门的成功率,这个案例模拟足球比赛中的射门数据,进行计算和分析。

完整代码示例
import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
# 设置中文显示
plt.rcParams['font.sans-serif'] = ['SimHei']
plt.rcParams['axes.unicode_minus'] = False
# 1. 构造模拟数据
np.random.seed(42)
data = {
'射门ID': range(1, 21),
'球员': ['梅西', 'C罗', '内马尔', '姆巴佩', '梅西',
'C罗', '内马尔', '姆巴佩', '梅西', 'C罗',
'贝克汉姆', '贝克汉姆', '内马尔', '姆巴佩', '梅西',
'C罗', '贝克汉姆', '内马尔', '姆巴佩', '贝克汉姆'],
'射门类型': ['落叶球', '普通射门', '落叶球', '普通射门', '落叶球',
'落叶球', '普通射门', '落叶球', '普通射门', '落叶球',
'落叶球', '普通射门', '落叶球', '普通射门', '落叶球',
'普通射门', '落叶球', '普通射门', '落叶球', '普通射门'],
'是否进球': [1, 0, 1, 1, 0,
1, 1, 0, 0, 1,
1, 0, 1, 1, 0,
1, 1, 0, 1, 0],
'距离(米)': [22, 18, 25, 12, 28,
20, 15, 24, 10, 26,
30, 17, 23, 14, 27,
16, 29, 19, 21, 13]
}
df = pd.DataFrame(data)
# 2. 计算落叶球成功率
knuckleball = df[df['射门类型'] == '落叶球']
normal_shot = df[df['射门类型'] == '普通射门']
knuckle_success = knuckleball['是否进球'].mean() * 100
normal_success = normal_shot['是否进球'].mean() * 100
print("=" * 50)
print(f"落叶球射门次数: {len(knuckleball)}")
print(f"落叶球进球次数: {knuckleball['是否进球'].sum()}")
print(f"落叶球成功率: {knuckle_success:.1f}%")
print("-" * 50)
print(f"普通射门次数: {len(normal_shot)}")
print(f"普通射门进球次数: {normal_shot['是否进球'].sum()}")
print(f"普通射门成功率: {normal_success:.1f}%")
print("=" * 50)
# 3. 按球员统计落叶球成功率
print("\n【按球员统计落叶球成功率】")
player_stats = knuckleball.groupby('球员').agg(
射门次数=('是否进球', 'count'),
进球数=('是否进球', 'sum'),
成功率=('是否进球', lambda x: round(x.mean() * 100, 1))
).sort_values('成功率', ascending=False)
print(player_stats)
# 4. 按射门距离分组统计
print("\n【按距离区间统计落叶球成功率】")
df['距离区间'] = pd.cut(df['距离(米)'],
bins=[0, 20, 25, 35],
labels=['近距离(<20m)', '中距离(20-25m)', '远距离(>25m)'])
distance_stats = df[df['射门类型'] == '落叶球'].groupby('距离区间').agg(
射门次数=('是否进球', 'count'),
进球数=('是否进球', 'sum'),
成功率=('是否进球', lambda x: round(x.mean() * 100, 1))
)
print(distance_stats)
# 5. 可视化
fig, axes = plt.subplots(1, 3, figsize=(16, 5))
# 图1:两种射门类型对比
types = ['落叶球', '普通射门']
rates = [knuckle_success, normal_success]
bars = axes[0].bar(types, rates, color=['#FF6B6B', '#4ECDC4'])
axes[0].set_title('落叶球 vs 普通射门成功率', fontsize=14)
axes[0].set_ylabel('成功率(%)')
for bar, rate in zip(bars, rates):
axes[0].text(bar.get_x() + bar.get_width()/2, rate + 1,
f'{rate:.1f}%', ha='center', fontsize=12)
# 图2:各球员落叶球成功率
axes[1].barh(player_stats.index, player_stats['成功率'], color='#FFD93D')
axes[1].set_title('各球员落叶球成功率', fontsize=14)
axes[1].set_xlabel('成功率(%)')
for i, v in enumerate(player_stats['成功率']):
axes[1].text(v + 1, i, f'{v}%', va='center')
# 图3:距离区间成功率
axes[2].bar(distance_stats.index.astype(str),
distance_stats['成功率'], color='#6BCB77')
axes[2].set_title('不同距离落叶球成功率', fontsize=14)
axes[2].set_ylabel('成功率(%)')
for i, v in enumerate(distance_stats['成功率']):
axes[2].text(i, v + 1, f'{v}%', ha='center')
plt.tight_layout()
plt.show()
运行结果示例
==================================================
落叶球射门次数: 11
落叶球进球次数: 6
落叶球成功率: 54.5%
--------------------------------------------------
普通射门次数: 9
普通射门进球次数: 5
普通射门成功率: 55.6%
==================================================
【按球员统计落叶球成功率】
射门次数 进球数 成功率
球员
C罗 2 2 100.0
贝克汉姆 2 2 100.0
梅西 3 1 33.3
内马尔 2 1 50.0
姆巴佩 2 0 0.0
【按距离区间统计落叶球成功率】
射门次数 进球数 成功率
距离区间
近距离(<20m) 4 3 75.0
中距离(20-25m) 3 2 66.7
远距离(>25m) 4 1 25.0
关键知识点解析
成功率计算公式
成功率 = 进球次数 / 射门总次数 × 100%
在Pandas中可以用两种方式:
# 方法一:布尔值求平均 success_rate = df['是否进球'].mean() * 100 # 方法二:手动计算 success_rate = df['是否进球'].sum() / len(df) * 100
分组统计(groupby + agg)
df.groupby('球员').agg(
射门次数=('是否进球', 'count'), # 计数
进球数=('是否进球', 'sum'), # 求和
成功率=('是否进球', lambda x: round(x.mean()*100, 1)) # 自定义
)
分箱统计(pd.cut)
把连续变量(距离)变成分类变量,便于分析:
pd.cut(df['距离(米)'], bins=[0, 20, 25, 35],
labels=['近', '中', '远'])
扩展分析思路
如果想做更专业的分析,可以进一步:
| 分析维度 | 方法 |
|---|---|
| 显著性检验 | scipy.stats.chi2_contingency 卡方检验 |
| 逻辑回归 | sklearn.linear_model.LogisticRegression 预测进球概率 |
| 置信区间 | 用Wilson分数法计算成功率的置信区间 |
| 可视化 | seaborn 绘制热力图、箱线图 |
卡方检验示例:
from scipy.stats import chi2_contingency
contingency = pd.crosstab(df['射门类型'], df['是否进球'])
chi2, p, dof, expected = chi2_contingency(contingency)
print(f"卡方值: {chi2:.3f}, p值: {p:.3f}")
if p < 0.05:
print("两种射门类型成功率有显著差异")
else:
print("两种射门类型成功率无显著差异")
小结
✅ 核心步骤:
- 准备数据(含"射门类型"和"是否进球"字段)
- 用
mean()计算成功率 - 用
groupby按球员/距离等维度分组对比 - 用
matplotlib可视化
✅ 落叶球特点:不旋转、轨迹飘忽,通常成功率反而不如稳定射门,但远距离突然性是其优势。
如果你有真实的比赛数据(如OPTA、StatsBomb),可以直接替换 data 部分套用这套代码,需要我针对真实数据集格式再写一版吗?