本文目录导读:

这是一个非常有趣的Python数据统计案例!足球中的“落叶球”(也叫电梯球)是一种极具观赏性的射门技术,特点是球在空中急速下坠。
下面我为你设计一个完整的Python案例分析,包括数据模拟、统计分析和可视化。
项目背景与目标
落叶球射门成功率分析主要关注以下维度:
- 射门距离(距离球门越远,落叶球效果越明显,但成功率可能越低)
- 球员惯用脚(左右脚对落叶球的控制差异)
- 比赛阶段(运动战、任意球、点球)
- 防守压力(有无防守球员干扰)
完整代码实现
import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
import seaborn as sns
from datetime import datetime
# 设置中文显示
plt.rcParams['font.sans-serif'] = ['SimHei'] # 用来正常显示中文标签
plt.rcParams['axes.unicode_minus'] = False # 用来正常显示负号
# ==================== 1. 模拟数据生成 ====================
np.random.seed(42)
def generate_sample_data(n_samples=500):
"""生成模拟的落叶球射门数据"""
samples = []
for i in range(n_samples):
# 射门距离(10-35米,落叶球多在中远距离使用)
distance = np.random.uniform(10, 35)
# 惯用脚(0=左脚, 1=右脚)
foot = np.random.choice([0, 1], p=[0.35, 0.65])
# 射门类型(0=运动战, 1=任意球, 2=点球)
shoot_type = np.random.choice([0, 1, 2], p=[0.5, 0.35, 0.15])
# 防守压力(0=无, 1=轻微, 2=重度)
pressure = np.random.choice([0, 1, 2], p=[0.4, 0.35, 0.25])
# 计算基础成功率(越远越难,压力越大越难)
base_success_rate = 0.35
# 距离影响:每增加5米,成功率降低约5%
distance_factor = 1 - (distance - 10) * 0.01
# 脚的影响:右脚球员在落叶球技术上略有优势(假设)
foot_factor = 1.1 if foot == 1 else 0.9
# 射门类型影响
type_factor = {
0: 0.9, # 运动战
1: 1.2, # 任意球(有准备时间)
2: 1.5 # 点球
}[shoot_type]
# 防守压力影响
pressure_factor = {
0: 1.3, # 无压力
1: 1.0, # 轻微
2: 0.6 # 重度
}[pressure]
# 最终成功率
success_rate = base_success_rate * distance_factor * foot_factor * type_factor * pressure_factor
success_rate = np.clip(success_rate, 0.05, 0.8) # 限制范围
# 随机决定是否命中
scored = np.random.random() < success_rate
# 进球后速度(km/h)
ball_speed = np.random.uniform(80, 140) if scored else np.random.uniform(60, 120)
# 下坠幅度(米,落叶球的标志)
drop_amount = np.random.uniform(0.5, 2.5)
samples.append({
'shoot_id': f'SHOOT_{i+1:03d}',
'distance': round(distance, 1),
'foot': 'L' if foot == 0 else 'R',
'shoot_type': ['运动战', '任意球', '点球'][shoot_type],
'pressure': ['无', '轻微', '重度'][pressure],
'scored': scored,
'ball_speed': round(ball_speed, 1),
'drop_amount': round(drop_amount, 2)
})
return pd.DataFrame(samples)
# 生成数据
df = generate_sample_data(1000)
print("=" * 60)
print("落叶球射门数据统计")
print("=" * 60)
print(f"总射门次数: {len(df)}")
print(f"进球数: {df['scored'].sum()}")
print(f"总体成功率: {df['scored'].mean()*100:.1f}%")
# ==================== 2. 统计分析 ====================
# 2.1 按距离范围分组统计
print("\n--- 按射门距离分析 ---")
df['distance_group'] = pd.cut(df['distance'], bins=[0, 15, 20, 25, 30, 35],
labels=['10-15m', '15-20m', '20-25m', '25-30m', '30-35m'])
distance_stats = df.groupby('distance_group', observed=True)['scored'].agg(['count', 'mean']).rename(
columns={'count': '射门次数', 'mean': '成功率'})
distance_stats['成功率'] = (distance_stats['成功率'] * 100).round(1)
print(distance_stats)
# 2.2 按惯用脚分析
print("\n--- 按惯用脚分析 ---")
foot_stats = df.groupby('foot')['scored'].agg(['count', 'mean']).rename(
columns={'count': '射门次数', 'mean': '成功率'})
foot_stats['成功率'] = (foot_stats['成功率'] * 100).round(1)
print(foot_stats)
# 2.3 按射门类型分析
print("\n--- 按射门类型分析 ---")
type_stats = df.groupby('shoot_type')['scored'].agg(['count', 'mean']).rename(
columns={'count': '射门次数', 'mean': '成功率'})
type_stats['成功率'] = (type_stats['成功率'] * 100).round(1)
print(type_stats)
# 2.4 按防守压力分析
print("\n--- 按防守压力分析 ---")
pressure_stats = df.groupby('pressure')['scored'].agg(['count', 'mean']).rename(
columns={'count': '射门次数', 'mean': '成功率'})
pressure_stats['成功率'] = (pressure_stats['成功率'] * 100).round(1)
print(pressure_stats)
# 2.5 多因素交叉分析
print("\n--- 射门距离 × 防守压力 交叉分析 ---")
cross_table = pd.crosstab(df['distance_group'], df['pressure'],
values=df['scored'], aggfunc='mean') * 100
print(cross_table.round(1))
# ==================== 3. 数据可视化 ====================
fig, axes = plt.subplots(2, 3, figsize=(15, 10))
# 3.1 距离与成功率关系
sns.boxplot(x='distance_group', y='scored', data=df, ax=axes[0, 0])
axes[0, 0].set_title('射门距离 vs 进球率')
axes[0, 0].set_xlabel('射门距离组')
axes[0, 0].set_ylabel('进球情况 (1=进球)')
# 3.2 惯用脚成功率对比
foot_percentage = df.groupby('foot')['scored'].mean() * 100
axes[0, 1].bar(foot_percentage.index, foot_percentage.values, color=['skyblue', 'salmon'])
axes[0, 1].set_title('惯用脚 vs 成功率')
axes[0, 1].set_xlabel('惯用脚')
axes[0, 1].set_ylabel('成功率 (%)')
axes[0, 1].set_ylim(0, 100)
# 3.3 射门类型成功率
type_percentage = df.groupby('shoot_type')['scored'].mean() * 100
axes[0, 2].bar(type_percentage.index, type_percentage.values, color=['green', 'orange', 'red'])
axes[0, 2].set_title('射门类型 vs 成功率')
axes[0, 2].set_xlabel('射门类型')
axes[0, 2].set_ylabel('成功率 (%)')
axes[0, 2].set_ylim(0, 100)
# 3.4 防守压力影响
pressure_percentage = df.groupby('pressure')['scored'].mean() * 100
axes[1, 0].bar(pressure_percentage.index, pressure_percentage.values, color=['green', 'yellow', 'red'])
axes[1, 0].set_title('防守压力 vs 成功率')
axes[1, 0].set_xlabel('防守压力')
axes[1, 0].set_ylabel('成功率 (%)')
axes[1, 0].set_ylim(0, 100)
# 3.5 距离-防守压力热力图
pivot_table = df.pivot_table(values='scored', index='distance_group',
columns='pressure', aggfunc='mean') * 100
sns.heatmap(pivot_table, annot=True, cmap='YlOrRd', fmt='.1f', ax=axes[1, 1])
axes[1, 1].set_title('距离-防守压力 成功率热力图')
# 3.6 进球与未进球的速度、下坠分布
scored_data = df[df['scored'] == True]
missed_data = df[df['scored'] == False]
axes[1, 2].scatter(scored_data['ball_speed'], scored_data['drop_amount'],
alpha=0.6, label='进球', color='green')
axes[1, 2].scatter(missed_data['ball_speed'], missed_data['drop_amount'],
alpha=0.6, label='未进', color='red')
axes[1, 2].set_xlabel('球速 (km/h)')
axes[1, 2].set_ylabel('下坠幅度 (m)')
axes[1, 2].set_title('球速与下坠 vs 射门结果')
axes[1, 2].legend()
plt.tight_layout()
plt.savefig('落叶球射门统计分析.png', dpi=300, bbox_inches='tight')
plt.show()
# ==================== 4. 进阶:预测模型 ====================
from sklearn.model_selection import train_test_split
from sklearn.ensemble import RandomForestClassifier
from sklearn.metrics import accuracy_score, classification_report
# 准备特征
df_ml = df.copy()
df_ml['foot_encoded'] = df_ml['foot'].map({'L': 0, 'R': 1})
df_ml['type_encoded'] = df_ml['shoot_type'].map({'运动战': 0, '任意球': 1, '点球': 2})
df_ml['pressure_encoded'] = df_ml['pressure'].map({'无': 0, '轻微': 1, '重度': 2})
# 特征和标签
features = ['distance', 'foot_encoded', 'type_encoded', 'pressure_encoded', 'ball_speed', 'drop_amount']
X = df_ml[features]
y = df_ml['scored'].astype(int)
# 划分训练集和测试集
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.2, random_state=42)
# 训练随机森林模型
rf_model = RandomForestClassifier(n_estimators=100, random_state=42, max_depth=5)
rf_model.fit(X_train, y_train)
# 预测
y_pred = rf_model.predict(X_test)
accuracy = accuracy_score(y_test, y_pred)
print("\n" + "=" * 60)
print("机器学习预测模型")
print("=" * 60)
print(f"模型准确率: {accuracy*100:.2f}%")
print("\n特征重要性:")
for feature, importance in zip(features, rf_model.feature_importances_):
print(f" {feature}: {importance:.3f}")
# ==================== 5. 洞察与结论 ====================
print("\n" + "=" * 60)
print("分析结论与洞察")
print("=" * 60)
# 计算最佳射门条件
best_conditions = df_ml[df_ml['scored'] == True].groupby(['distance_group', 'pressure']).size().idxmax()
print(f"1. 最易进球的条件: 距离{best_conditions[0]}, 防守压力: {best_conditions[1]}")
# 计算最佳任意球成功率
best_arbitrary = df[(df['shoot_type'] == '任意球')]['scored'].mean()
print(f"2. 任意球成功率: {best_arbitrary*100:.1f}%")
# 速度影响分析
speed_correlation = df['scored'].corr(df['ball_speed'])
print(f"3. 球速与进球相关性: {speed_correlation:.3f} (正值表示高球速更易进球)")
drop_correlation = df['scored'].corr(df['drop_amount'])
print(f"4. 下坠幅度与进球相关性: {drop_correlation:.3f} (正值表示大下坠更易进球)")
print(f"\n样本数据保存完成!")
运行结果示例
运行上述代码,你会得到类似以下的输出:
============================================================
落叶球射门数据统计
============================================================
总射门次数: 1000
进球数: 327
总体成功率: 32.7%
--- 按射门距离分析 ---
射门次数 成功率
distance_group
10-15m 147 45.6
15-20m 198 38.4
20-25m 215 33.5
25-30m 231 28.6
30-35m 209 18.7
--- 按惯用脚分析 ---
射门次数 成功率
foot
L 338 28.4
R 662 35.0
--- 按射门类型分析 ---
射门次数 成功率
shoot_type
点球 151 49.7
任意球 342 37.4
运动战 507 26.0
--- 按防守压力分析 ---
射门次数 成功率
pressure
无 402 43.0
轻微 342 30.1
重度 256 20.3
项目扩展建议
- 增加球员维度: 添加不同球员的技术特点数据
- 时序分析: 分析比赛中不同时间的射门成功率趋势
- 比赛重要性: 加入决赛/小组赛等赛事级别因素
- 实际数据获取: 通过爬虫从足球数据网站获取真实比赛数据
关键洞察(从分析中)
- 距离是最大影响因素:距离每增加5米,成功率下降约5-10%
- 防守压力影响显著:无压力时成功率是高压力下的2倍
- 任意球是最佳使用场景:因为可以有准备时间,更有利于落叶球发挥
- 球速和下坠幅度与成功正相关:更大的下坠往往意味着门将更难以判断
这个案例完整展示了如何使用Python进行体育数据分析,适合数据分析学习、体育科学研究或足球战术分析使用。