大谷さんの投球変化(2021-2023)

大谷さんの投球が、今年からスイーパー(スライダーの横滑りver)中心になったということで、pybaseballからデータを確認

<https://baseballsavant.mlb.com/savant-player/shohei-ohtani-660271?stats=gamelogs-r-pitching-mlb&season=2023>

***

# データ

2021～2023で見る

<pre><code>!pip install pybaseball
from pybaseball import statcast
import pandas as pd

from pybaseball import statcast

dates = [
    '2023-03-30', '2023-04-05', '2023-04-11', '2023-04-21', '2022-04-07', '2022-04-14',
    '2022-04-20', '2022-04-27', '2022-05-05', '2022-05-11', '2022-05-18', '2022-05-26',
    '2022-06-02', '2022-06-09', '2022-06-16', '2022-06-22', '2022-06-29', '2022-07-06',
    '2022-07-13', '2022-07-22', '2022-07-28', '2022-08-03', '2022-08-09', '2022-08-15',
    '2022-08-21', '2022-08-27', '2022-09-03', '2022-09-10', '2022-09-17', '2022-09-23',
    '2022-09-29', '2022-10-05', '2021-04-04', '2021-04-20', '2021-04-26', '2021-05-05',
    '2021-05-11', '2021-05-19', '2021-05-28', '2021-06-04', '2021-06-11', '2021-06-17',
    '2021-06-23', '2021-06-30', '2021-07-06', '2021-07-19', '2021-07-26', '2021-08-04',
    '2021-08-12', '2021-08-18', '2021-08-25', '2021-09-03', '2021-09-10', '2021-09-19'
]

# Create an empty DataFrame to store the data
df_660271_all_dates = pd.DataFrame()

# Fetch data for each date and concatenate
for date in dates:
    df_single_day = statcast(start_dt=date, end_dt=date)
    df_660271_single_day = df_single_day[df_single_day['pitcher'] == 660271]
    df_660271_all_dates = pd.concat([df_660271_all_dates, df_660271_single_day])

# Reset the index of the final DataFrame
df_660271_all_dates.reset_index(drop=True, inplace=True)


</code></pre>

***

# 球種

<pre><code># 投球結果を抽出
df_660271 = df_660271_all_dates

# df_660271のpitch_typeカラムに含まれるユニークな球種を確認する
unique_pitch_types = df_660271['pitch_type'].unique()

# 確認した球種を表示する
print(unique_pitch_types)

</code></pre>

結果

<pre><code>['ST' 'FS' 'FF' 'CU' 'SL' 'FC' 'SI' 'CS']
</code></pre>

*   ST - slutter / slider / cutter / sweeper　※実際の投球を見るとスイーパー
*   FS - splitter
*   FF - 4-seam fastball
*   CU - curveball
*   SL - slider
*   FC - cutter
*   SI - sinker (2-seam)
*   CS - カットスライダー？確認中

***

# 球種カウント

<pre><code>import pandas as pd

def pitch_counts(df):
    # 左打者と右打者に対する投球データを抽出
    df_L = df[df['stand'] == 'L']
    df_R = df[df['stand'] == 'R']

    # 各カテゴリーでの球種の出現回数をカウント
    total_counts = df['pitch_type'].value_counts()
    left_counts = df_L['pitch_type'].value_counts()
    right_counts = df_R['pitch_type'].value_counts()

    # 出現回数をデータフレームにまとめる
    pitch_counts_table = pd.DataFrame({'Total': total_counts, 'Left Batter': left_counts, 'Right Batter': right_counts})

    # NaNを0に置き換える
    pitch_counts_table.fillna(0, inplace=True)

    # カウントを整数に変換する
    pitch_counts_table = pitch_counts_table.astype(int)

    return pitch_counts_table

# 続けて、球種カウントのコードを実行します。
# Split the data by year
df_2021 = df_660271_all_dates[df_660271_all_dates['game_year'] == 2021]
df_2022= df_660271_all_dates[df_660271_all_dates['game_year'] == 2022]
df_2023 = df_660271_all_dates[df_660271_all_dates['game_year'] == 2023]

# Get pitch counts for each year
pitch_counts_2021 = pitch_counts(df_2021)
pitch_counts_2022 = pitch_counts(df_2022)
pitch_counts_2023 = pitch_counts(df_2023)

