在 matplotlib 中绘制棒球场和喷洒图:baseball-field-viz
Source: Dev.to
请提供您希望翻译的具体文本内容(除代码块和 URL 之外),我将为您翻译成简体中文并保持原有的 Markdown 格式。
背景
pybaseball 的内置 spraychart() 很方便,但它不支持叠加热图——这使得按区域可视化击球密度变得困难。
要在棒球场上使用 seaborn 的 kdeplot 或 histplot,必须手动在 Matplotlib 中绘制场地。每次都编写坐标转换和场地绘制代码很繁琐,所以我把它封装成了一个包。
pip install baseball-field-viz
PyPI:
GitHub:
baseball-field-viz 提供的功能
用于 Statcast 可视化的三个函数:
from baseball_field_viz import transform_coords, draw_field, spraychart
| 函数 | 描述 |
|---|---|
transform_coords(df) | 将 Statcast 的 hc_x/hc_y 转换为英尺(本垒板为原点) |
draw_field(ax) | 在 Matplotlib 的 Axes 上绘制棒球场 |
spraychart(ax, df, color_by='events') | 将上述两步合并为一行代码的函数 |
使用
快速开始
import matplotlib.pyplot as plt
from baseball_field_viz import spraychart
fig, ax = plt.subplots(figsize=(10, 10))
spraychart(ax, df, color_by='events', title='Player — Batted Balls')
plt.show()
使用 color_by='events' 时:
- 本垒打 → 红色
- 三垒打 → 橙色
- 二垒打 → 蓝色
- 一垒打 → 绿色
叠加热图(关键优势)
由于 draw_field 返回的是 Axes 对象,你可以在其上层叠任意 Matplotlib/Seaborn 绘图:
import seaborn as sns
from baseball_field_viz import draw_field, transform_coords
df_t = transform_coords(df[df['hc_x'].notna()])
hits = df_t[df_t['events'].isin(['home_run', 'double', 'triple', 'single'])]
outs = df_t[~df_t['events'].isin(['home_run', 'double', 'triple', 'single'])]
fig, axs = plt.subplots(1, 2, figsize=(16, 8))
# 命中热图
draw_field(axs[0])
sns.kdeplot(data=hits, x='x', y='y', ax=axs[0],
cmap='Reds', fill=True, alpha=0.6)
axs[0].set_xlim(-350, 350)
axs[0].set_ylim(-50, 400)
axs[0].set_title('Hits Heatmap')
# 出局热图
draw_field(axs[1])
sns.kdeplot(data=outs, x='x', y='y', ax=axs[1],
cmap='Blues', fill=True, alpha=0.6)
axs[1].set_xlim(-350, 350)
axs[1].set_ylim(-50, 400)
axs[1].set_title('Outs Heatmap')
plt.tight_layout()
plt.show()
应用于 WBC 2026 队员名单
我使用该库在 Kaggle 上发布了一个笔记本,数据来源于 WBC 2026 侦察数据集——包括所有 18 个国家的 WBC 2026 队员名单对应的 MLB 常规赛 Statcast 数据(2024‑2025 年)。 (注意:这并非 WBC 比赛数据,而是针对有资格参加 WBC 的球员的 MLB 数据。)
Kaggle Notebook:
所有 18 个国家 — 总览喷射图
使用 draw_field + 按国家的散点图,并采用 tab20 颜色映射:
from baseball_field_viz import draw_field, transform_coords
import matplotlib.cm as cm
import numpy as np
hit_events = ['home_run', 'double', 'triple', 'single']
hits = transform_coords(df[df['hc_x'].notna() & df['events'].isin(hit_events)])
country_list = sorted(hits['country_name'].unique())
colors = cm.tab20(np.linspace(0, 1, len(country_list)))
color_map = dict(zip(country_list, colors))
fig, ax = plt.subplots(figsize=(12, 12))
draw_field(ax)
for country in country_list:
subset = hits[hits['country_name'] == country]
ax.scatter(subset['x'], subset['y'],
c=[color_map[country]], alpha=0.35, s=12,
label=f"{country} ({len(subset)})")
ax.legend(loc='upper right', fontsize=8, ncol=2)
plt.show()
前 4 大国家对比
spraychart() 让按国家划分的网格绘制变得非常简便:
top_countries = ['USA', 'Dominican Republic', 'Venezuela', 'Japan']
fig, axs = plt.subplots(2, 2, figsize=(16, 14))
for ax, country in zip(axs.flat, top_countries):
df_c = df[df['country_name'] == country]
spraychart(ax, df_c, color_by='events', title=country)
plt.tight_layout()
plt.show()
日本 — 命中与出局热力图
KDE 覆盖层展示了散点图难以呈现的区域级趋势:
df_jpn_t = transform_coords(df[df['country_name'] == 'Japan'][df['hc_x'].notna()])
hits_jpn = df_jpn_t[df_jpn_t['events'].isin(hit_events)]
outs_jpn = df_jpn_t[~df_jpn_t['events'].isin(hit_events)]
fig, axs = plt.subplots(1, 2, figsize=(16, 8))
draw_field(axs[0])
sns.kdeplot(data=hits_jpn, x='x', y='y', ax=axs[0],
cmap='Reds', fill=True, alpha=0.6)
draw_field(axs[1])
sns.kdeplot(data=outs_jpn, x='x', y='y', ax=axs[1],
cmap='Blues', fill=True, alpha=0.6)
plt.show()
WBC 2026 侦察仪表盘
该数据集还可驱动交互式仪表盘:
Dashboard:
展示所有 18 个国家的球员击球和投球统计,均基于同一批 Statcast 数据构建。
v0.2.0 – Strike Zone 支持
版本 0.2.0 添加了两个新函数:
| 函数 | 描述 |
|---|---|
draw_strike_zone(ax, sz_top=3.5, sz_bot=1.5) | 在 plate_x/plate_z 坐标中绘制 strike‑zone 矩形 |
pitch_zone_chart(ax, df, color_by='pitch_type') | 绘制 pitch 位置,并自动调整大小的 strike‑zone 覆盖层 |
好球区
from pybaseball import statcast_pitcher
from baseball_field_viz import pitch_zone_chart
import matplotlib.pyplot as plt
df = statcast_pitcher('2025-03-01', '2025-10-31', 592789) # Yoshinobu Yamamoto
fig, ax = plt.subplots(figsize=(6, 6))
pitch_zone_chart(ax, df, color_by='pitch_type',
title='Yamamoto 2025 — Pitch Locations')
plt.show()
Statcast 包含每次投球的 sz_top/sz_bot 列(由 Hawk‑Eye 测量,而非基于身高推算)。
如果 DataFrame 中存在这些列,pitch_zone_chart 会自动使用它们的平均值——因此好球区反映每位击球手的实际站姿,而不是估计值。
安装
pip install baseball-field-viz
要求
- Python 3.9+
- matplotlib ≥ 3.5
- numpy ≥ 1.21
- pandas ≥ 1.3
摘要
draw_field(ax)+spraychart()替代冗余的 Statcast 可视化代码。- 直接访问
Axes使得可以叠加热图,这在 pybaseball 的spraychart()中无法实现。 - 已使用 2026 年 WBC Statcast 数据在 18 个国家进行测试。
PyPI:
GitHub: