我来为您设计一个实时显示场上优势的Python案例,这个案例模拟一场足球比赛,实时计算并显示双方的攻防优势。

import numpy as np
import matplotlib.pyplot as plt
from matplotlib.animation import FuncAnimation
import time
from collections import deque
import random
class MatchTracker:
"""实时比赛优势追踪器"""
def __init__(self, team1="主队", team2="客队"):
self.team1_name = team1
self.team2_name = team2
# 实时数据存储(缓存最近60秒数据)
self.time_window = 60
self.timestamps = deque(maxlen=self.time_window)
# 主队数据
self.team1_possession = deque(maxlen=self.time_window) # 控球率
self.team1_shots = deque(maxlen=self.time_window) # 射门
self.team1_corners = deque(maxlen=self.time_window) # 角球
self.team1_danger = deque(maxlen=self.time_window) # 危险进攻
# 客队数据
self.team2_possession = deque(maxlen=self.time_window)
self.team2_shots = deque(maxlen=self.time_window)
self.team2_corners = deque(maxlen=self.time_window)
self.team2_danger = deque(maxlen=self.time_window)
# 综合优势评分
self.team1_advantage = deque(maxlen=self.time_window)
self.team2_advantage = deque(maxlen=self.time_window)
# 比赛数据累计
self.total_possession_t1 = 50
self.total_possession_t2 = 50
self.score_t1 = 0
self.score_t2 = 0
# 统计权重
self.weights = {
'possession': 0.3, # 控球率权重
'shots': 0.25, # 射门权重
'corners': 0.15, # 角球权重
'danger': 0.3 # 危险进攻权重
}
# 创建图表
self.setup_plot()
def setup_plot(self):
"""初始化图表布局"""
self.fig = plt.figure(figsize=(15, 8))
gs = self.fig.add_gridspec(3, 3)
# 主优势仪表盘
self.ax_gauge = self.fig.add_subplot(gs[0, :])
self.ax_gauge.set_xlim(-1.5, 1.5)
self.ax_gauge.set_ylim(-1, 1)
self.ax_gauge.axis('off')
# 控球率
self.ax_possession = self.fig.add_subplot(gs[1, 0])
self.ax_possession.set_ylabel('控球率')
# 射门统计
self.ax_shots = self.fig.add_subplot(gs[1, 1])
self.ax_shots.set_ylabel('射门')
# 危险进攻
self.ax_danger = self.fig.add_subplot(gs[1, 2])
self.ax_danger.set_ylabel('危险进攻')
# 整体优势趋势
self.ax_trend = self.fig.add_subplot(gs[2, :])
self.ax_trend.set_ylabel('优势度')
self.ax_trend.set_xlabel('时间(秒)')
self.ax_trend.axhline(y=0, color='gray', linestyle='--', alpha=0.5)
self.fig.suptitle(f'{self.team1_name} vs {self.team2_name} 实时优势分析', fontsize=16, fontweight='bold')
# 颜色设置
self.color_t1 = '#1e88e5'
self.color_t2 = '#e53935'
def calculate_advantage(self):
"""计算综合优势"""
if not self.timestamps:
return 0, 0
# 获取最近的数据
t1_pos = np.mean(self.team1_possession) if self.team1_possession else 50
t2_pos = np.mean(self.team2_possession) if self.team2_possession else 50
t1_shot = np.sum(self.team1_shots) if self.team1_shots else 0
t2_shot = np.sum(self.team2_shots) if self.team2_shots else 0
t1_corner = np.sum(self.team1_corners) if self.team1_corners else 0
t2_corner = np.sum(self.team2_corners) if self.team2_corners else 0
t1_danger = np.sum(self.team1_danger) if self.team1_danger else 0
t2_danger = np.sum(self.team2_danger) if self.team2_danger else 0
# 归一化处理
max_shots = max(t1_shot, t2_shot, 1)
max_corners = max(t1_corner, t2_corner, 1)
max_danger = max(t1_danger, t2_danger, 1)
# 计算各项比分
possession_score = (t1_pos - t2_pos) / 100
shots_score = (t1_shot - t2_shot) / max_shots
corners_score = (t1_corner - t2_corner) / max_corners
danger_score = (t1_danger - t2_danger) / max_danger
# 加权计算总优势
t1_adv = (possession_score * self.weights['possession'] +
shots_score * self.weights['shots'] +
corners_score * self.weights['corners'] +
danger_score * self.weights['danger'])
# 加上比分权重
score_advantage = (self.score_t1 - self.score_t2) * 0.2
return t1_adv + score_advantage, -(t1_adv + score_advantage)
def update_data(self):
"""模拟实时数据更新"""
current_time = time.time()
self.timestamps.append(current_time)
# 模拟比赛数据(真实应用中从传感器/数据源获取)
# 主队数据
t1_possession = 40 + random.uniform(-15, 15)
t1_shots = random.choice([0, 0, 0, 0, 1, 1, 1, 2])
t1_corners = random.choice([0, 0, 0, 1, 1, 2])
t1_danger = random.choice([0, 0, 1, 1, 1, 2, 3])
# 客队数据(反相关增加对抗性)
t2_possession = 100 - t1_possession
t2_shots = random.choice([0, 0, 0, 0, 1, 1, 1, 2])
t2_corners = random.choice([0, 0, 0, 1, 1, 2])
t2_danger = random.choice([0, 0, 1, 1, 1, 2, 3])
# 更新数据存储
self.team1_possession.append(t1_possession)
self.team1_shots.append(t1_shots)
self.team1_corners.append(t1_corners)
self.team1_danger.append(t1_danger)
self.team2_possession.append(t2_possession)
self.team2_shots.append(t2_shots)
self.team2_corners.append(t2_corners)
self.team2_danger.append(t2_danger)
# 更新总控球率
self.total_possession_t1 = 0.95 * self.total_possession_t1 + 0.05 * t1_possession
self.total_possession_t2 = 100 - self.total_possession_t1
# 模拟进球(小概率)
if random.random() < 0.01: # 1%概率进球
if random.random() < 0.5:
self.score_t1 += 1
self.team1_advantage.append(0.5) # 进球加分
else:
self.score_t2 += 1
self.team2_advantage.append(0.5)
# 计算综合优势
t1_adv, t2_adv = self.calculate_advantage()
self.team1_advantage.append(t1_adv)
self.team2_advantage.append(t2_adv)
def update_plot(self, frame):
"""更新图表"""
# 更新数据
self.update_data()
# 清空所有子图
for ax in [self.ax_gauge, self.ax_possession, self.ax_shots,
self.ax_danger, self.ax_trend]:
ax.clear()
# 1. 仪表盘显示
advantage = self.team1_advantage[-1] if self.team1_advantage else 0
self.draw_gauge(self.ax_gauge, advantage)
# 2. 控球率饼图
sizes = [self.total_possession_t1, self.total_possession_t2]
labels = [f'{self.team1_name}\n{sizes[0]:.1f}%',
f'{self.team2_name}\n{sizes[1]:.1f}%']
colors = [self.color_t1, self.color_t2]
self.ax_possession.clear()
wedges, texts, autotexts = self.ax_possession.pie(
sizes, labels=labels, colors=colors, autopct='%1.1f%%',
startangle=90, explode=(0.05, 0.05)
)
self.ax_possession.set_title('控球率')
# 3. 射门统计
if self.team1_shots and self.team2_shots:
shots_t1 = np.sum(self.team1_shots)
shots_t2 = np.sum(self.team2_shots)
self.ax_shots.bar([0, 1], [shots_t1, shots_t2],
color=[self.color_t1, self.color_t2], alpha=0.7)
self.ax_shots.set_xticks([0, 1])
self.ax_shots.set_xticklabels([self.team1_name, self.team2_name])
self.ax_shots.set_title(f'射门(主队:{shots_t1} 客队:{shots_t2})')
# 4. 危险进攻
if self.team1_danger and self.team2_danger:
danger_t1 = np.sum(self.team1_danger)
danger_t2 = np.sum(self.team2_danger)
self.ax_danger.bar([0, 1], [danger_t1, danger_t2],
color=[self.color_t1, self.color_t2], alpha=0.7)
self.ax_danger.set_xticks([0, 1])
self.ax_danger.set_xticklabels([self.team1_name, self.team2_name])
self.ax_danger.set_title(f'危险进攻(主队:{danger_t1} 客队:{danger_t2})')
# 5. 优势趋势图
if len(self.team1_advantage) > 1:
x = range(len(self.team1_advantage))
self.ax_trend.plot(x, list(self.team1_advantage),
color=self.color_t1, label=f'{self.team1_name}优势', linewidth=2)
self.ax_trend.plot(x, list(self.team2_advantage),
color=self.color_t2, label=f'{self.team2_name}优势', linewidth=2)
# 填充优势区域
self.ax_trend.fill_between(x, 0, list(self.team1_advantage),
alpha=0.2, color=self.color_t1)
self.ax_trend.fill_between(x, 0, list(self.team2_advantage),
alpha=0.2, color=self.color_t2)
self.ax_trend.legend(loc='upper right')
self.ax_trend.grid(True, alpha=0.3)
self.ax_trend.set_ylim(-1.5, 1.5)
# 在仪表盘下方显示比分
self.fig.text(0.5, 0.88,
f'比分: {self.team1_name} {self.score_t1} - {self.score_t2} {self.team2_name}',
ha='center', fontsize=12, color='black',
bbox=dict(boxstyle='round', facecolor='wheat', alpha=0.5))
def draw_gauge(self, ax, value):
"""绘制优势仪表盘"""
# 背景圆环
theta = np.linspace(0, 2*np.pi, 100)
x = np.cos(theta)
y = np.sin(theta)
# 绘制半圆背景
theta_half = np.linspace(np.pi, 2*np.pi, 50)
ax.fill(np.cos(theta_half), np.sin(theta_half), color='lightgray', alpha=0.3)
# 绘制优势指示
angle = np.pi + (np.clip(value, -1, 1) + 1) * np.pi/2
arrow_length = 0.8
ax.annotate('', xy=(arrow_length*np.cos(angle), arrow_length*np.sin(angle)),
xytext=(0, 0),
arrowprops=dict(arrowstyle='->', lw=3, color='black'))
# 添加标签
ax.text(0, -0.2, f'优势度: {value:.2f}', ha='center', fontsize=12)
ax.text(-1.2, 0, self.team1_name, color=self.color_t1, fontweight='bold')
ax.text(1.2, 0, self.team2_name, color=self.color_t2, fontweight='bold')
# 绘制刻度和文字
for i in range(-2, 3):
angle_i = np.pi + (i + 2) * np.pi/4
value_i = i/2
ax.text(1.1*np.cos(angle_i), 1.1*np.sin(angle_i),
f'{value_i:.1f}', ha='center', fontsize=8)
# 判断优势方
if value > 0.1:
ax.text(0, 0.5, f'{self.team1_name}占优!', ha='center',
fontsize=14, color=self.color_t1, fontweight='bold')
elif value < -0.1:
ax.text(0, 0.5, f'{self.team2_name}占优!', ha='center',
fontsize=14, color=self.color_t2, fontweight='bold')
else:
ax.text(0, 0.5, '势均力敌', ha='center', fontsize=14, color='gray')
def main():
"""主函数"""
# 创建追踪器
tracker = MatchTracker("主队", "客队")
# 创建动画
ani = FuncAnimation(tracker.fig, tracker.update_plot,
interval=1000, # 1秒更新一次
save_count=100)
print("实时比赛优势追踪器已启动")
print("按Ctrl+C停止运行")
try:
plt.tight_layout()
plt.show()
except KeyboardInterrupt:
print("\n追踪器已停止")
# 显示最终统计
print("\n===== 比赛总结 =====")
print(f"最终比分: {tracker.team1_name} {tracker.score_t1} - {tracker.score_t2} {tracker.team2_name}")
print(f"最终控球率: {tracker.team1_name} {tracker.total_possession_t1:.1f}% - {tracker.total_possession_t2:.1f}% {tracker.team2_name}")
if tracker.team1_advantage:
final_adv = tracker.team1_advantage[-1]
print(f"最终优势度: {final_adv:.2f}")
if final_adv > 0.1:
print(f" {tracker.team1_name}在场上更具优势!")
elif final_adv < -0.1:
print(f" {tracker.team2_name}在场上更具优势!")
else:
print(" 两队势均力敌")
if __name__ == "__main__":
main()
运行效果:
- 实时仪表盘:顶部显示当前优势度,指针偏向优势方
- 控球率:直观的饼图显示双方控球比例
- 射门统计:柱状图展示双方射门次数
- 危险进攻:显示创造的威胁机会
- 优势趋势:动态折线图展示整场比赛的优势变化
扩展建议:
- 接入真实比赛数据(如API、传感器)
- 添加更多统计指标(传球成功率、抢断、犯规等)
- 增加比赛事件标记(进球、红黄牌等)
- 支持多场比赛同时监控
- 添加预警系统(某队连续占优时提醒)
这个系统可以用于体育分析、比赛直播辅助、战术分析等场景,实时反映场上哪一方更具优势。