記事のシリーズ
- pybaseball基礎編① pybaseballのライブラリの概要とインストール
- pybaseball基礎編② シーズン単位のデータ取得
- pybaseball基礎編③ 試合単位のデータ取得
- pybaseball基礎編④ 打席単位のデータ取得
- pybaseball基礎編⑤ 1球単位のデータ取得
- pybaseball基礎編⑥ 打球単位のデータ取得【本記事】
- pybaseball基礎編⑦ 投球単位のデータ取得(予定)
はじめに
前回はStatcastの1球単位のデータを取り上げ、球種、球速、カウント、description などを確認しました。
今回はそこから一歩進み、バットに当たって打球になったデータだけを取り出す方法を解説します。
Statcastでは、投球データの中に打球に関する情報も記録されています。代表的な項目が次の4つです。
launch_speed:打球速度(Exit Velocity)launch_angle:打球角度(Launch Angle)hit_distance_sc:打球距離bb_type:ゴロ、ライナー、フライなどの打球タイプ
これらを使うと、「ヒットだったか」だけではなく、どのようなコンタクトだったのかを分析できます。
この記事ではGoogle Colabを使い、pybaseball とpandasで打球データを抽出し、基本的な集計まで進めます。
1. 打球単位とは?
基礎編⑤では、
1行 = 1球
としてStatcastデータを見ました。
今回も元になるDataFrameは同じですが、その中から打球に関係する行だけを抽出します。
イメージは次のとおりです。
Statcastデータ
↓
1球 = 1行
↓
打球が記録された行を抽出
↓
打球速度・角度・距離・タイプを分析
例えば4球で三振になった打席には4球分の行がありますが、打球は発生していません。
一方、最後の球がヒットになった打席では、その球の行に打球データが記録されます。
2. Statcastデータを取得する
まずは1日分のデータを取得します。
from pybaseball import statcast
game_data = statcast(
start_dt="2025-06-16",
end_dt="2025-06-16"
)
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 |
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| 1416 | 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 |
| 1466 | 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 |
| 1536 | 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 |
| 1578 | 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 |
| 1660 | 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 |
最初は短い期間で試すのがおすすめです。
pybaseballの公式ドキュメントでは、Baseball Savant側の取得制限に対応するため、長期間のリクエストは分割されることが説明されています。大量データを扱う場合は、処理時間やメモリ使用量にも注意しましょう。
3. 打球データだけを取り出す
最も簡単な方法は、launch_speed が記録されている行を抽出することです。
batted_balls = game_data[
game_data["launch_speed"].notna()
].copy()
display(batted_balls.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 |
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| 1416 | 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 |
| 1536 | 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 |
| 1578 | 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 |
| 1660 | 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 |
| 1678 | FF | 2025-06-16 00:00:00 | 94.0 | -2.5 | 5.34 | Yates, Kirby | 578428 | 489446 | NaN | foul | <NA> | <NA> | <NA> | <NA> | 11 | NaN | R | R | R | LAD |
notna() は「値が空欄ではない」という条件です。
つまり、
game_data["launch_speed"].notna()
によって、launch_speed が記録されている行だけを残しています。
Statcast公式ドキュメントでは、launch_speed はStatcastが追跡した打球速度で、一部の直接追跡されていない打球について推定値が含まれると説明されています。
4. 打球に関係する列を確認する
まずは必要な列だけ表示してみます。
batted_ball_columns = [
"game_date",
"game_pk",
"at_bat_number",
"pitch_number",
"batter",
"pitcher",
"events",
"pitch_name",
"release_speed",
"launch_speed",
"launch_angle",
"hit_distance_sc",
"bb_type",
"launch_speed_angle"
]
display(
batted_balls[batted_ball_columns].head(20)
)
出力結果
| index | game_date | game_pk | at_bat_number | pitch_number | batter | pitcher | events | pitch_name | release_speed | launch_speed | launch_angle | hit_distance_sc | bb_type | launch_speed_angle |
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| 1416 | 2025-06-16 00:00:00 | 777490 | 73 | 3 | 642180 | 489446 | field_out | 4-Seam Fastball | 93.5 | 76.0 | 57 | 182 | popup | 3 |
| 1536 | 2025-06-16 00:00:00 | 777490 | 73 | 1 | 642180 | 489446 | NaN | 4-Seam Fastball | 93.0 | 59.8 | 48 | 159 | NaN | <NA> |
| 1578 | 2025-06-16 00:00:00 | 777490 | 72 | 4 | 578428 | 489446 | field_out | Split-Finger | 85.9 | 98.5 | 5 | 167 | line_drive | 4 |
| 1660 | 2025-06-16 00:00:00 | 777490 | 72 | 3 | 578428 | 489446 | NaN | 4-Seam Fastball | 93.4 | 81.0 | 43 | 262 | NaN | <NA> |
| 1678 | 2025-06-16 00:00:00 | 777490 | 72 | 2 | 578428 | 489446 | NaN | 4-Seam Fastball | 94.0 | 78.4 | 53 | 219 | NaN | <NA> |
| 1772 | 2025-06-16 00:00:00 | 777490 | 72 | 1 | 578428 | 489446 | NaN | 4-Seam Fastball | 93.3 | 66.6 | 48 | 189 | NaN | <NA> |
| 1825 | 2025-06-16 00:00:00 | 777490 | 71 | 3 | 630105 | 489446 | field_out | Split-Finger | 86.0 | 68.9 | -17 | 5 | ground_ball | 2 |
| 852 | 2025-06-16 00:00:00 | 777490 | 70 | 1 | 605141 | 688158 | field_out | 4-Seam Fastball | 95.3 | 91.1 | 54 | 238 | fly_ball | 3 |
| 967 | 2025-06-16 00:00:00 | 777490 | 68 | 1 | 669242 | 688158 | field_out | Slider | 87.1 | 88.2 | -16 | 5 | ground_ball | 2 |
| 994 | 2025-06-16 00:00:00 | 777490 | 67 | 5 | 624424 | 688158 | field_out | 4-Seam Fastball | 96.4 | 96.7 | 7 | 146 | line_drive | 4 |
| 1077 | 2025-06-16 00:00:00 | 777490 | 67 | 2 | 624424 | 688158 | NaN | 4-Seam Fastball | 96.7 | 66.2 | 64 | 164 | NaN | <NA> |
| 1106 | 2025-06-16 00:00:00 | 777490 | 67 | 1 | 624424 | 688158 | NaN | 4-Seam Fastball | 97.5 | 100.2 | -3 | 34 | NaN | <NA> |
| 1576 | 2025-06-16 00:00:00 | 777490 | 66 | 1 | 593428 | 656945 | field_out | 4-Seam Fastball | 97.3 | 69.6 | 71 | 115 | popup | 3 |
| 1788 | 2025-06-16 00:00:00 | 777490 | 64 | 3 | 592518 | 656945 | field_out | 4-Seam Fastball | 97.0 | 108.1 | -7 | 14 | ground_ball | 2 |
| 1118 | 2025-06-16 00:00:00 | 777490 | 63 | 1 | 681624 | 688158 | force_out | Slider | 86.9 | 104.1 | -8 | 13 | ground_ball | 2 |
| 1276 | 2025-06-16 00:00:00 | 777490 | 62 | 1 | 571970 | 688158 | NaN | Curveball | 82.3 | 67.7 | -31 | 4 | NaN | <NA> |
| 1296 | 2025-06-16 00:00:00 | 777490 | 61 | 7 | 669257 | 688158 | single | 4-Seam Fastball | 98.3 | 102.0 | 2 | 66 | ground_ball | 4 |
| 1364 | 2025-06-16 00:00:00 | 777490 | 61 | 6 | 669257 | 688158 | NaN | Slider | 88.2 | 77.0 | -27 | 5 | NaN | <NA> |
| 1456 | 2025-06-16 00:00:00 | 777490 | 61 | 4 | 669257 | 688158 | NaN | 4-Seam Fastball | 96.6 | 79.0 | 16 | 185 | NaN | <NA> |
| 1703 | 2025-06-16 00:00:00 | 777490 | 60 | 5 | 606192 | 688158 | NaN | Slider | 87.4 | 71.3 | 67 | 127 | NaN | <NA> |
ここから、
- どの球種を打ったのか
- 何mphの球だったのか
- 打球速度は何mphだったのか
- 打球角度は何度だったのか
- どのくらい飛んだのか
- ゴロ・ライナー・フライのどれだったのか
を確認できます。
5. launch_speed は打球速度
launch_speed はStatcastの**Exit Velocity(EV)**です。
単位はmphです。
display(
batted_balls[
[
"events",
"description",
"launch_speed"
]
].head(20)
)
出力結果
| index | events | description | launch_speed |
|---|---|---|---|
| 1416 | field_out | hit_into_play | 76.0 |
| 1536 | NaN | foul | 59.8 |
| 1578 | field_out | hit_into_play | 98.5 |
| 1660 | NaN | foul | 81.0 |
| 1678 | NaN | foul | 78.4 |
| 1772 | NaN | foul | 66.6 |
| 1825 | field_out | hit_into_play | 68.9 |
| 852 | field_out | hit_into_play | 91.1 |
| 967 | field_out | hit_into_play | 88.2 |
| 994 | field_out | hit_into_play | 96.7 |
| 1077 | NaN | foul | 66.2 |
| 1106 | NaN | foul | 100.2 |
| 1576 | field_out | hit_into_play | 69.6 |
| 1788 | field_out | hit_into_play | 108.1 |
| 1118 | force_out | hit_into_play | 104.1 |
| 1276 | NaN | foul | 67.7 |
| 1296 | single | hit_into_play | 102.0 |
| 1364 | NaN | foul | 77.0 |
| 1456 | NaN | foul | 79.0 |
| 1703 | NaN | foul | 71.3 |
平均打球速度は次のように計算できます。
avg_exit_velocity = (
batted_balls["launch_speed"].mean()
)
print(f"平均打球速度: {avg_exit_velocity:.1f} mph")
出力結果
平均打球速度: 83.3 mph
ただし、複数の選手や球種をまとめて平均すると意味が分かりにくくなる場合があります。
実際の分析では、選手別、球種別、投手別などに分けて見ると傾向を把握しやすくなります。
6. launch_angle は打球角度
launch_angle は打球角度です。単位は度です。
display(
batted_balls[
[
"events",
"description",
"launch_speed",
"launch_angle"
]
].head(20)
)
出力結果
| index | events | description | launch_speed | launch_angle |
|---|---|---|---|---|
| 1416 | field_out | hit_into_play | 76.0 | 57 |
| 1536 | NaN | foul | 59.8 | 48 |
| 1578 | field_out | hit_into_play | 98.5 | 5 |
| 1660 | NaN | foul | 81.0 | 43 |
| 1678 | NaN | foul | 78.4 | 53 |
| 1772 | NaN | foul | 66.6 | 48 |
| 1825 | field_out | hit_into_play | 68.9 | -17 |
| 852 | field_out | hit_into_play | 91.1 | 54 |
| 967 | field_out | hit_into_play | 88.2 | -16 |
| 994 | field_out | hit_into_play | 96.7 | 7 |
| 1077 | NaN | foul | 66.2 | 64 |
| 1106 | NaN | foul | 100.2 | -3 |
| 1576 | field_out | hit_into_play | 69.6 | 71 |
| 1788 | field_out | hit_into_play | 108.1 | -7 |
| 1118 | force_out | hit_into_play | 104.1 | -8 |
| 1276 | NaN | foul | 67.7 | -31 |
| 1296 | single | hit_into_play | 102.0 | 2 |
| 1364 | NaN | foul | 77.0 | -27 |
| 1456 | NaN | foul | 79.0 | 16 |
| 1703 | NaN | foul | 71.3 | 67 |
角度が低い打球、高い打球などを区別できます。

7. hit_distance_sc は打球距離
hit_distance_sc はStatcastの打球距離に関する項目です。
display(
batted_balls[
[
"launch_speed",
"launch_angle",
"hit_distance_sc"
]
].head(20)
)
出力結果
| index | launch_speed | launch_angle | hit_distance_sc |
|---|---|---|---|
| 1416 | 76.0 | 57 | 182 |
| 1536 | 59.8 | 48 | 159 |
| 1578 | 98.5 | 5 | 167 |
| 1660 | 81.0 | 43 | 262 |
| 1678 | 78.4 | 53 | 219 |
| 1772 | 66.6 | 48 | 189 |
| 1825 | 68.9 | -17 | 5 |
| 852 | 91.1 | 54 | 238 |
| 967 | 88.2 | -16 | 5 |
| 994 | 96.7 | 7 | 146 |
| 1077 | 66.2 | 64 | 164 |
| 1106 | 100.2 | -3 | 34 |
| 1576 | 69.6 | 71 | 115 |
| 1788 | 108.1 | -7 | 14 |
| 1118 | 104.1 | -8 | 13 |
| 1276 | 67.7 | -31 | 4 |
| 1296 | 102.0 | 2 | 66 |
| 1364 | 77.0 | -27 | 5 |
| 1456 | 79.0 | 16 | 185 |
| 1703 | 71.3 | 67 | 127 |
日本向けの分析では、フィートをメートルに変換することもできます。
batted_balls["hit_distance_m"] = (
batted_balls["hit_distance_sc"] * 0.3048
)
display(
batted_balls[
[
"hit_distance_sc",
"hit_distance_m"
]
].head(20)
)
出力結果
| index | hit_distance_sc | hit_distance_m |
|---|---|---|
| 1416 | 182 | 55.473600000000005 |
| 1536 | 159 | 48.4632 |
| 1578 | 167 | 50.9016 |
| 1660 | 262 | 79.8576 |
| 1678 | 219 | 66.7512 |
| 1772 | 189 | 57.607200000000006 |
| 1825 | 5 | 1.524 |
| 852 | 238 | 72.5424 |
| 967 | 5 | 1.524 |
| 994 | 146 | 44.500800000000005 |
| 1077 | 164 | 49.9872 |
| 1106 | 34 | 10.3632 |
| 1576 | 115 | 35.052 |
| 1788 | 14 | 4.2672 |
| 1118 | 13 | 3.9624 |
| 1276 | 4 | 1.2192 |
| 1296 | 66 | 20.1168 |
| 1364 | 5 | 1.524 |
| 1456 | 185 | 56.388000000000005 |
| 1703 | 127 | 38.7096 |
例えば400フィートは、
400 × 0.3048 ≒ 121.9m
です。
hit_distance_sc(打球飛距離)は、原則として「打球が最初に着地した地点、または野手のグローブなどに触れた地点」までのホームプレートからの水平距離を表しています。
8. bb_type で打球タイプを見る
bb_type は打球タイプです。
代表的な分類は次のとおりです。
bb_type |
意味 |
|---|---|
ground_ball |
ゴロ |
line_drive |
ライナー |
fly_ball |
フライ |
popup |
ポップアップ |
display(
batted_balls[
[
"launch_speed",
"launch_angle",
"bb_type"
]
].head(30)
)
出力結果
| index | launch_speed | launch_angle | bb_type |
|---|---|---|---|
| 1416 | 76.0 | 57 | popup |
| 1536 | 59.8 | 48 | NaN |
| 1578 | 98.5 | 5 | line_drive |
| 1660 | 81.0 | 43 | NaN |
| 1678 | 78.4 | 53 | NaN |
| 1772 | 66.6 | 48 | NaN |
| 1825 | 68.9 | -17 | ground_ball |
| 852 | 91.1 | 54 | fly_ball |
| 967 | 88.2 | -16 | ground_ball |
| 994 | 96.7 | 7 | line_drive |
| 1077 | 66.2 | 64 | NaN |
| 1106 | 100.2 | -3 | NaN |
| 1576 | 69.6 | 71 | popup |
| 1788 | 108.1 | -7 | ground_ball |
| 1118 | 104.1 | -8 | ground_ball |
| 1276 | 67.7 | -31 | NaN |
| 1296 | 102.0 | 2 | ground_ball |
| 1364 | 77.0 | -27 | NaN |
| 1456 | 79.0 | 16 | NaN |
| 1703 | 71.3 | 67 | NaN |
| 1098 | 97.3 | 1 | ground_ball |
| 1399 | 75.4 | 0 | ground_ball |
| 1422 | 99.0 | 9 | line_drive |
| 1474 | 76.7 | 42 | NaN |
| 1750 | 83.1 | 28 | line_drive |
| 1175 | 77.2 | -1 | ground_ball |
| 1543 | 72.6 | -20 | ground_ball |
| 1753 | 87.5 | 24 | line_drive |
| 1849 | 89.1 | 68 | popup |
| 383 | 84.7 | 24 | line_drive |
打球タイプの件数は value_counts() で確認できます。
bb_type_counts = (
batted_balls["bb_type"]
.value_counts(dropna=False)
)
display(bb_type_counts)
出力結果
| bb_type | count |
|---|---|
| NaN | 321 |
| ground_ball | 150 |
| line_drive | 76 |
| fly_ball | 76 |
| popup | 34 |
割合を確認するなら、
bb_type_ratio = (
batted_balls["bb_type"]
.value_counts(normalize=True, dropna=False)
.mul(100)
.round(1)
)
display(bb_type_ratio)
出力結果
| bb_type | proportion |
|---|---|
| NaN | 48.9 |
| ground_ball | 22.8 |
| line_drive | 11.6 |
| fly_ball | 11.6 |
| popup | 5.2 |
9. 選手別の平均打球速度を計算する
選手別に打球速度を集計してみます。
batter_summary = (
batted_balls
.groupby("batter")
.agg(
batted_balls=("launch_speed", "count"),
avg_exit_velocity=("launch_speed", "mean"),
max_exit_velocity=("launch_speed", "max"),
avg_launch_angle=("launch_angle", "mean")
)
.reset_index()
.sort_values(
"avg_exit_velocity",
ascending=False
)
)
display(batter_summary.head(20))
出力結果
| index | batter | batted_balls | avg_exit_velocity | max_exit_velocity | avg_launch_angle |
|---|---|---|---|---|---|
| 134 | 810938 | 1 | 104.5 | 104.5 | 3.0 |
| 115 | 691594 | 1 | 103.3 | 103.3 | -4.0 |
| 119 | 692225 | 1 | 102.3 | 102.3 | -10.0 |
| 53 | 663993 | 1 | 101.7 | 101.7 | -14.0 |
| 13 | 592450 | 3 | 100.93333333333334 | 110.7 | 9.333333333333334 |
| 62 | 666018 | 2 | 100.4 | 106.4 | 14.5 |
| 22 | 605170 | 3 | 99.46666666666665 | 103.4 | -2.6666666666666665 |
| 102 | 682663 | 4 | 99.425 | 107.7 | 6.0 |
| 94 | 680776 | 7 | 98.55714285714285 | 110.0 | 20.142857142857142 |
| 130 | 702284 | 1 | 97.6 | 97.6 | 7.0 |
| 54 | 664040 | 4 | 97.35 | 109.9 | 3.25 |
| 7 | 519317 | 6 | 96.53333333333335 | 111.1 | -5.5 |
| 70 | 668227 | 3 | 96.26666666666667 | 108.6 | 26.0 |
| 8 | 545361 | 4 | 95.35 | 104.6 | 21.5 |
| 89 | 676694 | 2 | 94.65 | 103.3 | -3.0 |
| 20 | 605137 | 1 | 93.9 | 93.9 | -6.0 |
| 129 | 701762 | 6 | 92.0 | 111.7 | 2.6666666666666665 |
| 80 | 669911 | 3 | 90.96666666666665 | 105.5 | 10.666666666666666 |
| 92 | 679032 | 1 | 90.8 | 90.8 | -12.0 |
| 118 | 691785 | 4 | 90.75 | 103.0 | 0.0 |
ここでは batter のMLB Player IDを分析のキーにしています。
選手名を表示したい場合は、IDから選手名を取得して後から結合する方法が安全です。
また、平均値だけでなく batted_balls も必ず確認しましょう。
1球だけの平均と100球以上の平均では、分析上の意味が大きく違うためです。
10. 特定の打者だけを分析する
特定の選手に絞る場合は batter を使えます。
batter_id = 660271
batter_balls = batted_balls[
batted_balls["batter"] == batter_id
].copy()
display(
batter_balls[
[
"game_date",
"events",
"pitch_name",
"release_speed",
"launch_speed",
"launch_angle",
"hit_distance_sc",
"bb_type"
]
]
)
出力結果
| index | game_date | events | pitch_name | release_speed | launch_speed | launch_angle | hit_distance_sc | bb_type |
|---|---|---|---|---|---|---|---|---|
| 258 | 2025-06-16 00:00:00 | single | 4-Seam Fastball | 98.3 | 87.9 | 25 | 274 | line_drive |
| 277 | 2025-06-16 00:00:00 | NaN | 4-Seam Fastball | 99.5 | 81.5 | 69 | 163 | NaN |
| 615 | 2025-06-16 00:00:00 | double | Slider | 91.5 | 105.2 | 17 | 337 | line_drive |
| 1713 | 2025-06-16 00:00:00 | NaN | Slider | 91.4 | 77.2 | -32 | 4 | NaN |
660271 は分析対象のMLB Player IDに置き換えてください。
選手IDを検索する場合は、pybaseballの playerid_lookup() を利用できます。
from pybaseball import playerid_lookup
playerid_lookup("ohtani", "shohei")
出力結果
| index | name_last | name_first | key_mlbam | key_retro | key_bbref | key_fangraphs | mlb_played_first | mlb_played_last |
|---|---|---|---|---|---|---|---|---|
| 0 | ohtani | shohei | 660271 | ohtas001 | ohtansh01 | 19755 | 2018.0 | 2026.0 |
11. ハードヒットを調べる
Statcastでは、95mph以上の打球をHard Hitと定義しています。
そのため、95mph以上の打球を抽出する基本コードは次のとおりです。
hard_hit = batted_balls[
batted_balls["launch_speed"] >= 95
].copy()
display(
hard_hit[
[
"batter",
"events",
"launch_speed",
"launch_angle",
"hit_distance_sc",
"bb_type"
]
].head(20)
)
出力結果
| index | batter | events | launch_speed | launch_angle | hit_distance_sc | bb_type |
|---|---|---|---|---|---|---|
| 1578 | 578428 | field_out | 98.5 | 5 | 167 | line_drive |
| 994 | 624424 | field_out | 96.7 | 7 | 146 | line_drive |
| 1106 | 624424 | NaN | 100.2 | -3 | 34 | NaN |
| 1788 | 592518 | field_out | 108.1 | -7 | 14 | ground_ball |
| 1118 | 681624 | force_out | 104.1 | -8 | 13 | ground_ball |
| 1296 | 669257 | single | 102.0 | 2 | 66 | ground_ball |
| 1098 | 518692 | single | 97.3 | 1 | 55 | ground_ball |
| 1422 | 665487 | single | 99.0 | 9 | 184 | line_drive |
| 461 | 657757 | double | 97.1 | 7 | 147 | line_drive |
| 488 | 592518 | home_run | 99.9 | 35 | 353 | fly_ball |
| 94 | 606192 | field_out | 110.3 | -3 | 25 | ground_ball |
| 282 | 669242 | single | 105.8 | 16 | 252 | line_drive |
| 388 | 571970 | single | 108.2 | 17 | 309 | line_drive |
| 681 | 642180 | field_out | 96.0 | 5 | 88 | ground_ball |
| 820 | 593428 | double | 100.1 | 17 | 319 | line_drive |
| 883 | 657757 | NaN | 97.0 | -1 | 27 | NaN |
| 615 | 660271 | double | 105.2 | 17 | 337 | line_drive |
| 647 | 669242 | field_out | 105.7 | -17 | 5 | ground_ball |
| 801 | 681624 | double | 95.4 | 11 | 228 | line_drive |
| 1209 | 650333 | field_out | 96.9 | -5 | 11 | ground_ball |
件数と割合も計算できます。
print("ハードヒット数:", len(hard_hit))
hard_hit_rate = (
len(hard_hit) / len(batted_balls) * 100
)
print(f"ハードヒット率: {hard_hit_rate:.1f}%")
出力結果
ハードヒット数: 173 ハードヒット率: 26.3%
12. launch_speed_angle とBarrel
Statcastには、打球速度と打球角度の組み合わせを分類する launch_speed_angle があります。
display(
batted_balls[
[
"launch_speed",
"launch_angle",
"launch_speed_angle"
]
].head(20)
)
出力結果
| index | launch_speed | launch_angle | launch_speed_angle |
|---|---|---|---|
| 1416 | 76.0 | 57 | 3 |
| 1536 | 59.8 | 48 | <NA> |
| 1578 | 98.5 | 5 | 4 |
| 1660 | 81.0 | 43 | <NA> |
| 1678 | 78.4 | 53 | <NA> |
| 1772 | 66.6 | 48 | <NA> |
| 1825 | 68.9 | -17 | 2 |
| 852 | 91.1 | 54 | 3 |
| 967 | 88.2 | -16 | 2 |
| 994 | 96.7 | 7 | 4 |
| 1077 | 66.2 | 64 | <NA> |
| 1106 | 100.2 | -3 | <NA> |
| 1576 | 69.6 | 71 | 3 |
| 1788 | 108.1 | -7 | 2 |
| 1118 | 104.1 | -8 | 2 |
| 1276 | 67.7 | -31 | <NA> |
| 1296 | 102.0 | 2 | 4 |
| 1364 | 77.0 | -27 | <NA> |
| 1456 | 79.0 | 16 | <NA> |
| 1703 | 71.3 | 67 | <NA> |
Statcastでは、
- Weak
- Topped
- Under
- Flare/Burner
- Solid Contact
- Barrel
などに分類されます。

13. ヒットになった打球だけを取り出す
events と組み合わせると、ヒットになった打球を確認できます。
hit_events = [
"single",
"double",
"triple",
"home_run"
]
hits = batted_balls[
batted_balls["events"].isin(hit_events)
].copy()
display(
hits[
[
"batter",
"events",
"launch_speed",
"launch_angle",
"hit_distance_sc",
"bb_type"
]
].head(20)
)
出力結果
| index | batter | events | launch_speed | launch_angle | hit_distance_sc | bb_type |
|---|---|---|---|---|---|---|
| 1296 | 669257 | single | 102.0 | 2 | 66 | ground_ball |
| 1098 | 518692 | single | 97.3 | 1 | 55 | ground_ball |
| 1422 | 665487 | single | 99.0 | 9 | 184 | line_drive |
| 461 | 657757 | double | 97.1 | 7 | 147 | line_drive |
| 488 | 592518 | home_run | 99.9 | 35 | 353 | fly_ball |
| 227 | 605141 | single | 83.2 | 24 | 285 | line_drive |
| 258 | 660271 | single | 87.9 | 25 | 274 | line_drive |
| 282 | 669242 | single | 105.8 | 16 | 252 | line_drive |
| 388 | 571970 | single | 108.2 | 17 | 309 | line_drive |
| 420 | 669257 | double | 81.1 | 31 | 279 | fly_ball |
| 490 | 606192 | single | 68.2 | -40 | 2 | ground_ball |
| 820 | 593428 | double | 100.1 | 17 | 319 | line_drive |
| 615 | 660271 | double | 105.2 | 17 | 337 | line_drive |
| 801 | 681624 | double | 95.4 | 11 | 228 | line_drive |
| 1353 | 650333 | single | 95.6 | 12 | 224 | line_drive |
| 1647 | 665487 | single | 74.6 | 32 | 253 | fly_ball |
| 1102 | 666160 | home_run | 98.8 | 30 | 368 | fly_ball |
| 1255 | 642731 | single | 110.8 | 8 | 197 | line_drive |
| 1723 | 696100 | home_run | 105.2 | 26 | 400 | fly_ball |
| 64 | 695578 | home_run | 104.6 | 25 | 373 | fly_ball |
これで、
ヒットになった打球は、どのくらいの速度・角度だったのか?
を調べられます。
14. 投手側から被打球を見る
打球分析は打者だけでなく、投手にも使えます。
pitcher_summary = (
batted_balls
.groupby("pitcher")
.agg(
batted_balls=("launch_speed", "count"),
avg_exit_velocity=("launch_speed", "mean"),
max_exit_velocity=("launch_speed", "max")
)
.reset_index()
.sort_values(
"avg_exit_velocity",
ascending=False
)
)
display(pitcher_summary.head(20))
出力結果
| index | pitcher | batted_balls | avg_exit_velocity | max_exit_velocity |
|---|---|---|---|---|
| 42 | 680767 | 2 | 103.80000000000001 | 105.9 |
| 6 | 592836 | 2 | 102.44999999999999 | 107.6 |
| 21 | 650556 | 2 | 95.15 | 111.7 |
| 44 | 681882 | 5 | 95.02000000000001 | 110.4 |
| 20 | 647315 | 6 | 94.68333333333334 | 104.8 |
| 1 | 458677 | 4 | 93.775 | 106.9 |
| 15 | 640448 | 9 | 90.78888888888889 | 110.8 |
| 31 | 666171 | 3 | 89.13333333333333 | 104.3 |
| 45 | 681911 | 1 | 89.1 | 89.1 |
| 33 | 667755 | 37 | 88.94324324324324 | 111.1 |
| 55 | 695418 | 7 | 88.91428571428571 | 102.5 |
| 25 | 656945 | 2 | 88.85 | 108.1 |
| 14 | 623474 | 4 | 88.25 | 104.6 |
| 53 | 690953 | 29 | 87.48620689655172 | 107.7 |
| 41 | 678368 | 15 | 87.46666666666667 | 102.1 |
| 34 | 669302 | 23 | 87.17826086956521 | 110.0 |
| 39 | 676508 | 21 | 87.1047619047619 | 100.1 |
| 54 | 695243 | 1 | 86.0 | 86.0 |
| 51 | 688158 | 11 | 85.77272727272727 | 104.1 |
| 49 | 687765 | 32 | 85.159375 | 109.2 |
これで投手ごとの被打球速度を確認できます。
15. 打球速度と打球角度を可視化する
Google Colabでは、matplotlibを使って散布図を作れます。
import matplotlib.pyplot as plt
plt.figure(figsize=(10, 6))
plt.scatter(
batted_balls["launch_speed"],
batted_balls["launch_angle"],
alpha=0.5
)
plt.xlabel("Exit Velocity (mph)")
plt.ylabel("Launch Angle (degrees)")
plt.title("Exit Velocity vs Launch Angle")
plt.show()
出力結果

横軸が打球速度、縦軸が打球角度です。
この図を見ることで、打球がどのような速度・角度の組み合わせに分布しているのかを直感的に確認できます。
まとめ
今回は、pybaseball を使ってMLBのStatcastデータから打球単位のデータを抽出する方法を解説しました。
特に重要なのは次の項目です。
| 列名 | 内容 |
|---|---|
launch_speed |
打球速度(Exit Velocity) |
launch_angle |
打球角度 |
hit_distance_sc |
打球距離に関するStatcast項目 |
bb_type |
ゴロ・ライナー・フライなど |
launch_speed_angle |
打球速度と角度による分類 |
events |
打席の結果 |
batter |
打者のMLB Player ID |
pitcher |
投手のMLB Player ID |
打球分析では、
打球速度だけでなく、打球速度・角度・距離・打球タイプを組み合わせて見る
ことがポイントです。
次回の記事
次回は、
【pybaseball基礎編⑦】投球単位のデータ取得
を予定しています。
基礎編⑤では1球単位の基本データ、基礎編⑥では打球データを扱いました。
基礎編⑦では投手側に戻り、
- 投球速度
- 回転数
- 回転軸
- リリース位置
- 球種
- 投球位置
- 球種ごとの特徴
などを使って、1球の投球そのものを詳しく分析する方法を扱います。
データ参照元
本記事では、MLBのStatcastデータをpybaseballから取得して分析しています。
Statcastのデータ項目については、MLBのBaseball Savantが公開しているStatcast Search CSV Documentationを参照しています。
Statcast Search CSV Documentation
pybaseballは、Baseball SavantのStatcastデータをPythonから取得し、pandasのDataFrameとして扱いやすくするためのライブラリです。
データを利用・公開する場合は、利用元の最新の規約、データ提供条件、各サービスの利用条件を確認してください。
動作環境
本記事では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