本文目录导读:

我来详细介绍如何用Python分析球员跑动热点图(热力图),这是一个完整的实战案例:
数据准备
我们需要球员跑动数据,这里提供两种方式:
import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
from matplotlib.colors import LinearSegmentedColormap
import seaborn as sns
from scipy.stats import gaussian_kde
import warnings
warnings.filterwarnings('ignore')
# 方法1:模拟数据(假设场景)
def generate_position_data(num_frames=1000, player_speed=8):
"""生成模拟球员跑动位置数据"""
np.random.seed(42)
# 足球场尺寸:105m x 68m
positions = []
x, y = 50, 34 # 初始位置(中场)
for i in range(num_frames):
# 模拟球员移动模式
if i % 100 == 0: # 改变跑动策略
target_x = np.random.uniform(0, 105)
target_y = np.random.uniform(0, 68)
# 向目标移动
dx = target_x - x
dy = target_y - y
dist = np.sqrt(dx**2 + dy**2)
if dist > 1:
# 加入一些随机性
step = min(player_speed, dist)
x += (dx/dist) * step + np.random.normal(0, 0.5)
y += (dy/dist) * step + np.random.normal(0, 0.5)
# 限制在场地内
x = np.clip(x, 0, 105)
y = np.clip(y, 0, 68)
positions.append([i*0.1, x, y]) # 时间戳、x坐标、y坐标
return pd.DataFrame(positions, columns=['time', 'x', 'y'])
# 方法2:加载真实数据(如果需要)
# df = pd.read_csv('player_tracking_data.csv')
# 生成示例数据
df = generate_position_data(num_frames=2000, player_speed=8)
print(df.head())
print(f"数据点数量: {len(df)}")
基础热力图绘制
def plot_basic_heatmap(df, title="球员跑动热点图"):
"""绘制基础热力图"""
fig, ax = plt.subplots(1, 2, figsize=(16, 6))
# 图1:2D直方图(热力图)
heatmap, xedges, yedges = np.histogram2d(
df['x'], df['y'],
bins=(40, 30), # 网格数量
range=[[0, 105], [0, 68]]
)
im = ax[0].imshow(heatmap.T, origin='lower',
extent=[0, 105, 0, 68],
cmap='hot', # 使用热点图配色
aspect='equal', alpha=0.8)
plt.colorbar(im, ax=ax[0], label='访问频率')
# 图2:等高线图
x = df['x'].values
y = df['y'].values
# 使用KDE核密度估计
kde = gaussian_kde(np.vstack([x, y]),
bw_method=0.1) # 带宽参数控制平滑度
# 创建网格
xi = np.linspace(0, 105, 100)
yi = np.linspace(0, 68, 100)
zi = kde(np.meshgrid(xi, yi))
# 绘制等高线
contour = ax[1].contourf(xi, yi, zi, levels=15, cmap='YlOrRd')
plt.colorbar(contour, ax=ax[1], label='密度')
for axes in ax:
# 绘制球场边界
axes.set_xlim(0, 105)
axes.set_ylim(0, 68)
axes.set_aspect('equal')
axes.set_xlabel('X坐标(米)')
axes.set_ylabel('Y坐标(米)')
axes.set_title(title)
# 绘制中场线
axes.axhline(y=34, color='green', linestyle='--', alpha=0.3)
axes.axvline(x=52.5, color='green', linestyle='--', alpha=0.3)
# 绘制禁区
axes.axhline(y=13.84, xmin=0.9, xmax=1, color='gray', alpha=0.5)
axes.axhline(y=54.16, xmin=0.9, xmax=1, color='gray', alpha=0.5)
plt.tight_layout()
plt.show()
return heatmap
# 运行基础热力图
heatmap = plot_basic_heatmap(df)
高级热力图分析
class PlayerHeatmapAnalyzer:
"""球员跑动热力图分析器"""
def __init__(self, df, field_length=105, field_width=68):
self.df = df
self.field_length = field_length
self.field_width = field_width
def analyze_zone_coverage(self):
"""分析不同区域的覆盖情况"""
# 将球场分为9个区域
zones = {
'前场左路': (0, 33, 45, 68), # (x_min, x_max, y_min, y_max)
'前场中路': (0, 33, 22, 45),
'前场右路': (0, 33, 0, 22),
'中场左路': (33, 66, 45, 68),
'中场中路': (33, 66, 22, 45),
'中场右路': (33, 66, 0, 22),
'后场左路': (66, 105, 45, 68),
'后场中路': (66, 105, 22, 45),
'后场右路': (66, 105, 0, 22)
}
zone_presence = {}
zone_hotspots = {}
for zone, (x_min, x_max, y_min, y_max) in zones.items():
mask = (self.df['x'] >= x_min) & (self.df['x'] < x_max) & \
(self.df['y'] >= y_min) & (self.df['y'] < y_max)
zone_presence[zone] = self.df[mask].shape[0] / len(self.df) * 100
# 计算区域内的热点(最常出现的位置)
if self.df[mask].shape[0] > 0:
zone_data = self.df[mask]
kde = gaussian_kde(np.vstack([zone_data['x'].values,
zone_data['y'].values]))
xi = np.linspace(x_min, x_max, 20)
yi = np.linspace(y_min, y_max, 20)
zi = kde(np.meshgrid(xi, yi))
max_idx = np.unravel_index(zi.argmax(), zi.shape)
zone_hotspots[zone] = (xi[max_idx[0]], yi[max_idx[1]])
return zone_presence, zone_hotspots
def analyze_time_patterns(self):
"""分析时间维度的跑动模式"""
# 按时间段分析
if 'time' in self.df.columns:
max_time = self.df['time'].max()
time_bins = pd.cut(self.df['time'], bins=5)
time_stats = {}
for interval, group in self.df.groupby(time_bins):
# 计算该时段内的跑动距离
if len(group) > 1:
distance = np.sqrt(np.diff(group['x'].values)**2 +
np.diff(group['y'].values)**2)
total_distance = np.sum(distance)
avg_speed = total_distance / (len(group) * 0.1) # 假设0.1秒每帧
time_stats[str(interval)] = {
'distance': total_distance,
'avg_speed': avg_speed,
'positions': len(group)
}
return time_stats
return None
def visualize_comprehensive(self):
"""综合可视化分析"""
fig = plt.figure(figsize=(18, 12))
# 1. 主热力图(密度图)
ax1 = plt.subplot(2, 3, 1)
self._plot_density_heatmap(ax1)
# 2. 区域热力图
ax2 = plt.subplot(2, 3, 2)
self._plot_zone_heatmap(ax2)
# 3. 时间序列图
ax3 = plt.subplot(2, 3, 3)
self._plot_temporal_analysis(ax3)
# 4. 移动轨迹图
ax4 = plt.subplot(2, 3, 4)
self._plot_movement_trace(ax4)
# 5. 速度分布图
ax5 = plt.subplot(2, 3, 5)
self._plot_speed_distribution(ax5)
# 6. 热区百分比饼图
ax6 = plt.subplot(2, 3, 6)
self._plot_zone_distribution(ax6)
plt.tight_layout()
plt.show()
def _plot_density_heatmap(self, ax):
"""绘制密度热力图"""
x = self.df['x'].values
y = self.df['y'].values
kde = gaussian_kde(np.vstack([x, y]), bw_method=0.08)
xi = np.linspace(0, self.field_length, 100)
yi = np.linspace(0, self.field_width, 100)
zi = kde(np.meshgrid(xi, yi))
# 使用自定义配色
colors = ['blue', 'cyan', 'green', 'yellow', 'red']
cmap = LinearSegmentedColormap.from_list('custom', colors, N=100)
im = ax.contourf(xi, yi, zi, levels=20, cmap=cmap)
plt.colorbar(im, ax=ax, label='密度')
self._draw_field(ax)
ax.set_title('球员跑动热点密度图')
def _plot_zone_heatmap(self, ax):
"""绘制区域热力图"""
zone_presence, zone_hotspots = self.analyze_zone_coverage()
# 绘制9宫格区域
colors = ['red', 'orange', 'yellow', 'lightgreen', 'green']
for idx, (zone, percentage) in enumerate(zone_presence.items()):
# 根据覆盖率设置颜色深浅
color_intensity = percentage / 20 # 归一化
if '前场' in zone:
x_start, x_end = 0, 35
elif '中场' in zone:
x_start, x_end = 35, 70
else:
x_start, x_end = 70, 105
if '左路' in zone:
y_start, y_end = 45, 68
elif '中路' in zone:
y_start, y_end = 22, 45
else:
y_start, y_end = 0, 22
rect = plt.Rectangle((x_start, y_start), x_end-x_start, y_end-y_start,
alpha=min(color_intensity, 1),
color='red' if percentage > 15 else 'orange' if percentage > 10 else 'yellow')
ax.add_patch(rect)
# 标注区域覆盖率
ax.text(x_start+10, y_start+8, f'{percentage:.1f}%',
fontsize=8, ha='center')
self._draw_field(ax)
ax.set_title('区域跑动覆盖率')
def _plot_temporal_analysis(self, ax):
"""绘制时间序列分析"""
time_stats = self.analyze_time_patterns()
if time_stats:
times = list(time_stats.keys())
distances = [stats['distance'] for stats in time_stats.values()]
speeds = [stats['avg_speed'] for stats in time_stats.values()]
ax2 = ax.twinx()
line1, = ax.plot(range(len(distances)), distances, 'b-', label='距离')
line2, = ax2.plot(range(len(speeds)), speeds, 'r-', label='速度')
ax.set_xlabel('时间段')
ax.set_ylabel('跑动距离 (m)', color='b')
ax2.set_ylabel('平均速度 (m/s)', color='r')
ax.set_xticks(range(len(times)))
ax.set_xticklabels([t[8:13] for t in times], rotation=45)
lines = [line1, line2]
labels = [l.get_label() for l in lines]
ax.legend(lines, labels, loc='upper left')
ax.set_title('时间维度分析')
def _plot_movement_trace(self, ax):
"""绘制运动轨迹"""
# 采样部分数据点避免过于密集
sample = self.df.iloc[::50]
ax.plot(sample['x'], sample['y'], 'b-', alpha=0.3, linewidth=1, label='轨迹')
# 标记起始点和终点
ax.plot(self.df['x'].iloc[0], self.df['y'].iloc[0], 'go', markersize=10, label='起始点')
ax.plot(self.df['x'].iloc[-1], self.df['y'].iloc[-1], 'r*', markersize=15, label='终点')
self._draw_field(ax, draw_center=False)
ax.legend(loc='upper right')
ax.set_title('球员移动轨迹')
def _plot_speed_distribution(self, ax):
"""绘制速度分布"""
# 计算连续帧之间的速度和方向
dx = np.diff(self.df['x'].values)
dy = np.diff(self.df['y'].values)
distance = np.sqrt(dx**2 + dy**2)
# 假设采样频率为10Hz (0.1秒每帧)
speed = distance / 0.1
# 过滤明显异常值
speed = speed[speed < 15] # 足球运动员最大速度约10m/s
ax.hist(speed, bins=30, color='skyblue', edgecolor='black', alpha=0.7)
ax.axvline(speed.mean(), color='red', linestyle='--', label=f'平均速度: {speed.mean():.2f} m/s')
ax.axvline(speed.median(), color='green', linestyle='--', label=f'中位数: {speed.median():.2f} m/s')
ax.set_xlabel('速度 (m/s)')
ax.set_ylabel('频次')
ax.set_title('速度分布')
ax.legend()
def _plot_zone_distribution(self, ax):
"""绘制区域分布饼图"""
zone_presence, _ = self.analyze_zone_coverage()
# 分类
categories = {
'前场': 0,
'中场': 0,
'后场': 0
}
for zone, percentage in zone_presence.items():
for key in categories:
if key in zone:
categories[key] += percentage
labels = list(categories.keys())
values = list(categories.values())
colors = ['#FF6B6B', '#4ECDC4', '#45B7D1']
wedges, texts, autotexts = ax.pie(values, labels=labels, colors=colors,
autopct='%1.1f%%', startangle=90)
ax.set_title('区域分布占比')
def _draw_field(self, ax, draw_center=True):
"""绘制球场背景"""
ax.set_xlim(0, self.field_length)
ax.set_ylim(0, self.field_width)
ax.set_aspect('equal')
ax.set_facecolor('lightgreen')
# 绘制边界线
ax.plot([0, 0, 105, 105, 0], [0, 68, 68, 0, 0], 'k-', linewidth=2)
# 中场线
if draw_center:
ax.axhline(y=34, color='white', linestyle='-', alpha=0.5)
ax.axvline(x=52.5, color='white', linestyle='-', alpha=0.5)
# 中圈
circle = plt.Circle((52.5, 34), 9.15, color='white', fill=False, alpha=0.5)
ax.add_patch(circle)
# 禁区
# 左侧禁区
ax.add_patch(plt.Rectangle((0, 13.84), 16.5, 14.32,
facecolor='none', edgecolor='white', alpha=0.5))
ax.add_patch(plt.Rectangle((0, 30.34), 5.5, 7.32,
facecolor='none', edgecolor='white', alpha=0.5))
# 右侧禁区
ax.add_patch(plt.Rectangle((88.5, 13.84), 16.5, 14.32,
facecolor='none', edgecolor='white', alpha=0.5))
ax.add_patch(plt.Rectangle((99.5, 30.34), 5.5, 7.32,
facecolor='none', edgecolor='white', alpha=0.5))
ax.set_xlabel('X坐标 (米)')
ax.set_ylabel('Y坐标 (米)')
# 使用分析器
analyzer = PlayerHeatmapAnalyzer(df)
analyzer.visualize_comprehensive()
交互式可视化
import plotly.graph_objects as go
from plotly.subplots import make_subplots
def interactive_heatmap(df):
"""创建交互式热力图"""
# 计算KDE
x = df['x'].values
y = df['y'].values
kde = gaussian_kde(np.vstack([x, y]), bw_method=0.1)
# 创建网格
xi = np.linspace(0, 105, 200)
yi = np.linspace(0, 68, 200)
xi, yi = np.meshgrid(xi, yi)
positions = np.vstack([xi.ravel(), yi.ravel()])
zi = kde(positions).reshape(xi.shape)
# 创建交互式图
fig = make_subplots(
rows=1, cols=2,
subplot_titles=('2D Heatmap', '3D Surface'),
specs=[[{'type': 'heatmap'}, {'type': 'surface'}]]
)
# 2D热力图
fig.add_trace(
go.Heatmap(
z=zi,
x=xi[0],
y=yi[:, 0],
colorscale='Jet',
colorbar=dict(title='密度'),
hovertemplate='X: %{x:.1f}m<br>Y: %{y:.1f}m<br>密度: %{z:.3f}<extra></extra>'
),
row=1, col=1
)
# 3D曲面图
fig.add_trace(
go.Surface(
x=xi,
y=yi,
z=zi,
colorscale='Jet',
hovertemplate='X: %{x:.1f}m<br>Y: %{y:.1f}m<br>密度: %{z:.3f}<extra></extra>'
),
row=1, col=2
)
# 更新布局
fig.update_layout(
title='球员跑动热点图 - 交互式分析',
height=600,
showlegend=False
)
fig.update_xaxes(title_text='X坐标 (m)', row=1, col=1)
fig.update_yaxes(title_text='Y坐标 (m)', row=1, col=1)
fig.show()
# 运行交互式可视化
interactive_heatmap(df)
输出分析报告
def generate_analysis_report(df):
"""生成分析报告"""
analyzer = PlayerHeatmapAnalyzer(df)
print("=" * 50)
print("球员跑动热力图分析报告")
print("=" * 50)
# 基础统计
print("\n1. 基础统计:")
print(f" - 总数据点数: {len(df)}")
print(f" - 覆盖时间: {df['time'].max():.1f}秒")
# 计算总跑动距离
dx = np.diff(df['x'].values)
dy = np.diff(df['y'].values)
total_distance = np.sum(np.sqrt(dx**2 + dy**2))
print(f" - 总跑动距离: {total_distance:.1f}米")
# 计算平均速度
avg_speed = total_distance / df['time'].max()
print(f" - 平均速度: {avg_speed:.2f}米/秒")
# 区域分析
print("\n2. 区域覆盖分析:")
zone_presence, zone_hotspots = analyzer.analyze_zone_coverage()
for zone, percentage in sorted(zone_presence.items(), key=lambda x: x[1], reverse=True):
print(f" - {zone}: {percentage:.1f}% 的时间")
# 热点位置
print("\n3. 主要热点位置:")
for zone, (x, y) in zone_hotspots.items():
print(f" - {zone}: ({x:.1f}, {y:.1f})")
# 时间模式
print("\n4. 时间模式分析:")
time_stats = analyzer.analyze_time_patterns()
if time_stats:
for time_slot, stats in time_stats.items():
print(f" - {time_slot}: 距离{stats['distance']:.1f}m, "
f"速度{stats['avg_speed']:.2f}m/s")
print("\n5. 分析结论:")
# 判断球员类型
forward_percentage = zone_presence.get('前场中路', 0) + zone_presence.get('前场左路', 0) + zone_presence.get('前场右路', 0)
if forward_percentage > 40:
print(" * 球员倾向于前场活动,可能是前锋或攻击型球员")
elif forward_percentage < 20:
print(" * 球员主要在己方半场活动,可能是防守型球员")
else:
print(" * 球员覆盖全场,活动范围广泛")
# 判断活动模式
hot_zone = max(zone_hotspots.items(), key=lambda x: x[0])
print(f" * 最活跃区域: {hot_zone[0]}")
print("=" * 50)
# 生成报告
generate_analysis_report(df)
完整运行示例
# 完整流程
if __name__ == "__main__":
# 1. 生成数据
print("生成球员跑动数据...")
df = generate_position_data(num_frames=3000, player_speed=8)
# 2. 创建分析器
analyzer = PlayerHeatmapAnalyzer(df)
# 3. 绘制基础热力图
print("绘制基础热力图...")
plot_basic_heatmap(df)
# 4. 进行综合分析
print("进行综合分析...")
analyzer.visualize_comprehensive()
# 5. 交互式可视化
print("创建交互式可视化...")
interactive_heatmap(df)
# 6. 生成报告
generate_analysis_report(df)
这个完整的Python案例涵盖了:
- 数据准备和模拟
- 基础热力图绘制
- 高级区域分析
- 时间序列分析
- 速度分析
- 交互式可视化
- 综合分析报告
你可以根据实际数据调整参数(如球场尺寸、采样频率等),或者添加更多自定义的分析功能。
版权声明:NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播