記事のシリーズ
- pybaseball基礎編① pybaseballのライブラリの概要とインストール
- pybaseball基礎編② シーズン単位のデータ取得
- pybaseball基礎編③ 試合単位のデータ取得【本記事】
- pybaseball基礎編④ 打席単位のデータ取得(予定)
- pybaseball基礎編⑤ 1球単位のデータ取得(予定)
- pybaseball基礎編⑥ 打球単位のデータ取得(予定)
- pybaseball基礎編⑦ 投球単位のデータ取得(予定)
はじめに
前回の「pybaseball基礎編②」では、シーズン単位でMLBのStatcastデータを取得する方法を紹介しました。
今回は一段階細かくして、1試合単位でデータを取得し、pandasを使って試合内容を集計する方法を解説します。
この記事では、次の流れを実践します。
- 特定日のStatcastデータを取得する
- チームを指定して対象試合を絞り込む
game_pkを確認するstatcast_single_game()で1試合のデータを取得する- 1試合の投球数や球種を集計する
- 打球速度や打球角度を調べる
- 大谷翔平選手の打席・打球を確認する
1. 試合単位のデータとは?
Statcastの元データは、基本的に1球につき1行という投球単位のデータです。
イメージとしては、
シーズン
↓
試合
↓
打席
↓
投球
↓
打球
という階層になっています。
そのため、statcast_single_game()で1試合を指定しても、返ってくるDataFrameが「1試合=1行」になるわけではありません。
1試合分の投球データを取得し、それをpandasで集計することで、
- 総投球数
- イニング別投球数
- 球種別投球数
- 平均球速
- 打球数
- 最大打球速度
などの「試合単位」の指標を作ります。
2. pybaseballとStatcastの関係
データの流れを整理すると、
MLB
↓
Statcast
↓
Baseball Savant
↓
pybaseball
↓
pandas DataFrame
↓
Pythonで分析
というイメージです。
StatcastはMLBのトラッキングシステムによって取得されるデータです。
Baseball Savantでは、MLBのStatcast関連データを検索・閲覧できます。
pybaseballを使うと、これらのデータをPythonから取得して、pandasのDataFrameとして扱えます。
つまり、pybaseballそのものが野球データベースというわけではなく、野球データをPythonから扱いやすくするためのアクセス手段と考えると分かりやすいです。
3. 特定日のStatcastデータを取得する
まず、2025年6月16日のデータを取得します。
from pybaseball import statcast
data = statcast("2025-06-16", "2025-06-16")
display(data.head())
print("行数:", len(data))
print("列数:", len(data.columns))
出力結果
| index | pitch_type | game_date | release_speed | release_pos_x | release_pos_z | player_name | batter | pitcher | events | description | spin_dir | spin_rate_deprecated | break_angle_deprecated | break_length_deprecated | zone | des | game_type | stand | p_throws | home_team |
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| 1453 | FF | 2025-06-16 00:00:00 | 93.5 | -2.44 | 5.35 | Yates, Kirby | 642180 | 489446 | field_out | hit_into_play | <NA> | <NA> | <NA> | <NA> | 2 | Tyler Wade pops out to third baseman Max Muncy. | R | L | R | LAD |
| 1463 | FS | 2025-06-16 00:00:00 | 86.7 | -2.47 | 5.17 | Yates, Kirby | 642180 | 489446 | NaN | called_strike | <NA> | <NA> | <NA> | <NA> | 13 | NaN | R | L | R | LAD |
| 1541 | FF | 2025-06-16 00:00:00 | 93.0 | -2.46 | 5.21 | Yates, Kirby | 642180 | 489446 | NaN | foul | <NA> | <NA> | <NA> | <NA> | 3 | NaN | R | L | R | LAD |
| 1561 | FS | 2025-06-16 00:00:00 | 85.9 | -2.39 | 5.18 | Yates, Kirby | 578428 | 489446 | field_out | hit_into_play | <NA> | <NA> | <NA> | <NA> | 9 | Jose Iglesias lines out to shortstop Mookie Betts. | R | R | R | LAD |
| 1635 | FF | 2025-06-16 00:00:00 | 93.4 | -2.35 | 5.38 | Yates, Kirby | 578428 | 489446 | NaN | foul | <NA> | <NA> | <NA> | <NA> | 11 | NaN | R | R | R | LAD |
行数: 1960
列数: 119
取得したデータは、基本的に1球単位です。
例えば、次のように主要な列を確認できます。
display(
data[
[
"game_date",
"game_pk",
"inning",
"pitcher",
"batter",
"pitch_name",
"release_speed"
]
].head()
)
出力結果
| index | game_date | game_pk | inning | pitcher | batter | pitch_name | release_speed |
|---|---|---|---|---|---|---|---|
| 1453 | 2025-06-16 00:00:00 | 777490 | 9 | 489446 | 642180 | 4-Seam Fastball | 93.5 |
| 1463 | 2025-06-16 00:00:00 | 777490 | 9 | 489446 | 642180 | Split-Finger | 86.7 |
| 1541 | 2025-06-16 00:00:00 | 777490 | 9 | 489446 | 642180 | 4-Seam Fastball | 93.0 |
| 1561 | 2025-06-16 00:00:00 | 777490 | 9 | 489446 | 578428 | Split-Finger | 85.9 |
| 1635 | 2025-06-16 00:00:00 | 777490 | 9 | 489446 | 578428 | 4-Seam Fastball | 93.4 |
4. チームを指定して対象試合を絞り込む
特定チームだけを分析したい場合は、teamを指定できます。
from pybaseball import statcast
dodgers_data = statcast(
"2025-06-16",
"2025-06-16",
team="LAD"
)
display(dodgers_data.head())
print("行数:", len(dodgers_data))
出力結果
| index | pitch_type | game_date | release_speed | release_pos_x | release_pos_z | player_name | batter | pitcher | events | description | spin_dir | spin_rate_deprecated | break_angle_deprecated | break_length_deprecated | zone | des | game_type | stand | p_throws | home_team |
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| 82 | FF | 2025-06-16 00:00:00 | 93.5 | -2.44 | 5.35 | Yates, Kirby | 642180 | 489446 | field_out | hit_into_play | <NA> | <NA> | <NA> | <NA> | 2 | Tyler Wade pops out to third baseman Max Muncy. | R | L | R | LAD |
| 87 | FS | 2025-06-16 00:00:00 | 86.7 | -2.47 | 5.17 | Yates, Kirby | 642180 | 489446 | NaN | called_strike | <NA> | <NA> | <NA> | <NA> | 13 | NaN | R | L | R | LAD |
| 97 | FF | 2025-06-16 00:00:00 | 93.0 | -2.46 | 5.21 | Yates, Kirby | 642180 | 489446 | NaN | foul | <NA> | <NA> | <NA> | <NA> | 3 | NaN | R | L | R | LAD |
| 98 | FS | 2025-06-16 00:00:00 | 85.9 | -2.39 | 5.18 | Yates, Kirby | 578428 | 489446 | field_out | hit_into_play | <NA> | <NA> | <NA> | <NA> | 9 | Jose Iglesias lines out to shortstop Mookie Betts. | R | R | R | LAD |
| 109 | FF | 2025-06-16 00:00:00 | 93.4 | -2.35 | 5.38 | Yates, Kirby | 578428 | 489446 | NaN | foul | <NA> | <NA> | <NA> | <NA> | 11 | NaN | R | R | R | LAD |
行数: 147
このように、まず対象日とチームを絞り込むと、その日の対象試合を探しやすくなります。
5. game_pkとは?
game_pkは、MLBの試合を識別するためのIDです。
試合単位でデータを取得するときに重要になります。
まず対象試合を一覧にします。
games = (
dodgers_data[
[
"game_date",
"game_pk",
"away_team",
"home_team"
]
]
.drop_duplicates()
.sort_values("game_pk")
)
display(games)
出力結果
| index | game_date | game_pk | away_team | home_team |
|---|---|---|---|---|
| 82 | 2025-06-16 00:00:00 | 777490 | SD | LAD |
ここで対象試合とgame_pkを確認できます。
game_pkは手作業で適当に入力するのではなく、まず取得したStatcastデータから確認するのがおすすめです。
6. statcast_single_game()で1試合を取得する
pybaseballには、特定の1試合のStatcastデータを取得するstatcast_single_game()があります。
from pybaseball import statcast_single_game
game_data = statcast_single_game(game_pk=777490)
display(game_data.head())
出力結果
| index | pitch_type | game_date | release_speed | release_pos_x | release_pos_z | player_name | batter | pitcher | events | description | spin_dir | spin_rate_deprecated | break_angle_deprecated | break_length_deprecated | zone | des | game_type | stand | p_throws | home_team |
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| 206 | FF | 2025-06-16 00:00:00 | 93.5 | -2.44 | 5.35 | Wade, Tyler | 642180 | 489446 | field_out | hit_into_play | NaN | NaN | NaN | NaN | 2 | Tyler Wade pops out to third baseman Max Muncy. | R | L | R | LAD |
| 217 | FS | 2025-06-16 00:00:00 | 86.7 | -2.47 | 5.17 | Wade, Tyler | 642180 | 489446 | NaN | called_strike | NaN | NaN | NaN | NaN | 13 | NaN | R | L | R | LAD |
| 219 | FF | 2025-06-16 00:00:00 | 93.0 | -2.46 | 5.21 | Wade, Tyler | 642180 | 489446 | NaN | foul | NaN | NaN | NaN | NaN | 3 | NaN | R | L | R | LAD |
| 227 | FS | 2025-06-16 00:00:00 | 85.9 | -2.39 | 5.18 | Iglesias, Jose | 578428 | 489446 | field_out | hit_into_play | NaN | NaN | NaN | NaN | 9 | Jose Iglesias lines out to shortstop Mookie Betts. | R | R | R | LAD |
| 237 | FF | 2025-06-16 00:00:00 | 93.4 | -2.35 | 5.38 | Iglesias, Jose | 578428 | 489446 | NaN | foul | NaN | NaN | NaN | NaN | 11 | NaN | R | R | R | LAD |
1試合
↓
多数の投球
↓
多数のDataFrame行
という構造です。
7. 取得した試合の基本情報を確認する
display(
game_data[
[
"game_date",
"game_pk",
"away_team",
"home_team"
]
].drop_duplicates()
)
print("投球数:", len(game_data))
出力結果
| index | game_date | game_pk | away_team | home_team |
|---|---|---|---|---|
| 206 | 2025-06-16 00:00:00 | 777490 | SD | LAD |
投球数: 297
これで、試合日、対戦カード、試合ID、投球数を確認できます。
8. 試合単位の基本サマリーを作る
投球単位のDataFrameを、試合単位の情報に集約してみます。
import pandas as pd
game_summary = pd.DataFrame({
"項目": [
"投球数",
"打球数",
"平均球速(mph)",
"平均打球速度(mph)"
],
"値": [
len(game_data),
game_data["launch_speed"].notna().sum(),
game_data["release_speed"].mean(),
game_data.loc[
game_data["launch_speed"].notna(),
"launch_speed"
].mean()
]
})
display(game_summary)
出力結果
| index | 項目 | 値 |
|---|---|---|
| 0 | 投球数 | 297.0 |
| 1 | 打球数 | 104.0 |
| 2 | 平均球速(mph) | 92.74410774410775 |
| 3 | 平均打球速度(mph) | 82.81346153846152 |
ここが「試合単位の分析」の基本です。
9. イニング別の投球数を調べる
inning_counts = (
game_data
.groupby("inning")
.size()
.reset_index(name="pitches")
)
display(inning_counts)
これで、どのイニングに投球が集中していたのかを確認できます。
10. 球種別の投球数を調べる
pitch_counts = (
game_data
.groupby("pitch_name")
.size()
.reset_index(name="pitches")
.sort_values("pitches", ascending=False)
)
pitch_counts["usage_percent"] = (
pitch_counts["pitches"]
/ pitch_counts["pitches"].sum()
* 100
).round(1)
display(pitch_counts)
出力結果
| index | pitch_name | pitches | usage_percent |
|---|---|---|---|
| 0 | 4-Seam Fastball | 136 | 45.8 |
| 6 | Slider | 65 | 21.9 |
| 8 | Sweeper | 36 | 12.1 |
| 5 | Sinker | 24 | 8.1 |
| 2 | Curveball | 13 | 4.4 |
| 3 | Cutter | 8 | 2.7 |
| 1 | Changeup | 6 | 2.0 |
| 4 | Knuckle Curve | 5 | 1.7 |
| 7 | Split-Finger | 4 | 1.3 |
これで、試合中にどの球種が何球投げられたのかを確認できます。
11. 投手ごとの平均球速を調べる
pitcher_speed = (
game_data
.groupby("pitcher")
.agg(
pitches=("release_speed", "count"),
avg_speed=("release_speed", "mean")
)
.reset_index()
.sort_values("avg_speed", ascending=False)
)
from pybaseball import playerid_reverse_lookup
pitcher_ids = (
pitcher_speed["pitcher"]
.dropna()
.astype(int)
.tolist()
)
pitcher_names = playerid_reverse_lookup(
pitcher_ids,
key_type="mlbam"
)
pitcher_names = pitcher_names[
["key_mlbam", "name_first", "name_last"]
]
pitcher_names["player_name"] = (
pitcher_names["name_last"]
+ ", "
+ pitcher_names["name_first"]
)
pitcher_speed = pitcher_speed.merge(
pitcher_names[
["key_mlbam", "player_name"]
],
left_on="pitcher",
right_on="key_mlbam",
how="left"
)
pitcher_speed = pitcher_speed[
[
"pitcher",
"player_name",
"pitches",
"avg_speed"
]
]
display(pitcher_speed)
出力結果
| index | pitcher | player_name | pitches | avg_speed |
|---|---|---|---|---|
| 0 | 656629 | kopech, michael | 11 | 96.89090909090909 |
| 1 | 656945 | scott, tanner | 7 | 94.92857142857143 |
| 2 | 656302 | cease, dylan | 101 | 94.02574257425744 |
| 3 | 660271 | ohtani, shohei | 28 | 93.87857142857142 |
| 4 | 607455 | banda, anthony | 21 | 92.66190476190476 |
| 5 | 681911 | vesia, alex | 8 | 91.9875 |
| 6 | 593974 | peralta, wandy | 19 | 91.93684210526315 |
| 7 | 489446 | yates, kirby | 10 | 91.2 |
| 8 | 688158 | morgan, david | 30 | 91.12666666666668 |
| 9 | 676508 | casparius, ben | 62 | 90.56612903225808 |
投球数と平均球速を一緒に見ることで、試合全体の平均よりも具体的な情報を得られます。
12. 4シームの球速を調べる
4シーム・ファストボールは一般的にFFとして記録されます。
four_seam = game_data[
game_data["pitch_type"] == "FF"
].copy()
four_seam_speed = (
four_seam
.groupby(["pitcher"], dropna=False)
.agg(
pitches=("release_speed", "count"),
avg_speed=("release_speed", "mean"),
max_speed=("release_speed", "max")
)
.reset_index()
.sort_values("avg_speed", ascending=False)
)
from pybaseball import playerid_reverse_lookup
pitcher_ids = (
pitcher_speed["pitcher"]
.dropna()
.astype(int)
.tolist()
)
pitcher_names = playerid_reverse_lookup(
pitcher_ids,
key_type="mlbam"
)
pitcher_names = pitcher_names[
["key_mlbam", "name_first", "name_last"]
]
pitcher_names["player_name"] = (
pitcher_names["name_last"]
+ ", "
+ pitcher_names["name_first"]
)
pitcher_speed = four_seam_speed.merge(
pitcher_names[
["key_mlbam", "player_name"]
],
left_on="pitcher",
right_on="key_mlbam",
how="left"
)
pitcher_speed = pitcher_speed[
[
"pitcher",
"player_name",
"pitches",
"avg_speed"
]
]
display(pitcher_speed)
出力結果
| index | pitcher | player_name | pitches | avg_speed |
|---|---|---|---|---|
| 0 | 660271 | ohtani, shohei | 9 | 99.05555555555556 |
| 1 | 656302 | cease, dylan | 49 | 98.65306122448979 |
| 2 | 656629 | kopech, michael | 9 | 97.6888888888889 |
| 3 | 656945 | scott, tanner | 5 | 97.26 |
| 4 | 688158 | morgan, david | 13 | 96.94615384615385 |
| 5 | 607455 | banda, anthony | 8 | 96.6375 |
| 6 | 676508 | casparius, ben | 29 | 96.23103448275862 |
| 7 | 489446 | yates, kirby | 7 | 93.34285714285714 |
| 8 | 681911 | vesia, alex | 7 | 93.10000000000001 |
13. 打球だけを取り出す
launch_speedが記録されている行を取り出します。
batted_balls = game_data[
game_data["launch_speed"].notna()
].copy()
display(
batted_balls[
[
"player_name",
"events",
"launch_speed",
"launch_angle",
"hit_distance_sc"
]
]
)
出力結果
| index | player_name | events | launch_speed | launch_angle | hit_distance_sc |
|---|---|---|---|---|---|
| 203 | Wade, Tyler | field_out | 76.0 | 57.0 | 182.0 |
| 221 | Wade, Tyler | NaN | 59.8 | 48.0 | 159.0 |
| 235 | Iglesias, Jose | field_out | 98.5 | 5.0 | 167.0 |
| 237 | Iglesias, Jose | NaN | 81.0 | 43.0 | 262.0 |
| 255 | Iglesias, Jose | NaN | 78.4 | 53.0 | 219.0 |
| 257 | Iglesias, Jose | NaN | 66.6 | 48.0 | 189.0 |
ここでは、
launch_speed:打球速度launch_angle:打球角度hit_distance_sc:打球飛距離
などを確認できます。
14. 最も速い打球を調べる
hardest_hit = (
batted_balls
.sort_values("launch_speed", ascending=False)
)
display(
hardest_hit[
[
"player_name",
"events",
"launch_speed",
"launch_angle",
"hit_distance_sc"
]
].head(10)
)
出力結果
| index | player_name | events | launch_speed | launch_angle | hit_distance_sc |
|---|---|---|---|---|---|
| 16 | Hernández, Teoscar | field_out | 110.3 | -3.0 | 25.0 |
| 41 | Muncy, Max | single | 108.2 | 17.0 | 309.0 |
| 275 | Machado, Manny | field_out | 108.1 | -7.0 | 14.0 |
| 32 | Edman, Tommy | single | 105.8 | 16.0 | 252.0 |
| 78 | Edman, Tommy | field_out | 105.7 | -17.0 | 5.0 |
| 74 | Ohtani, Shohei | double | 105.2 | 17.0 | 337.0 |
| 155 | Pages, Andy | force_out | 104.1 | -8.0 | 13.0 |
| 185 | Smith, Will | single | 102.0 | 2.0 | 66.0 |
| 152 | Conforto, Michael | NaN | 100.2 | -3.0 | 34.0 |
| 99 | Bogaerts, Xander | double | 100.1 | 17.0 | 319.0 |
これで、その試合で最も速い打球を確認できます。
15. 大谷翔平選手のデータだけを取り出す
大谷翔平選手のMLBAM IDは660271です。
OHTANI_ID = 660271
ohtani_data = game_data[
game_data["batter"].astype(str) == str(OHTANI_ID)
].copy()
display(ohtani_data.head())
出力結果
| index | pitch_type | game_date | release_speed | release_pos_x | release_pos_z | player_name | batter | pitcher | events | description | spin_dir | spin_rate_deprecated | break_angle_deprecated | break_length_deprecated | zone | des | game_type | stand | p_throws | home_team |
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| 108 | SL | 2025-06-16 00:00:00 | 87.1 | -2.38 | 5.28 | Ohtani, Shohei | 660271 | 688158 | walk | ball | NaN | NaN | NaN | NaN | 13 | Shohei Ohtani walks. | R | L | R | LAD |
| 109 | CU | 2025-06-16 00:00:00 | 84.1 | -1.71 | 5.59 | Ohtani, Shohei | 660271 | 688158 | NaN | ball | NaN | NaN | NaN | NaN | 14 | NaN | R | L | R | LAD |
| 113 | FF | 2025-06-16 00:00:00 | 97.3 | -2.06 | 5.44 | Ohtani, Shohei | 660271 | 688158 | NaN | ball | NaN | NaN | NaN | NaN | 11 | NaN | R | L | R | LAD |
| 119 | FF | 2025-06-16 00:00:00 | 96.4 | -2.06 | 5.43 | Ohtani, Shohei | 660271 | 688158 | NaN | ball | NaN | NaN | NaN | NaN | 11 | NaN | R | L | R | LAD |
| 122 | CU | 2025-06-16 00:00:00 | 83.2 | -1.84 | 5.58 | Ohtani, Shohei | 660271 | 688158 | NaN | swinging_strike | NaN | NaN | NaN | NaN | 14 | NaN | R | L | R | LAD |
これで、その試合における大谷翔平選手の打席に関連する投球データを抽出できます。
16. 大谷翔平選手が見た球を確認する
ohtani_pitches = (
ohtani_data[
[
"game_date",
"inning",
"at_bat_number",
"pitch_number",
"pitch_name",
"release_speed",
"events",
"description"
]
]
.sort_values(
["at_bat_number", "pitch_number"]
)
)
display(ohtani_pitches)
出力結果
| index | game_date | inning | at_bat_number | pitch_number | pitch_name | release_speed | events | description |
|---|---|---|---|---|---|---|---|---|
| 291 | 2025-06-16 00:00:00 | 1 | 6 | 1 | Slider | 89.0 | NaN | ball |
| 277 | 2025-06-16 00:00:00 | 1 | 6 | 2 | 4-Seam Fastball | 99.5 | NaN | called_strike |
| 271 | 2025-06-16 00:00:00 | 1 | 6 | 3 | Slider | 90.6 | NaN | called_strike |
| 261 | 2025-06-16 00:00:00 | 1 | 6 | 4 | Slider | 90.4 | NaN | ball |
| 250 | 2025-06-16 00:00:00 | 1 | 6 | 5 | Slider | 91.4 | NaN | foul |
| 241 | 2025-06-16 00:00:00 | 1 | 6 | 6 | Slider | 91.3 | NaN | ball |
| 230 | 2025-06-16 00:00:00 | 1 | 6 | 7 | Slider | 92.4 | strikeout | swinging_strike |
| 76 | 2025-06-16 00:00:00 | 3 | 22 | 1 | Slider | 92.1 | NaN | blocked_ball |
| 74 | 2025-06-16 00:00:00 | 3 | 22 | 2 | Slider | 91.5 | double | hit_into_play |
| 31 | 2025-06-16 00:00:00 | 4 | 36 | 1 | 4-Seam Fastball | 99.5 | NaN | foul |
| 30 | 2025-06-16 00:00:00 | 4 | 36 | 2 | 4-Seam Fastball | 98.3 | single | hit_into_play |
| 213 | 2025-06-16 00:00:00 | 6 | 53 | 1 | Slider | 88.7 | NaN | swinging_strike |
| 209 | 2025-06-16 00:00:00 | 6 | 53 | 2 | Sinker | 96.1 | NaN | called_strike |
| 195 | 2025-06-16 00:00:00 | 6 | 53 | 3 | Slider | 88.8 | NaN | blocked_ball |
| 189 | 2025-06-16 00:00:00 | 6 | 53 | 4 | Sinker | 95.4 | strikeout | swinging_strike |
| 122 | 2025-06-16 00:00:00 | 8 | 69 | 1 | Curveball | 83.2 | NaN | swinging_strike |
| 119 | 2025-06-16 00:00:00 | 8 | 69 | 2 | 4-Seam Fastball | 96.4 | NaN | ball |
| 113 | 2025-06-16 00:00:00 | 8 | 69 | 3 | 4-Seam Fastball | 97.3 | NaN | ball |
| 109 | 2025-06-16 00:00:00 | 8 | 69 | 4 | Curveball | 84.1 | NaN | ball |
| 108 | 2025-06-16 00:00:00 | 8 | 69 | 5 | Slider | 87.1 | walk | ball |
これを見ると、何回の打席で何球目にどの球種が投げられたか、その結果どうなったかを確認できます。
17. 大谷翔平選手への球種割合を調べる
ohtani_pitch_usage = (
ohtani_data
.groupby("pitch_name")
.size()
.reset_index(name="pitches")
.sort_values("pitches", ascending=False)
)
ohtani_pitch_usage["usage_percent"] = (
ohtani_pitch_usage["pitches"]
/ ohtani_pitch_usage["pitches"].sum()
* 100
).round(1)
display(ohtani_pitch_usage)
出力結果
| index | pitch_name | pitches | usage_percent |
|---|---|---|---|
| 3 | Slider | 11 | 55.0 |
| 0 | 4-Seam Fastball | 5 | 25.0 |
| 1 | Curveball | 2 | 10.0 |
| 2 | Sinker | 2 | 10.0 |
複数試合を比較すれば、相手投手によって球種配分が変わったか、といった分析にも発展できます。
18. 大谷翔平選手の打球を調べる
ohtani_batted_balls = ohtani_data[
ohtani_data["launch_speed"].notna()
].copy()
display(
ohtani_batted_balls[
[
"events",
"pitch_name",
"release_speed",
"launch_speed",
"launch_angle",
"hit_distance_sc"
]
]
)
出力結果
| index | events | pitch_name | release_speed | launch_speed | launch_angle | hit_distance_sc |
|---|---|---|---|---|---|---|
| 30 | single | 4-Seam Fastball | 98.3 | 87.9 | 25.0 | 274.0 |
| 31 | NaN | 4-Seam Fastball | 99.5 | 81.5 | 69.0 | 163.0 |
| 74 | double | Slider | 91.5 | 105.2 | 17.0 | 337.0 |
| 250 | NaN | Slider | 91.4 | 77.2 | -32.0 | 4.0 |
これで、どの球種を打ったのか、その球の球速、打球速度、打球角度、飛距離、打撃結果などを確認できます。
19. 試合単位のデータを取得する基本テンプレート
from pybaseball import statcast, statcast_single_game
DATE = "2025-06-16"
TEAM = "LAD"
daily_data = statcast(
DATE,
DATE,
team=TEAM
)
games = (
daily_data[
[
"game_date",
"game_pk",
"away_team",
"home_team"
]
]
.drop_duplicates()
)
display(games)
GAME_PK = games.iloc[0]["game_pk"]
game_data = statcast_single_game(GAME_PK)
display(
game_data[
[
"game_date",
"game_pk",
"away_team",
"home_team"
]
].drop_duplicates()
)
print("投球数:", len(game_data))
この形を覚えておけば、別の日の試合を分析するときにも応用できます。
20. まとめ
今回はpybaseballを使って、MLBの1試合分のStatcastデータを取得する方法を紹介しました。
試合情報
├─ game_pk
├─ 対戦チーム
└─ 試合日
投球情報
├─ 総投球数
├─ イニング別投球数
├─ 球種別投球数
└─ 球速
打球情報
├─ 打球数
├─ 打球速度
├─ 打球角度
└─ 飛距離
選手別分析
└─ 大谷翔平選手の打席・打球データ
statcast_single_game()を使うことで、1試合を丸ごと取得して、そこから必要な指標を作るという分析ができます。
ポイントは、
日付・チームを指定
↓
game_pkを確認
↓
statcast_single_game()
↓
1試合分の投球データ
↓
pandasで集計
↓
試合単位の分析
という流れです。
21. 次回の記事
次回の「pybaseball基礎編④」では、今回取得した1試合のデータをさらに細かく分解して、打席単位の分析に進みます。
例えば、
- 1打席で何球投げられたか
- 初球は何だったか
- 何球目で結果が出たか
- どの球種で打席結果が変わったか
- 大谷翔平選手の1打席をどう分析するか
といった内容を扱います。
データ参照元
本記事では、MLBのStatcastデータをpybaseballから取得して分析しています。
pybaseballは、Statcastを含む野球データをPythonから扱いやすくするためのライブラリです。
実際にデータを利用・公開する場合は、利用元の最新の規約やデータ提供条件も確認してください。
動作環境
本記事ではGoogle Colabを利用しています。
Pythonとpybaseballのバージョンは、以下のコードで確認できます。
import sys
import pybaseball
print("Python:", sys.version)
print("pybaseball:", pybaseball.__version__)
出力結果
Python: 3.13.15 (main, Aug 6 2026, 11:06:22) [GCC 13.3.0]
pybaseball: 2.2.7