本文目录导读:

我来为您设计一个统计足球比赛中交叉跑位造成威胁次数的Python案例。
核心方案
import pandas as pd
import numpy as np
from datetime import datetime
import matplotlib.pyplot as plt
import seaborn as sns
class CrossRunThreatAnalyzer:
"""
交叉跑位威胁分析器
分析球员交叉跑位时创造的进攻威胁次数
"""
def __init__(self):
# 威胁判定参数
self.threat_zone = 30 # 距离球门30米内为威胁区域
self.min_speed = 5.0 # 最小跑动速度(m/s)
self.cross_angle = 45 # 最小交叉角度(度)
def generate_sample_data(self, n_players=10, n_actions=50):
"""
生成模拟比赛数据
包含球员位置、速度、方向等信息
"""
np.random.seed(42)
data = []
for i in range(n_actions):
# 生成随机时间点
timestamp = datetime(2024, 1, 1, 15, 0, 0) + pd.Timedelta(seconds=i*2)
# 进攻方球员(假设进攻方向向右,球门在x=105处)
attacker_x = np.random.uniform(50, 100)
attacker_y = np.random.uniform(0, 68)
attacker_speed = np.random.uniform(4, 9)
attacker_angle = np.random.uniform(0, 360)
# 防守球员
defender_x = attacker_x + np.random.uniform(-10, 10)
defender_y = attacker_y + np.random.uniform(-10, 10)
defender_speed = np.random.uniform(3, 8)
# 是否有交叉跑位(50%概率)
is_cross_run = np.random.choice([0, 1], p=[0.5, 0.5])
# 是否形成威胁(与位置、速度、交叉相关)
threat_probability = self.calculate_threat_probability(
attacker_x, attacker_y, attacker_speed,
defender_x, defender_y, is_cross_run
)
is_threat = np.random.choice([0, 1], p=[1-threat_probability, threat_probability])
data.append({
'timestamp': timestamp,
'attacker_x': attacker_x,
'attacker_y': attacker_y,
'attacker_speed': attacker_speed,
'attacker_angle': attacker_angle,
'defender_x': defender_x,
'defender_y': defender_y,
'defender_speed': defender_speed,
'is_cross_run': is_cross_run,
'is_threat': is_threat
})
return pd.DataFrame(data)
def calculate_threat_probability(self, ax, ay, speed, dx, dy, cross_run):
"""
计算威胁概率
"""
# 距离球门的距离(假设球门在 (105, 34) 位置)
goal_x, goal_y = 105, 34
dist_to_goal = np.sqrt((ax - goal_x)**2 + (ay - goal_y)**2)
# 基本威胁概率
threat_prob = 0
# 1. 位置因素(距离越近威胁越大)
if dist_to_goal < self.threat_zone:
threat_prob += 0.3 * (1 - dist_to_goal / self.threat_zone)
# 2. 速度因素
if speed > self.min_speed:
threat_prob += 0.2 * min(1, speed / 10)
# 3. 防守距离因素(防守越远威胁越大)
defend_dist = np.sqrt((ax - dx)**2 + (ay - dy)**2)
threat_prob += 0.2 * min(1, defend_dist / 15)
# 4. 交叉跑位加成
if cross_run == 1:
threat_prob += 0.3
return min(1, threat_prob)
def analyze_cross_run_threats(self, df):
"""
统计分析交叉跑位造成的威胁
"""
print("="*60)
print("交叉跑位威胁统计分析")
print("="*60)
# 1. 基础统计
total_actions = len(df)
cross_runs = df[df['is_cross_run'] == 1]
threats = df[df['is_threat'] == 1]
cross_threats = cross_runs[cross_runs['is_threat'] == 1]
print(f"\n总行动次数: {total_actions}")
print(f"交叉跑位次数: {len(cross_runs)}")
print(f"总威胁次数: {len(threats)}")
print(f"交叉跑位造成的威胁次数: {len(cross_threats)}")
# 2. 计算比率
if len(cross_runs) > 0:
cross_threat_rate = len(cross_threats) / len(cross_runs) * 100
print(f"\n交叉跑位威胁成功率: {cross_threat_rate:.1f}%")
# 3. 时间段分布
df['time_bin'] = pd.cut(
df['timestamp'].dt.minute,
bins=[0, 15, 30, 45, 60],
labels=['0-15min', '15-30min', '30-45min', '45-60min']
)
print("\n按时间段分布:")
time_distribution = df.groupby('time_bin', observed=True).agg({
'is_cross_run': 'sum',
'is_threat': 'sum'
}).rename(columns={'is_cross_run': '交叉跑位数', 'is_threat': '威胁数'})
print(time_distribution)
# 4. 区域分析(将球场分为三块区域)
df['zone'] = pd.cut(
df['attacker_x'],
bins=[0, 35, 70, 105],
labels=['防守三区', '中场', '进攻三区']
)
print("\n按区域分布:")
zone_distribution = df.groupby('zone', observed=True).agg({
'is_threat': ['sum', 'mean']
}).rename(columns={'sum': '威胁次数', 'mean': '威胁率'})
print(zone_distribution)
return cross_threats
def visualize_results(self, df, cross_threats):
"""
可视化分析结果
"""
fig, axes = plt.subplots(2, 2, figsize=(12, 10))
# 1. 威胁比例饼图
ax1 = axes[0, 0]
threat_types = {
'交叉跑位威胁': len(cross_threats),
'其他方式威胁': len(df[df['is_threat']==1]) - len(cross_threats),
'无威胁': len(df) - len(df[df['is_threat']==1])
}
colors = ['#FF6B6B', '#4ECDC4', '#95E1D3']
ax1.pie(
threat_types.values(),
labels=threat_types.keys(),
colors=colors,
autopct='%1.1f%%',
startangle=90
)
ax1.set_title('威胁来源分布')
# 2. 场地位置散点图
ax2 = axes[0, 1]
non_threat = df[df['is_threat']==0]
threat_points = df[df['is_threat']==1]
cross_threat_points = cross_threats
ax2.scatter(non_threat['attacker_x'], non_threat['attacker_y'],
label='无威胁', alpha=0.3, s=20, c='gray')
ax2.scatter(threat_points['attacker_x'], threat_points['attacker_y'],
label='有威胁', alpha=0.5, s=40, c='orange')
ax2.scatter(cross_threat_points['attacker_x'], cross_threat_points['attacker_y'],
label='交叉跑位威胁', alpha=0.8, s=60, c='red', marker='*')
ax2.set_xlabel('球场长度位置(m)')
ax2.set_ylabel('球场宽度位置(m)')
ax2.set_title('威胁位置分布')
ax2.legend()
ax2.grid(True, alpha=0.3)
# 3. 速度分布图
ax3 = axes[1, 0]
cross_runs = df[df['is_cross_run']==1]
non_cross_runs = df[df['is_cross_run']==0]
ax3.hist(cross_runs['attacker_speed'], bins=15, alpha=0.5,
label='交叉跑位', color='red')
ax3.hist(non_cross_runs['attacker_speed'], bins=15, alpha=0.5,
label='非交叉跑位', color='blue')
ax3.set_xlabel('速度(m/s)')
ax3.set_ylabel('频次')
ax3.set_title('速度分布对比')
ax3.legend()
# 4. 时间段威胁趋势
ax4 = axes[1, 1]
df['time_min'] = df['timestamp'].dt.minute
trend_data = df.groupby('time_min')['is_threat'].mean().rolling(window=3).mean()
ax4.plot(trend_data.index, trend_data.values, marker='o', color='purple')
ax4.set_xlabel('比赛时间(分钟)')
ax4.set_ylabel('威胁率')
ax4.set_title('威胁率时间趋势')
ax4.grid(True, alpha=0.3)
plt.tight_layout()
plt.show()
def export_report(self, df, cross_threats, filename='threat_report.csv'):
"""
导出详细报告
"""
report_data = {
'指标': [
'总行动次数',
'交叉跑位次数',
'总威胁次数',
'交叉跑位威胁次数',
'交叉跑位威胁占比',
'平均速度'
],
'数值': [
len(df),
len(df[df['is_cross_run']==1]),
len(df[df['is_threat']==1]),
len(cross_threats),
f"{len(cross_threats)/len(df)*100:.1f}%" if len(df) > 0 else "0%",
f"{df['attacker_speed'].mean():.2f} m/s"
]
}
report_df = pd.DataFrame(report_data)
report_df.to_csv(filename, index=False)
print(f"\n报告已保存到: {filename}")
# 同时保存详细数据
cross_threats.to_csv('cross_threat_details.csv', index=False)
print(f"详细数据已保存到: cross_threat_details.csv")
# 使用示例
def main():
# 创建分析器
analyzer = CrossRunThreatAnalyzer()
# 生成模拟数据
print("正在生成模拟比赛数据...")
df = analyzer.generate_sample_data(n_players=10, n_actions=80)
# 执行分析
cross_threats = analyzer.analyze_cross_run_threats(df)
# 可视化
analyzer.visualize_results(df, cross_threats)
# 导出报告
analyzer.export_report(df, cross_threats)
print("\n分析完成!")
print(f"交叉跑位共造成 {len(cross_threats)} 次威胁")
if __name__ == "__main__":
main()
补充方案:更简化的版本
import random
from collections import Counter
def simple_cross_run_threat_analysis():
"""
简化版交叉跑位威胁统计
"""
# 模拟足球比赛数据
players = ["前锋A", "前锋B", "边锋C", "边锋D", "前腰E", "后腰F"]
# 生成比赛事件
events = []
for i in range(50):
event = {
'事件ID': i+1,
'球员': random.choice(players),
'速度': random.uniform(3, 8),
'位置': random.choice(['禁区前沿', '边路', '中路', '肋部']),
'交叉跑位': random.random() < 0.4, # 40%概率交叉
'防守压力': random.choice(['小', '中', '大']),
'最终威胁': False # 默认无威胁
}
# 判断是否有威胁
if (event['速度'] > 5 and
(event['交叉跑位'] or event['位置'] == '禁区前沿') and
event['防守压力'] != '大'):
event['最终威胁'] = True
events.append(event)
# 统计交叉跑位造成的威胁
cross_threats = [e for e in events if e['交叉跑位'] and e['最终威胁']]
total_cross = [e for e in events if e['交叉跑位']]
print("="*50)
print("交叉跑位威胁统计结果")
print("="*50)
print(f"总事件数: {len(events)}")
print(f"交叉跑位次数: {len(total_cross)}")
print(f"交叉跑位造成威胁次数: {len(cross_threats)}")
print(f"威胁成功率: {len(cross_threats)/len(total_cross)*100:.1f}%")
# 按球员统计
player_stats = Counter([e['球员'] for e in cross_threats])
print("\n球员威胁次数排名:")
for player, count in player_stats.most_common():
print(f" {player}: {count}次")
return len(cross_threats)
# 运行简化版本
if __name__ == "__main__":
result = simple_cross_run_threat_analysis()
print(f"\n最终统计:交叉跑位造成威胁 {result} 次")
核心功能说明
- 数据模拟:生成包含跑位、速度、位置等信息的比赛数据
- 威胁判定:基于位置、速度、防守压力等因素计算威胁概率
- 统计分析:交叉跑位次数、威胁次数、成功率
- 可视化:展示威胁分布、时间趋势等
- 报告导出:CSV格式的分析报告
这个系统可以帮助教练和数据分析师统计比赛中通过交叉跑位创造进攻威胁的具体次数和效果。