前田健太選手・TJ手術前後(4/4 vs TB)

前田健太選手がトミー・ジョン手術から復活したので、手術前後のデータを比較してみます。

*   手術前：<br>2021年8月14日 vs タンパベイ・レイズ　6回　0失点
*   手術後：<br>2023年4月4日 vsマイアミ・マーリンズ　5回　1失点

\==<span style="font-size: 24px;">まとめ</span>==

*   各球種、リリースポイントの位置が低い方向へ変化している
*   各球種、球速がupしている
*   スライダー、ストレートの回転数が上昇傾向

引き続き次回も注目したい

***

# データ準備

今回は、前田健太さんの試合についてお話しましょう。

まずは、2021年8月14日のタンパベイ・レイズ戦。この試合では、前田さんは6回を投げて無失点。

そして、もう1つの試合は2023年4月4日のマイアミ・マーリンズ戦。この試合では、彼は5回を投げて1失点を記録しました。

この2つの試合の間に、前田さんはトミージョン手術を受けました。

それでは、この情報をもとに、前田健太さんの手術前後のパフォーマンスを比較してみましょう。

<pre><code>!pip install pybaseball
from pybaseball import statcast
import pandas as pd
# 2021年8月14日のデータ取得
df_2021_single_day = statcast(start_dt='2021-08-14', end_dt='2021-08-14')
df_628317_2021_single_day = df_2021_single_day[df_2021_single_day['pitcher'] == 628317]

# 2023年4月4日のデータ取得
df_2023_single_day = statcast(start_dt='2023-04-04', end_dt='2023-04-04')
df_628317_2023_single_day = df_2023_single_day[df_2023_single_day['pitcher'] == 628317]

# 両日のデータを結合
df_628317_selected_days = pd.concat([df_628317_2021_single_day, df_628317_2023_single_day])

</code></pre>

***

# 球種

2つの試合から、彼が投げた球種を調べました

<pre><code># 投球結果を抽出
df_628317 = df_628317_selected_days

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

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

</code></pre>

結果、

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

結果を見てみると、彼が投げた球種は以下の5つです。

*   SL（スライダー）
*   FS（フォークボール）
*   FF（フォーシーム・ファストボール）
*   CU（カーブ）
*   SI（シンカー）

***

# 球種カウント

球種カウントを比較してみました。

<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

# 2021年と2023年のデータに対して球種カウントを取得
pitch_counts_2021 = pitch_counts(df_628317_2021_single_day)
pitch_counts_2023 = pitch_counts(df_628317_2023_single_day)

# 結果を表示
print("2021年の球種カウント:")
print(pitch_counts_2021)
print("\n2023年の球種カウント:")
print(pitch_counts_2023)

</code></pre>

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

2021年の球種カウント:

*   カーブ（CU）: 8球（左打者 8球、右打者 0球）
*   フォーシーム・ファストボール（FF）: 18球（左打者 14球、右打者 4球）
*   フォークボール（FS）: 26球（左打者 24球、右打者 2球）
*   スライダー（SL）: 24球（左打者 15球、右打者 9球）

2023年の球種カウント:

*   カーブ（CU）: 1球（左打者 1球、右打者 0球）
*   フォーシーム・ファストボール（FF）: 17球（左打者 4球、右打者 13球）
*   フォークボール（FS）: 21球（左打者 13球、右打者 8球）
*   シンカー（SI）: 11球（左打者 0球、右打者 11球）
*   スライダー（SL）: 29球（左打者 1球、右打者 28球）

***

# 球種の分布を円グラフ

<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 = {'FF': 'red', 'SL': 'blue', 'FS': 'green', 'CU': 'orange', 'SI': 'purple'}

    # 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 and 2023 data
plot_pitch_distribution(df_628317_2021_single_day, 2021)
plot_pitch_distribution(df_628317_2023_single_day, 2023)

</code></pre>