# Display the results
print("2021年の球種カウント:")
print(pitch_counts_2021)
print("\n2022年の球種カウント:")
print(pitch_counts_2022)
print("\n2023年の球種カウント:")
print(pitch_counts_2023)

</code></pre>

![](https://pbs.twimg.com/media/FuVDKu1agAAFSCM?format=png\&name=small)

***

# 球種カウント(円グラフ)

2022は、右打者には多かったスイーパー(ST)。2023からは、左右ともに多くなっている。

<pre><code>import matplotlib.pyplot as plt

def plot_pitch_distribution(df, year):
    df_L = df[df['stand'] == 'L']
    df_R = df[df['stand'] == 'R']

    fig, axs = plt.subplots(1, 3, figsize=(18, 6))
    plt.suptitle(f'Pitch Distribution in {year}')

    colors = {'FC': 'red', 'FF': 'blue', 'ST': 'green', 'CU': 'orange', 'SL': 'purple', 'SI': 'brown', 'FS': 'cyan', 'CS': 'magenta'}

    # Total
    df['pitch_type'].value_counts().plot(kind='pie', ax=axs[0], autopct='%.1f%%', colors=[colors[key] for key in df['pitch_type'].value_counts().index])
    axs[0].set_title('Total')
    axs[0].set_ylabel('')

    # vs Left batter
    df_L['pitch_type'].value_counts().plot(kind='pie', ax=axs[1], autopct='%.1f%%', colors=[colors[key] for key in df_L['pitch_type'].value_counts().index])
    axs[1].set_title('vs Left batter')
    axs[1].set_ylabel('')

    # vs Right batter
    df_R['pitch_type'].value_counts().plot(kind='pie', ax=axs[2], autopct='%.1f%%', colors=[colors[key] for key in df_R['pitch_type'].value_counts().index])
    axs[2].set_title('vs Right batter')
    axs[2].set_ylabel('')

    plt.show()

# Plot pitch distribution for 2021, 2022, and 2023 data
plot_pitch_distribution(df_2021, 2021)
plot_pitch_distribution(df_2022, 2022)
plot_pitch_distribution(df_2023, 2023)

</code></pre>

![](https://pbs.twimg.com/media/FuVDo4cagAA0cqT?format=jpg\&name=large)
![](https://pbs.twimg.com/media/FuVDpzjaQAAmU4F?format=jpg\&name=large)
![](https://pbs.twimg.com/media/FuVDq2laUAAIhSu?format=jpg\&name=large)

***

# 投球コース

とりあえず

<pre><code>import matplotlib.pyplot as plt

def plot_pitch_location(df, year):
    # データを pitch_type ごとにグループ分けする
    grouped = df.groupby('pitch_type')

    colors = {'FC': 'red', 'FF': 'blue', 'ST': 'green', 'CU': 'orange', 'SL': 'purple', 'SI': 'brown', 'FS': 'cyan', 'CS': 'magenta'}


    # pitch_type ごとに、'plate_x' を X 軸、'plate_z' を Y 軸とした散布図を作成する
    for pitch_type, data in grouped:
        plt.scatter(data['plate_x'], data['plate_z'], label=pitch_type, color=colors[pitch_type])

    # ストライクゾーン
    x = [-0.88, 0.88, 0.88, -0.88, -0.88]
    y = [1.51, 1.51, 3.4, 3.4, 1.51]
    plt.fill(x, y, color='r', alpha=0.3)

    # 凡例を表示する
    plt.legend(bbox_to_anchor=(1.05, 1), loc='upper left', borderaxespad=0)

    plt.xlim(-3, 4)
    plt.ylim(-3, 7)

    plt.xlabel('Plate X')
    plt.ylabel('Plate Z')

    # 罫線
    plt.grid(which='both', linestyle='--', color='gray', alpha=0.5)

    plt.title(f'Pitch Location in {year}')
    
    # グラフを表示する
    plt.show()

# 2021年、2022年、2023年のデータに対して投球位置をプロット
plot_pitch_location(df_2021, 2021)
plot_pitch_location(df_2022, 2022)
plot_pitch_location(df_2023, 2023)
</code></pre>

![](https://pbs.twimg.com/media/FuVEFAuaMAAHrvD?format=png\&name=small)
![](https://pbs.twimg.com/media/FuVEF5QagAcPOld?format=png\&name=small)
![](https://pbs.twimg.com/media/FuVEG7VaAAAyhYD?format=png\&name=small)

***

# リリースポイント

スイーパー(ST)とストレート(FF)のリリースポイント近くで投げてるので、見分けが難しそう。打ちづらそう。

<pre><code>import matplotlib.pyplot as plt

def plot_release_point(df, year):
    # データを pitch_type ごとにグループ分けする
    grouped = df.groupby('pitch_type')

    colors = {'FC': 'red', 'FF': 'blue', 'ST': 'green', 'CU': 'orange', 'SL': 'purple', 'SI': 'brown', 'FS': 'cyan', 'CS': 'magenta'}

    # pitch_type ごとに、'release_pos_x' を X 軸、'release_pos_z' を Y 軸とした散布図を作成する
    for pitch_type, data in grouped:
        plt.scatter(data['release_pos_x'], data['release_pos_z'], label=pitch_type, color=colors[pitch_type])

    # 凡例を表示する
    plt.legend(bbox_to_anchor=(1.05, 1), loc='upper left', borderaxespad=0)

    plt.xlabel('Release Pos X')
    plt.ylabel('Release Pos Z')

    # 罫線
    plt.grid(which='both', linestyle='--', color='gray', alpha=0.5)

    plt.title(f'Release Point in {year}')

    # X軸とY軸のレン.5ジを指定
    plt.xlim(-4, 0)
    plt.ylim(5, 7)

    # グラフを表示する
    plt.show()

# 2021年、2022年、2023年のデータに対してリリースポイントをプロット
plot_release_point(df_2021, 2021)
plot_release_point(df_2022, 2022)
plot_release_point(df_2023, 2023)

</code></pre>

![](https://pbs.twimg.com/media/FuVEyWzaMAEFn8f?format=png\&name=small)
![](https://pbs.twimg.com/media/FuVE0tlaYAoMgVp?format=png\&name=small)
![](https://pbs.twimg.com/media/FuVE1i8agAANW_Z?format=png\&name=small)

## スイーパー(ST)とストレート(FF)

<pre><code>import matplotlib.pyplot as plt

def plot_release_point(df, year):
    # データを pitch_type ごとにグループ分けする
    grouped = df.groupby('pitch_type')

    plot_config = {
        'FF': {'color': 'blue', 'marker': 'o'},
        'ST': {'color': 'green', 'marker': 'x'}
    }

    # pitch_type ごとに、'release_pos_x' を X 軸、'release_pos_z' を Y 軸とした散布図を作成する
    for pitch_type, data in grouped:
        if pitch_type in plot_config:
            plt.scatter(data['release_pos_x'], data['release_pos_z'], label=pitch_type, color=plot_config[pitch_type]['color'], marker=plot_config[pitch_type]['marker'], alpha=0.5)

    # 凡例を表示する
    plt.legend(bbox_to_anchor=(1.05, 1), loc='upper left', borderaxespad=0)

    plt.xlabel('Release Pos X')
    plt.ylabel('Release Pos Z')

    # 罫線
    plt.grid(which='both', linestyle='--', color='gray', alpha=0.5)

    plt.title(f'Release Point in {year}')

    # X軸とY軸のレンジを指定
    plt.xlim(-4, 0)
    plt.ylim(5, 7)

    # グラフを表示する
    plt.show()

# 2021年、2022年、2023年のデータに対してリリースポイントをプロット
plot_release_point(df_2021, 2021)
plot_release_point(df_2022, 2022)
plot_release_point(df_2023, 2023)

</code></pre>

![](https://pbs.twimg.com/media/FuVFXN1acAAWavV?format=png\&name=small)
![](https://pbs.twimg.com/media/FuVFYa9akAAbEty?format=png\&name=small)
![](https://pbs.twimg.com/media/FuVFZXvaMAQ8sve?format=png\&name=small)

***

# 球種ごとのリリースポイント移り変わり

<pre><code>import matplotlib.pyplot as plt

def plot_release_point_by_year(df_2021, df_2022, df_2023):
    # 3つのデータフレームを結合
    combined_df = pd.concat([df_2021, df_2022, df_2023])

    # データを pitch_type ごとにグループ分けする
    grouped = combined_df.groupby('pitch_type')

    # グラフの数と行列を指定
    nrows = 4
    ncols = 2

    fig, axes = plt.subplots(nrows=nrows, ncols=ncols, figsize=(10, 15))

    for idx, (pitch_type, data) in enumerate(grouped):
        row = idx // ncols
        col = idx % ncols

        ax = axes[row][col]

        # 2021年、2022年、2023年のデータをそれぞれプロット
        data_2021 = data[data.index.isin(df_2021.index)]
        data_2022 = data[data.index.isin(df_2022.index)]
        data_2023 = data[data.index.isin(df_2023.index)]

        ax.scatter(data_2021['release_pos_x'], data_2021['release_pos_z'], label='2021', color='blue')
        ax.scatter(data_2022['release_pos_x'], data_2022['release_pos_z'], label='2022', color='red')
        ax.scatter(data_2023['release_pos_x'], data_2023['release_pos_z'], label='2023', color='green')

        ax.set_title(pitch_type)
        ax.set_xlabel('Release Pos X')
        ax.set_ylabel('Release Pos Z')

        # 罫線
        ax.grid(which='both', linestyle='--', color='gray', alpha=0.5)

        # X軸とY軸の範囲を統一する
        ax.set_xlim(-4, 0)  # X軸の範囲を-3から3に設定
        ax.set_ylim(5, 7)   # Y軸の範囲を0から7に設定


        # 凡例を表示する
        ax.legend()

    # グラフを表示する
    plt.tight_layout()
    plt.show()

# 各球種ごとにグラフを分けて、2021年、2022年、2023年のデータを色分けしてプロット
plot_release_point_by_year(df_2021, df_2022, df_2023)

</code></pre>

![](https://pbs.twimg.com/media/FuVGCAkakAAoig7?format=png\&name=900x900)

***

# 球種ごとのコース移り変わり

<pre><code>import matplotlib.pyplot as plt

def plot_pitch_location_by_year(df_2021, df_2022, df_2023):
    # 3つのデータフレームを結合
    combined_df = pd.concat([df_2021, df_2022, df_2023])

    # データを pitch_type ごとにグループ分けする
    grouped = combined_df.groupby('pitch_type')

    # グラフの数と行列を指定
    nrows = 4
    ncols = 2

    # サブプロットのタイトル用に球種を格納
    titles = []

    fig, axes = plt.subplots(nrows=nrows, ncols=ncols, figsize=(10, 15))

    for idx, (pitch_type, data) in enumerate(grouped):
        # サブプロットのタイトルに球種を追加
        titles.append(pitch_type)

        # サブプロットに対応する行と列のインデックスを計算
        row = idx // ncols
        col = idx % ncols

        ax = axes[row][col]

        # 2021年、2022年、2023年のデータをそれぞれプロット
        data_2021 = data[data.index.isin(df_2021.index)]
        data_2022 = data[data.index.isin(df_2022.index)]
        data_2023 = data[data.index.isin(df_2023.index)]

        ax.scatter(data_2021['plate_x'], data_2021['plate_z'], label='2021', color='blue')
        ax.scatter(data_2022['plate_x'], data_2022['plate_z'], label='2022', color='red')
        ax.scatter(data_2023['plate_x'], data_2023['plate_z'], label='2023', color='green')

        # ストライクゾーン
        x = [-0.88, 0.88, 0.88, -0.88, -0.88]
        y = [1.51, 1.51, 3.4, 3.4, 1.51]
        ax.fill(x, y, color='r', alpha=0.3)

        ax.set_title(pitch_type)
        ax.set_xlabel('Plate X')
        ax.set_ylabel('Plate Z')

        # 罫線
        ax.grid(which='both', linestyle='--', color='gray', alpha=0.5)

        # X軸とY軸の範囲を統一する
        ax.set_xlim(-4, 4)  # X軸の範囲を-3から3に設定
        ax.set_ylim(-2, 6)   # Y軸の範囲を0から5に設定

        # 凡例を表示する
        ax.legend()

    # グラフを表示する
    plt.tight_layout()
    plt.show()

# 各球種ごとにグラフを分けて、2021年、2022年、2023年のデータを色分けしてプロット
plot_pitch_location_by_year(df_2021, df_2022, df_2023)

</code></pre>

![](https://pbs.twimg.com/media/FuVGW4LagAA2yRf?format=png\&name=900x900)

***

# 球種ごとのスピンレート移り変わり(Boxplot)

<pre><code>import matplotlib.pyplot as plt
import pandas as pd

def plot_spin_rate_by_year_boxplot(df_2021, df_2022, df_2023, pitch_types):
    pitch_types = [pt for pt in pitch_types if pt is not None and pd.notna(pt)]  # nan を除外
    fig, axs = plt.subplots(len(pitch_types), 1, figsize=(8, len(pitch_types) * 4))

    for i, pitch_type in enumerate(pitch_types):
        ax = axs[i]

        grouped_2021 = df_2021[df_2021['pitch_type'] == pitch_type].dropna(subset=['release_spin_rate'])
        grouped_2022 = df_2022[df_2022['pitch_type'] == pitch_type].dropna(subset=['release_spin_rate'])
        grouped_2023 = df_2023[df_2023['pitch_type'] == pitch_type].dropna(subset=['release_spin_rate'])

        data_to_plot = []
        labels = []
        if not grouped_2021.empty:
            data_to_plot.append(grouped_2021['release_spin_rate'])
            labels.append('2021')
        if not grouped_2022.empty:
            data_to_plot.append(grouped_2022['release_spin_rate'])
            labels.append('2022')
        if not grouped_2023.empty:
            data_to_plot.append(grouped_2023['release_spin_rate'])
            labels.append('2023')

        if data_to_plot:
            bp = ax.boxplot(data_to_plot, labels=labels)
            for j, d in enumerate(data_to_plot):
                mean_val = d.mean()
                ax.text(j + 0.8, mean_val, f"{mean_val:.1f}", ha='center', va='bottom', fontsize=10, color='blue')

        ax.set_title(f"Pitch Type: {pitch_type}")
        ax.set_xlabel('Year')
        ax.set_ylabel('Release Spin Rate')

        # 罫線
        ax.grid(which='both', linestyle='--', color='gray', alpha=0.5)

    fig.suptitle('Release Spin Rate by Year and Pitch Type', fontsize=16, y=1.02)
    plt.tight_layout(pad=3)
    plt.show()

# 2021年、2022年、2023年のデータに対してリリーススピンレートをプロット
pitch_types_2021 = df_2021['pitch_type'].unique()
pitch_types_2022 = df_2022['pitch_type'].unique()
pitch_types_2023 = df_2023['pitch_type'].unique()

# すべての年に存在する球種を取得
all_pitch_types = set(pitch_types_2021) | set(pitch_types_2022) | set(pitch_types_2023)

plot_spin_rate_by_year_boxplot(df_2021, df_2022, df_2023, all_pitch_types)

</code></pre>

![](https://pbs.twimg.com/media/FuVGvgcaMAAggAL?format=png\&name=4096x4096)

***

# 球種ごとのスピード移り変わり(Boxplot)

<pre><code>import matplotlib.pyplot as plt
import pandas as pd

def plot_release_speed_by_year_boxplot(df_2021, df_2022, df_2023, pitch_types):
    pitch_types = [pt for pt in pitch_types if pt is not None and pd.notna(pt)]  # nan を除外
    fig, axs = plt.subplots(len(pitch_types), 1, figsize=(8, len(pitch_types) * 4))

    for i, pitch_type in enumerate(pitch_types):
        ax = axs[i]

        grouped_2021 = df_2021[df_2021['pitch_type'] == pitch_type].dropna(subset=['release_speed'])
        grouped_2022 = df_2022[df_2022['pitch_type'] == pitch_type].dropna(subset=['release_speed'])
        grouped_2023 = df_2023[df_2023['pitch_type'] == pitch_type].dropna(subset=['release_speed'])

        data_to_plot = []
        labels = []
        if not grouped_2021.empty:
            data_to_plot.append(grouped_2021['release_speed'])
            labels.append('2021')
        if not grouped_2022.empty:
            data_to_plot.append(grouped_2022['release_speed'])
            labels.append('2022')
        if not grouped_2023.empty:
            data_to_plot.append(grouped_2023['release_speed'])
            labels.append('2023')

        if data_to_plot:
            bp = ax.boxplot(data_to_plot, labels=labels)
            for j, d in enumerate(data_to_plot):
                mean_val = d.mean()
                ax.text(j + 0.8, mean_val, f"{mean_val:.1f}", ha='center', va='bottom', fontsize=10, color='blue')

        ax.set_title(f"Pitch Type: {pitch_type}")
        ax.set_xlabel('Year')
        ax.set_ylabel('Release Speed')

        # 罫線
        ax.grid(which='both', linestyle='--', color='gray', alpha=0.5)

    fig.suptitle('Release Speed by Year and Pitch Type', fontsize=16, y=1.02)
    plt.tight_layout(pad=3)
    plt.show()

# 2021年、2022年、2023年のデータに対してリリーススピードをプロット
pitch_types_2021 = df_2021['pitch_type'].unique()
pitch_types_2022 = df_2022['pitch_type'].unique()
pitch_types_2023 = df_2023['pitch_type'].unique()

# すべての年に存在する球種を取得
all_pitch_types = set(pitch_types_2021) | set(pitch_types_2022) | set(pitch_types_2023)

plot_release_speed_by_year_boxplot(df_2021, df_2022, df_2023, all_pitch_types)

</code></pre>

![](https://pbs.twimg.com/media/FuVHAA9aEAM6vnR?format=png\&name=4096x4096)

## km換算

<pre><code>import matplotlib.pyplot as plt
import pandas as pd

def mile_to_km(mile):
    return mile * 1.60934

def plot_release_speed_by_year_boxplot(df_2021, df_2022, df_2023, pitch_types):
    pitch_types = [pt for pt in pitch_types if pt is not None and pd.notna(pt)]  # nan を除外
    fig, axs = plt.subplots(len(pitch_types), 1, figsize=(8, len(pitch_types) * 4))

    for i, pitch_type in enumerate(pitch_types):
        ax = axs[i]

        grouped_2021 = df_2021[df_2021['pitch_type'] == pitch_type].dropna(subset=['release_speed'])
        grouped_2022 = df_2022[df_2022['pitch_type'] == pitch_type].dropna(subset=['release_speed'])
        grouped_2023 = df_2023[df_2023['pitch_type'] == pitch_type].dropna(subset=['release_speed'])

        data_to_plot = []
        labels = []
        if not grouped_2021.empty:
            data_to_plot.append(grouped_2021['release_speed'].apply(mile_to_km))
            labels.append('2021')
        if not grouped_2022.empty:
            data_to_plot.append(grouped_2022['release_speed'].apply(mile_to_km))
            labels.append('2022')
        if not grouped_2023.empty:
            data_to_plot.append(grouped_2023['release_speed'].apply(mile_to_km))
            labels.append('2023')

        if data_to_plot:
            bp = ax.boxplot(data_to_plot, labels=labels)
            for j, d in enumerate(data_to_plot):
                mean_val = d.mean()
                ax.text(j + 0.8, mean_val, f"{mean_val:.1f}", ha='center', va='bottom', fontsize=10, color='blue')

        ax.set_title(f"Pitch Type: {pitch_type}")
        ax.set_xlabel('Year')
        ax.set_ylabel('Release Speed (km/h)')

        # 罫線
        ax.grid(which='both', linestyle='--', color='gray', alpha=0.5)

    fig.suptitle('Release Speed by Year and Pitch Type', fontsize=16, y=1.02)
    plt.tight_layout(pad=3)
    plt.show()

# 2021年、2022年、2023年のデータに対してリリーススピードをプロット
pitch_types_2021 = df_2021['pitch_type'].unique()
pitch_types_2022 = df_2022['pitch_type'].unique()
pitch_types_2023 = df_2023['pitch_type'].unique()

# すべての年に存在する球種を取得
all_pitch_types = set(pitch_types_2021) | set(pitch_types_2022) | set(pitch_types_2023)

plot_release_speed_by_year_boxplot(df_2021, df_2022, df_2023, all_pitch_types)

</code></pre>

![](https://pbs.twimg.com/media/FuVHSlbaAAQUWVc?format=png\&name=4096x4096)

***

# release_extension: 投手のリリースポイントからホームプレートまでの距離（ft）

<pre><code>import matplotlib.pyplot as plt
import pandas as pd

def plot_release_extension_by_year_boxplot(df_2021, df_2022, df_2023, pitch_types):
    pitch_types = [pt for pt in pitch_types if pt is not None and pd.notna(pt)]  # nan を除外
    fig, axs = plt.subplots(len(pitch_types), 1, figsize=(8, len(pitch_types) * 4))

    for i, pitch_type in enumerate(pitch_types):
        ax = axs[i]

        grouped_2021 = df_2021[df_2021['pitch_type'] == pitch_type].dropna(subset=['release_extension'])
        grouped_2022 = df_2022[df_2022['pitch_type'] == pitch_type].dropna(subset=['release_extension'])
        grouped_2023 = df_2023[df_2023['pitch_type'] == pitch_type].dropna(subset=['release_extension'])

        data_to_plot = []
        labels = []
        if not grouped_2021.empty:
            data_to_plot.append(grouped_2021['release_extension'])
            labels.append('2021')
        if not grouped_2022.empty:
            data_to_plot.append(grouped_2022['release_extension'])
            labels.append('2022')
        if not grouped_2023.empty:
            data_to_plot.append(grouped_2023['release_extension'])
            labels.append('2023')

        if data_to_plot:
            bp = ax.boxplot(data_to_plot, labels=labels)
            for j, d in enumerate(data_to_plot):
                mean_val = d.mean()
                ax.text(j + 0.8, mean_val, f"{mean_val:.1f}", ha='center', va='bottom', fontsize=10, color='blue')

        ax.set_title(f"Pitch Type: {pitch_type}")
        ax.set_xlabel('Year')
        ax.set_ylabel('Release Extension')

        ax.grid(which='both', linestyle='--', color='gray', alpha=0.5)

    fig.suptitle('Release Extension by Year and Pitch Type', fontsize=16, y=1.02)
    plt.tight_layout(pad=3)
    plt.show()

pitch_types_2021 = df_2021['pitch_type'].unique()
pitch_types_2022 = df_2022['pitch_type'].unique()
pitch_types_2023 = df_2023['pitch_type'].unique()

all_pitch_types = set(pitch_types_2021) | set(pitch_types_2022) | set(pitch_types_2023)

plot_release_extension_by_year_boxplot(df_2021, df_2022, df_2023, all_pitch_types)

</code></pre>

![](https://pbs.twimg.com/media/FuVHiqhaAAEPH-h?format=png\&name=4096x4096)

***

# 2023の投球内容

## 投球カウント

雨の影響で2回で交代した試合は除く

<pre><code>df_0330 = df_660271_all_dates[df_660271_all_dates['game_date'] == '2023-03-30']
df_0405 = df_660271_all_dates[df_660271_all_dates['game_date'] == '2023-04-05']
df_0411 = df_660271_all_dates[df_660271_all_dates['game_date'] == '2023-04-11']
df_0421 = df_660271_all_dates[df_660271_all_dates['game_date'] == '2023-04-21']

# Get pitch counts for each date
pitch_counts_0330 = pitch_counts(df_0330)
pitch_counts_0405 = pitch_counts(df_0405)
pitch_counts_0411 = pitch_counts(df_0411)
pitch_counts_0421 = pitch_counts(df_0421)

# Display the results
print("2023年3月30日の球種カウント:")
print(pitch_counts_0330)
print("\n2023年4月5日の球種カウント:")
print(pitch_counts_0405)
print("\n2023年4月11日の球種カウント:")
print(pitch_counts_0411)
print("\n2023年4月21日の球種カウント:")
print(pitch_counts_0421)

</code></pre>

![](https://pbs.twimg.com/media/FuVIGUkagAAlnMe?format=png\&name=900x900)

***

## 各試合の投球カウント(円グラフ)

4/21の試合では、スプリット(FC)、カーブ(CU)が多くなった

キャッチャーが変わった影響か。

<pre><code>import matplotlib.pyplot as plt

def plot_pitch_distribution(df, date):
    df_L = df[df['stand'] == 'L']
    df_R = df[df['stand'] == 'R']

    fig, axs = plt.subplots(1, 3, figsize=(18, 6))
    plt.suptitle(f'Pitch Distribution on {date}')

    colors = {'FC': 'red', 'FF': 'blue', 'ST': 'green', 'CU': 'orange', 'SL': 'purple', 'SI': 'brown', 'FS': 'cyan', 'CS': 'magenta'}

    # Total
    df['pitch_type'].value_counts().plot(kind='pie', ax=axs[0], autopct='%.1f%%', colors=[colors[key] for key in df['pitch_type'].value_counts().index])
    axs[0].set_title('Total')
    axs[0].set_ylabel('')

    # vs Left batter
    df_L['pitch_type'].value_counts().plot(kind='pie', ax=axs[1], autopct='%.1f%%', colors=[colors[key] for key in df_L['pitch_type'].value_counts().index])
    axs[1].set_title('vs Left batter')
    axs[1].set_ylabel('')

    # vs Right batter
    df_R['pitch_type'].value_counts().plot(kind='pie', ax=axs[2], autopct='%.1f%%', colors=[colors[key] for key in df_R['pitch_type'].value_counts().index])
    axs[2].set_title('vs Right batter')
    axs[2].set_ylabel('')

    plt.show()

df_2023_03_30 = df_660271_all_dates[df_660271_all_dates['game_date'] == '2023-03-30']
df_2023_04_05 = df_660271_all_dates[df_660271_all_dates['game_date'] == '2023-04-05']
df_2023_04_11 = df_660271_all_dates[df_660271_all_dates['game_date'] == '2023-04-11']
df_2023_04_21 = df_660271_all_dates[df_660271_all_dates['game_date'] == '2023-04-21']

plot_pitch_distribution(df_2023_04_05, '2023-03-30')
plot_pitch_distribution(df_2023_04_05, '2023-04-05')
plot_pitch_distribution(df_2023_04_11, '2023-04-11')
plot_pitch_distribution(df_2023_04_21, '2023-04-21')

</code></pre>

![](https://pbs.twimg.com/media/FuVIvbiaMAMxFBF?format=jpg\&name=large)
![](https://pbs.twimg.com/media/FuVIwMPacAAZnK6?format=jpg\&name=large)
![](https://pbs.twimg.com/media/FuVIxYQaEAIDIRj?format=jpg\&name=large)
![](https://pbs.twimg.com/media/FuVIyPkaUAIMC0b?format=jpg\&name=large)

***

## 球種カウント (トータルのみ)

<pre><code>import matplotlib.pyplot as plt

def plot_pitch_distribution(df, date):
    fig, ax = plt.subplots(figsize=(6, 6))
    plt.suptitle(f'Pitch Distribution on {date}')

    colors = {'FC': 'red', 'FF': 'blue', 'ST': 'green', 'CU': 'orange', 'SL': 'purple', 'SI': 'brown', 'FS': 'cyan', 'CS': 'magenta'}

    # Total
    df['pitch_type'].value_counts().plot(kind='pie', ax=ax, autopct='%.1f%%', colors=[colors[key] for key in df['pitch_type'].value_counts().index])
    ax.set_title('Total')
    ax.set_ylabel('')

    plt.show()

df_2023_03_30 = df_660271_all_dates[df_660271_all_dates['game_date'] == '2023-03-30']
df_2023_04_05 = df_660271_all_dates[df_660271_all_dates['game_date'] == '2023-04-05']
df_2023_04_11 = df_660271_all_dates[df_660271_all_dates['game_date'] == '2023-04-11']
df_2023_04_21 = df_660271_all_dates[df_660271_all_dates['game_date'] == '2023-04-21']

plot_pitch_distribution(df_2023_04_05, '2023-03-30')
plot_pitch_distribution(df_2023_04_05, '2023-04-05')
plot_pitch_distribution(df_2023_04_11, '2023-04-11')
plot_pitch_distribution(df_2023_04_21, '2023-04-21')

</code></pre>

![](https://pbs.twimg.com/media/FuVJkVkaEAElEJg?format=png\&name=small)
![](https://pbs.twimg.com/media/FuVJlaJagAALaRn?format=png\&name=small)
![](https://pbs.twimg.com/media/FuVJmY5aUAAUC0F?format=png\&name=small)
![](https://pbs.twimg.com/media/FuVJniLaMAEkkmb?format=png\&name=small)

***

また今度見よ