在 matplotlib 中绘制棒球场和喷洒图:baseball-field-viz

发布: (2026年2月23日 GMT+8 06:52)
5 分钟阅读
原文: Dev.to

Source: Dev.to

请提供您希望翻译的具体文本内容(除代码块和 URL 之外),我将为您翻译成简体中文并保持原有的 Markdown 格式。

背景

pybaseball 的内置 spraychart() 很方便,但它不支持叠加热图——这使得按区域可视化击球密度变得困难。

要在棒球场上使用 seaborn 的 kdeplothistplot,必须手动在 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:

0 浏览
Back to Blog

相关文章

阅读更多 »