![](https://pbs.twimg.com/media/FtM17\_raAAI-sNR?format=jpg\&name=large)
![](https://pbs.twimg.com/media/FtM1-UdaQAAlFGM?format=jpg\&name=large)

2021年の試合では、フォークボールが最も多く投げられ、全体の約36%を占めていました。次いでスライダーが約33%、フォーシーム・ファストボールが約25%、カーブが約11%でした。左打者に対してはフォークボールが特に多く投げられていましたが、右打者に対してはスライダーとフォーシーム・ファストボールが主体でした。

一方、2023年の試合では、スライダーが最も多く投げられ、全体の約43%を占めていました。次いでフォークボールが約31%、フォーシーム・ファストボールが約25%、シンカーが約16%、カーブが約1%でした。この試合でも左打者に対してはフォークボールが多く投げられていましたが、右打者に対してはスライダーが非常に多く投げられていました。

***

# 投球位置を散布図で比較

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

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

    colors = {'FF': 'red', 'SL': 'blue', 'FS': 'green', 'CU': 'orange', 'SI': 'purple'}

    # 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.1)

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

    plt.xlim(-3, 3)
    plt.ylim(-2, 6)

    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年と2023年のデータに対して投球位置をプロット
plot_pitch_location(df_628317_2021_single_day, 2021)
plot_pitch_location(df_628317_2023_single_day, 2023)

</code></pre>

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

***

# リリースポイントを散布図

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

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

    colors = {'FF': 'red', 'SL': 'blue', 'FS': 'green', 'CU': 'orange', 'SI': 'purple'}

    # 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軸のレンジを指定
    plt.xlim(-2.8, -1.8)
    plt.ylim(5.4, 6.2)

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

# 2021年と2023年のデータに対してリリースポイントをプロット
plot_release_point(df_628317_2021_single_day, 2021)
plot_release_point(df_628317_2023_single_day, 2023)

</code></pre>

![](https://pbs.twimg.com/media/FtM4SQracAE4\_1\_?format=png\&name=small)
![](https://pbs.twimg.com/media/FtM4Ui-aAAEZv8u?format=png\&name=small)

***

# リリースポイントを球種ごとに比較

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

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

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

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

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

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

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

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

        ax = axes[row][col]

        # 2021年と2023年のデータをそれぞれプロット
        data_2021 = data[data.index.isin(df_2021.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_2023['release_pos_x'], data_2023['release_pos_z'], label='2023', color='red')

        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)

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

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

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

</code></pre>

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

***

# リリーススピンレートを球種ごと (Boxplot)

このグラフでは、各球種について、2021年と2023年のリリーススピンレートをボックスプロットで表示しています。

ボックスプロットは、データのばらつきや分布を視覚的に把握するために使用されるグラフです。箱の上下の線は第1四分位数（Q1）と第3四分位数（Q3）を示し、箱の中央の線は中央値（Q2）を示しています。また、外れ値を示す点が含まれている場合もあります。

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

def plot_spin_rate_by_year_boxplot(df_2021, df_2023, pitch_types):
    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_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_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年と2023年のデータに対してリリーススピンレートをプロット
pitch_types_2021 = df_628317_2021_single_day['pitch_type'].unique()
pitch_types_2023 = df_628317_2023_single_day['pitch_type'].unique()

# 両年に存在する球種と、2021年には存在しないが2023年に存在する球種を取得
all_pitch_types = set(pitch_types_2021) | set(pitch_types_2023)

plot_spin_rate_by_year_boxplot(df_628317_2021_single_day, df_628317_2023_single_day, all_pitch_types)

</code></pre>

![](https://pbs.twimg.com/media/FtM7jZOacAE-R6K?format=png\&name=large)

***

# リリーススピードを球種ごと (Boxplot)

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

def plot_release_speed_by_year_boxplot(df_2021, df_2023, pitch_types):
    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_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_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年と2023年のデータに対してリリーススピードをプロット
pitch_types_2021 = df_628317_2021_single_day['pitch_type'].unique()
pitch_types_2023 = df_628317_2023_single_day['pitch_type'].unique()

# 両年に存在する球種と、2021年には存在しないが2023年に存在する球種を取得
all_pitch_types = set(pitch_types_2021) | set(pitch_types_2023)

plot_release_speed_by_year_boxplot(df_628317_2021_single_day, df_628317_2023_single_day, all_pitch_types)

</code></pre>

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

***

以上