我来设计一个综合Python案例,分析足球比赛中哪支球队的防线更稳固可靠。

项目:足球防守数据分析系统
数据生成与加载
import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
import seaborn as sns
from scipy import stats
import warnings
warnings.filterwarnings('ignore')
# 设置中文显示
plt.rcParams['font.sans-serif'] = ['SimHei']
plt.rcParams['axes.unicode_minus'] = False
# 生成模拟数据
np.random.seed(42)
def generate_defensive_data(team_name, num_matches=38):
"""生成球队防守数据"""
matches = []
for i in range(num_matches):
# 基础防守数据
data = {
'team': team_name,
'match': i + 1,
'goals_conceded': np.random.poisson(1.2) if team_name != 'TeamA' else np.random.poisson(0.9),
'shots_against': np.random.normal(10, 3),
'tackles': np.random.normal(20, 5),
'interceptions': np.random.normal(15, 4),
'clearances': np.random.normal(12, 3),
'blocks': np.random.normal(5, 2),
'aerial_duels_won': np.random.normal(55, 10),
'possession': np.random.normal(52, 8),
'fouls': np.random.normal(10, 3),
'yellow_cards': np.random.poisson(1.5),
'red_cards': np.random.poisson(0.1)
}
matches.append(data)
return pd.DataFrame(matches)
# 生成4支球队的数据
teams = ['TeamA', 'TeamB', 'TeamC', 'TeamD']
all_data = pd.concat([generate_defensive_data(team) for team in teams], ignore_index=True)
数据清洗与预处理
class DataCleaner:
"""数据清洗类"""
@staticmethod
def clean_data(df):
"""清洗数据"""
df_clean = df.copy()
# 处理异常值(使用IQR方法)
numeric_cols = df_clean.select_dtypes(include=[np.number]).columns
for col in numeric_cols:
Q1 = df_clean[col].quantile(0.25)
Q3 = df_clean[col].quantile(0.75)
IQR = Q3 - Q1
lower_bound = Q1 - 1.5 * IQR
upper_bound = Q3 + 1.5 * IQR
# 用边界值替换异常值
df_clean[col] = df_clean[col].clip(lower_bound, upper_bound)
# 处理缺失值
df_clean = df_clean.fillna(df_clean.median())
return df_clean
@staticmethod
def add_derived_features(df):
"""添加衍生特征"""
df = df.copy()
# 防守效率指数(综合考虑多个因素)
df['defense_efficiency'] = (
df['goals_conceded'].rank(method='average') * 0.3 +
(100 - df['shots_against'].rank(method='average')) * 0.2 +
df['tackles'].rank(method='average') * 0.15 +
df['interceptions'].rank(method='average') * 0.15 +
df['clearances'].rank(method='average') * 0.1 +
df['aerial_duels_won'].rank(method='average') * 0.1
)
# 防守强度评分
df['defense_strength'] = (
(df['goals_conceded'].mean() / (df['goals_conceded'] + 1)) * 100 +
(df['tackles'] + df['interceptions']) * 0.1 +
df['aerial_duels_won'] * 0.05
)
return df
# 清洗数据
cleaner = DataCleaner()
clean_data = cleaner.clean_data(all_data)
clean_data = cleaner.add_derived_features(clean_data)
数据分析与计算
class DefensiveAnalyzer:
"""防守分析器"""
def __init__(self, df):
self.df = df
self.teams = df['team'].unique()
def basic_stats(self):
"""基础统计分析"""
stats_df = []
for team in self.teams:
team_data = self.df[self.df['team'] == team]
stats = {
'team': team,
'avg_goals_conceded': team_data['goals_conceded'].mean(),
'goals_std': team_data['goals_conceded'].std(),
'avg_shots_against': team_data['shots_against'].mean(),
'clean_sheets': (team_data['goals_conceded'] == 0).sum(),
'tackles_per_match': team_data['tackles'].mean(),
'interceptions_per_match': team_data['interceptions'].mean(),
'clearances_per_match': team_data['clearances'].mean(),
'blocks_per_match': team_data['blocks'].mean(),
'aerial_duels_won_rate': team_data['aerial_duels_won'].mean(),
'avg_possession': team_data['possession'].mean(),
'fouls_per_match': team_data['fouls'].mean(),
'discipline_score': 100 - (team_data['yellow_cards'].mean() * 2 + team_data['red_cards'].mean() * 5)
}
stats_df.append(stats)
return pd.DataFrame(stats_df)
def reliability_score(self):
"""计算防守可靠性评分"""
scores = []
for team in self.teams:
team_data = self.df[self.df['team'] == team]
# 多个维度的评分
consistency = 100 / (1 + team_data['goals_conceded'].std())
avg_performance = 100 / (1 + team_data['goals_conceded'].mean())
recovery_ability = (team_data['goals_conceded'] <= 1).sum() / len(team_data) * 100
pressure_resistance = 100 / (1 + team_data['shots_against'].mean() / 10)
discipline = 100 - (team_data['yellow_cards'].mean() * 2 + team_data['red_cards'].mean() * 5)
# 综合评分(加权平均)
total_score = (
consistency * 0.25 +
avg_performance * 0.25 +
recovery_ability * 0.20 +
pressure_resistance * 0.15 +
discipline * 0.15
)
scores.append({
'team': team,
'consistency': consistency,
'avg_performance': avg_performance,
'recovery_ability': recovery_ability,
'pressure_resistance': pressure_resistance,
'discipline': discipline,
'reliability_score': total_score
})
return pd.DataFrame(scores)
def correlation_analysis(self):
"""相关性分析"""
corr_columns = ['goals_conceded', 'shots_against', 'tackles',
'interceptions', 'clearances', 'aerial_duels_won']
return self.df[corr_columns].corr()
def statistical_test(self):
"""统计显著性检验"""
results = {}
# 方差分析(ANOVA)
goals_by_team = [group['goals_conceded'].values
for name, group in self.df.groupby('team')]
anova_result = stats.f_oneway(*goals_by_team)
results['anova_f_statistic'] = anova_result.statistic
results['anova_p_value'] = anova_result.pvalue
# 两两t检验
t_test_results = []
for i in range(len(self.teams)):
for j in range(i+1, len(self.teams)):
team1 = self.teams[i]
team2 = self.teams[j]
t_stat, p_value = stats.ttest_ind(
self.df[self.df['team'] == team1]['goals_conceded'],
self.df[self.df['team'] == team2]['goals_conceded']
)
t_test_results.append({
'teams': f'{team1} vs {team2}',
't_statistic': t_stat,
'p_value': p_value,
'significant': p_value < 0.05
})
results['pairwise_tests'] = pd.DataFrame(t_test_results)
return results
# 执行分析
analyzer = DefensiveAnalyzer(clean_data)
basic_stats = analyzer.basic_stats()
reliability = analyzer.reliability_score()
correlations = analyzer.correlation_analysis()
statistical_results = analyzer.statistical_test()
可视化
def visualize_analysis(basic_stats, reliability, analyzer_data):
"""可视化分析结果"""
fig = plt.figure(figsize=(20, 15))
# 1. 进球失球对比图
ax1 = plt.subplot(3, 3, 1)
teams = basic_stats['team']
x = np.arange(len(teams))
bars1 = ax1.bar(x, basic_stats['avg_goals_conceded'],
color='skyblue', label='失球数')
bars2 = ax1.bar(x, basic_stats['goals_std'],
color='lightcoral', bottom=basic_stats['avg_goals_conceded'],
label='标准差')
for i, (bar1, bar2) in enumerate(zip(bars1, bars2)):
height = bar1.get_height() + bar2.get_height()
ax1.text(bar1.get_x() + bar1.get_width()/2, height + 0.1,
f'{bar1.get_height():.2f}', ha='center', va='bottom')
ax1.set_xticks(x)
ax1.set_xticklabels(teams)
ax1.set_ylabel('平均失球数')
ax1.set_title('平均失球数及波动范围')
ax1.legend()
ax1.grid(True, alpha=0.3)
# 2. 防守关键指标雷达图
ax2 = plt.subplot(3, 3, 2, projection='polar')
metrics = ['抢断', '拦截', '解围', '封堵', '控球率']
angles = np.linspace(0, 2 * np.pi, len(metrics), endpoint=False).tolist()
angles += angles[:1]
radar_data = basic_stats[['tackles_per_match', 'interceptions_per_match',
'clearances_per_match', 'blocks_per_match',
'avg_possession']].values
colors = ['#FF6B6B', '#4ECDC4', '#45B7D1', '#96CEB4']
for idx, (team, values) in enumerate(zip(basic_stats['team'], radar_data)):
# 归一化
values_norm = values / values.mean()
values_norm = values_norm.tolist() + values_norm[:1]
ax2.plot(angles, values_norm, 'o-', linewidth=2,
label=team, color=colors[idx])
ax2.fill(angles, values_norm, alpha=0.25, color=colors[idx])
ax2.set_xticks(angles[:-1])
ax2.set_xticklabels(metrics)
ax2.set_title('防守关键指标对比')
ax2.legend(loc='upper right', bbox_to_anchor=(1.3, 1.0))
# 3. 可靠性评分对比
ax3 = plt.subplot(3, 3, 3)
scores = reliability.sort_values('reliability_score', ascending=False)
bars = ax3.bar(scores['team'], scores['reliability_score'],
color=['#4CAF50' if s > scores['reliability_score'].mean() else '#FF9800'
for s in scores['reliability_score']])
for bar, score in zip(bars, scores['reliability_score']):
ax3.text(bar.get_x() + bar.get_width()/2, bar.get_height() + 0.5,
f'{score:.1f}', ha='center', va='bottom')
ax3.axhline(scores['reliability_score'].mean(), color='red',
linestyle='--', alpha=0.5, label='平均值')
ax3.set_xlabel('球队')
ax3.set_ylabel('可靠性评分')
ax3.set_title('防守可靠性综合评分')
ax3.legend()
ax3.set_ylim(0, scores['reliability_score'].max() * 1.2)
ax3.grid(True, alpha=0.3)
ax3.tick_params(axis='x', rotation=45)
# 4. 相关性热力图
ax4 = plt.subplot(3, 3, 4)
sns.heatmap(analyzer_data['correlations'], annot=True, fmt='.3f',
cmap='RdBu_r', center=0, ax=ax4, square=True)
ax4.set_title('防守指标相关性矩阵')
# 5. 零封场次对比
ax5 = plt.subplot(3, 3, 5)
clean_sheets = basic_stats[['team', 'clean_sheets']].sort_values('clean_sheets',
ascending=False)
bars = ax5.bar(clean_sheets['team'], clean_sheets['clean_sheets'],
color='#2196F3')
for bar, count in zip(bars, clean_sheets['clean_sheets']):
ax5.text(bar.get_x() + bar.get_width()/2, bar.get_height() + 0.3,
f'{int(count)}', ha='center', va='bottom')
ax5.set_xlabel('球队')
ax5.set_ylabel('零封场次')
ax5.set_title('零封对手场次对比')
ax5.grid(True, alpha=0.3)
ax5.tick_params(axis='x', rotation=45)
# 6. 控球率与失球数散点图
ax6 = plt.subplot(3, 3, 6)
colors = plt.cm.Set1(np.linspace(0, 1, len(analyzer_data['df']['team'].unique())))
for idx, team in enumerate(analyzer_data['df']['team'].unique()):
team_data = analyzer_data['df'][analyzer_data['df']['team'] == team]
ax6.scatter(team_data['possession'], team_data['goals_conceded'],
alpha=0.6, label=team, color=colors[idx])
# 添加趋势线
z = np.polyfit(analyzer_data['df']['possession'],
analyzer_data['df']['goals_conceded'], 1)
p = np.poly1d(z)
ax6.plot(np.unique(analyzer_data['df']['possession']),
p(np.unique(analyzer_data['df']['possession'])),
"r--", alpha=0.5, label='趋势线')
ax6.set_xlabel('控球率 (%)')
ax6.set_ylabel('失球数')
ax6.set_title('控球率与失球数关系')
ax6.legend()
ax6.grid(True, alpha=0.3)
# 7. 防守效率分布箱线图
ax7 = plt.subplot(3, 3, 7)
defense_data = [analyzer_data['df'][analyzer_data['df']['team'] == team]['defense_strength']
for team in analyzer_data['df']['team'].unique()]
bp = ax7.boxplot(defense_data, labels=analyzer_data['df']['team'].unique())
ax7.set_xlabel('球队')
ax7.set_ylabel('防守强度')
ax7.set_title('防守强度分布')
ax7.grid(True, alpha=0.3)
ax7.tick_params(axis='x', rotation=45)
# 8. 统计检验结果
ax8 = plt.subplot(3, 3, 8)
ax8.axis('off')
p_values = statistical_results['pairwise_tests'][['teams', 'p_value', 'significant']]
# 创建表格
table_data = []
for _, row in p_values.iterrows():
table_data.append([row['teams'], f"{row['p_value']:.4f}",
"显著" if row['significant'] else "不显著"])
table = ax8.table(cellText=table_data,
colLabels=['对比球队', 'p值', '显著性'],
cellLoc='center', loc='center',
colWidths=[0.3, 0.15, 0.18])
table.auto_set_font_size(False)
table.set_fontsize(10)
table.scale(1, 1.8)
ax8.set_title('统计检验结果(两两对比)')
# 9. 综合评分对比
ax9 = plt.subplot(3, 3, 9)
composite_scores = basic_stats.copy()
composite_scores['综合得分'] = (
(100 / (1 + composite_scores['avg_goals_conceded'])) * 0.3 +
(composite_scores['avg_possession'] / 100) * 100 * 0.2 +
(composite_scores['tackles_per_match'] /
composite_scores['tackles_per_match'].max()) * 100 * 0.2 +
(composite_scores['interceptions_per_match'] /
composite_scores['interceptions_per_match'].max()) * 100 * 0.15 +
(composite_scores['aerial_duels_won_rate'] /
composite_scores['aerial_duels_won_rate'].max()) * 100 * 0.15
)
composite_scores = composite_scores.sort_values('综合得分', ascending=False)
bars = ax9.bar(composite_scores['team'], composite_scores['综合得分'],
color=['#FF5733' if s > composite_scores['综合得分'].mean()
else '#33A8FF' for s in composite_scores['综合得分']])
for bar, score in zip(bars, composite_scores['综合得分']):
ax9.text(bar.get_x() + bar.get_width()/2, bar.get_height() + 1,
f'{score:.1f}', ha='center', va='bottom')
ax9.set_xlabel('球队')
ax9.set_ylabel('综合得分')
ax9.set_title('防守综合能力对比')
ax9.grid(True, alpha=0.3)
ax9.tick_params(axis='x', rotation=45)
plt.tight_layout()
plt.show()
主程序执行
def main():
"""主程序"""
print("=" * 60)
print(" 足球防守数据分析报告")
print("=" * 60)
# 执行分析
analyzer = DefensiveAnalyzer(clean_data)
print("\n【1】基础数据统计:")
print("-" * 60)
basic_stats = analyzer.basic_stats()
display_cols = ['team', 'avg_goals_conceded', 'goals_std', 'clean_sheets',
'avg_possession', 'tackles_per_match', 'interceptions_per_match']
print(basic_stats[display_cols].to_string(index=False))
print("\n【2】各防守指标排名:")
print("-" * 60)
metrics = {
'avg_goals_conceded': '平均失球',
'clean_sheets': '零封场次',
'tackles_per_match': '场均抢断',
'interceptions_per_match': '场均拦截',
'aerial_duels_won_rate': '制空权'
}
for col, label in metrics.items():
ranked = basic_stats[['team', col]].sort_values(col, ascending=False if '抢断' in label or '拦截' in label or '制空' in label else True)
print(f"{label}排名: {' > '.join(ranked['team'])}")
print("\n【3】防守可靠性评分:")
print("-" * 60)
reliability = analyzer.reliability_score()
reliability_sorted = reliability.sort_values('reliability_score', ascending=False)
for _, row in reliability_sorted.iterrows():
print(f"{row['team']}: {row['reliability_score']:.2f} 分")
print("\n【4】统计显著性检验:")
print("-" * 60)
stat_results = analyzer.statistical_test()
print(f"ANOVA F值: {stat_results['anova_f_statistic']:.3f}")
print(f"ANOVA p值: {stat_results['anova_p_value']:.4f}")
if stat_results['anova_p_value'] < 0.05:
print("各队防守水平存在显著差异 (p < 0.05)")
else:
print("各队防守水平无显著差异 (p > 0.05)")
print("\n【5】最终排名结论:")
print("-" * 60)
# 综合所有指标进行最终排名
final_rank = []
for team in basic_stats['team']:
team_stats = basic_stats[basic_stats['team'] == team].iloc[0]
team_reliability = reliability[reliability['team'] == team].iloc[0]
score = (team_stats['avg_goals_conceded'] * -10 + # 失球越少越好
team_stats['clean_sheets'] * 2 +
team_reliability['reliability_score'])
final_rank.append({'team': team, 'final_score': score,
'reliability': team_reliability['reliability_score']})
# 排序并输出结果
final_df = pd.DataFrame(final_rank).sort_values('final_score', ascending=False)
rank_emojis = ['🥇', '🥈', '🥉', '🏅']
print("\n最终防守排名:")
for i, (_, row) in enumerate(final_df.iterrows()):
medal = rank_emojis[i] if i < 4 else ''
print(f"{medal} 第{i+1}名: {row['team']} (综合评分: {row['final_score']:.2f})")
# 输出最终结论
best_team = final_df.iloc[0]['team']
print(f"\n【{best_team} 的防线最稳固可靠!")
# 可视化
visualize_analysis(basic_stats, reliability,
{'df': clean_data, 'correlations': correlations})
return final_df
if __name__ == "__main__":
results = main()
这个综合案例包含了:
- 数据结构化处理:生成模拟数据,包含多个防守指标
- 数据清洗:处理异常值、缺失值、添加衍生特征
- 统计分析:基础统计、可靠性评分、相关性分析、统计检验
- 数据可视化:多种图表展示分析结果
- 综合评分:多个维度综合评估球队防守能力
运行后会输出详细的防守分析报告,包括各队排名、统计显著性检验结果,并生成直观的可视化图表,帮助我们判断哪支球队的防线最稳固可靠。