⚾️Pythonで分析するパ・リーグ5年間の打撃成績トレンド（2020-2024）

# はじめに
Pythonを使用してパリーグの2020年から2024年の打撃成績を分析するコードを作成しました。このコードでは、pandas、numpy、matplotlib、seabornなどのライブラリを使って、データの処理と視覚化を行います。
近年の日本プロ野球では、投高打低の傾向が指摘されていますが、実際のデータからどのような変化が見られるのでしょうか。本分析では特に以下の点に注目します。
- 各チームの打撃指標（打率、本塁打数など）の5年間の推移
- 得点生産に寄与する要素の相関関係
- リーグ全体としての打撃トレンド

なお、2020年シーズンはコロナ禍の影響のため試合数が120試合になっていることから、他シーズンと比較可能にするため143試合に換算して計算しています。

# データ引用元
日本野球機構のサイトからパシフィックリーグの2020年〜2024年シーズンまでのチーム打撃成績を引用させていただきました。

https://npb.jp/bis/2024/stats/tmb_p.html

# ライブラリ
今回の分析では、以下のライブラリを活用しています。
```
pandas: データフレーム操作とデータ分析のコア
numpy: 数値計算と配列操作
matplotlib: グラフ描画の基盤ライブラリ
seaborn: 統計的データ可視化のための高レベルインターフェース
japanize_matplotlib: 日本語フォント対応
scikit-learn: 線形回帰モデルによる傾向分析
```
これらのライブラリを組み合わせることで、データの前処理から視覚化、統計分析までを一貫して行うことができます。

# 主な機能
**データの読み込みと前処理**
- CSVデータを文字列からDataFrameに変換
- 各種指標を適切な型に変換

**チーム別指標の推移分析**
- 打率、本塁打数などの年次推移をグラフ化
- 各チームに適切なカラーコードを割り当て

**相関分析**
- 打率と得点、本塁打と得点などの相関関係を可視化
- 線形回帰モデルによる傾向分析とR²値の算出

**リーグ平均の推移**
- 打率、長打率、出塁率などの主要指標の年度別リーグ平均を追跡

**ヒートマップ分析**
- チーム別の主要指標をヒートマップで視覚化
- パフォーマンスの強み/弱みを色で表現

**トレンド分析**
- 各チームの5年間の変化を数値化
- 打率トレンドの方向性と強さを線形回帰で計算

**順位変動の追跡**
- 年度別の打率順位の変化を表示

**リーグ全体のトレンド分析**
- 主要指標の5年間の変化率を計算

# コード
```python
import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
import seaborn as sns
from matplotlib.colors import LinearSegmentedColormap
import japanize_matplotlib  # 日本語表示対応
from sklearn.linear_model import LinearRegression
from typing import List, Dict, Any, Tuple

# フォントの設定
plt.rcParams['font.size'] = 12

# チーム別の色の設定
TEAM_COLORS = {
    'ソフトバンク': '#FFC700',
    'オリックス': '#0000FF',
    '楽天': '#870C25',
    '日本ハム': '#00387F',
    'ロッテ': '#000000',
    '西武': '#0033CC'
}

def load_data() -> pd.DataFrame:
    """
    CSVデータを読み込み、DataFrameにする
    2020年の統計は143試合換算に調整
    """
    # CSVデータ
    csv_data = """年度,チーム,打率,試合,打席,打数,得点,安打,二塁打,三塁打,本塁打,塁打,打点,盗塁,盗塁刺,犠打,犠飛,四球,故意四,死球,三振,併殺打,長打率,出塁率
    2020,楽天,.258,120,4628,3990,557,1029,190,23,112,1601,534,67,30,87,30,473,9,48,918,87,.401,.341
    2020,日本ハム,.249,120,4560,3952,493,986,153,17,89,1440,472,80,23,85,31,460,9,32,878,86,.364,.330
    2020,ソフトバンク,.249,120,4480,3933,531,979,165,28,126,1578,500,99,32,81,34,398,13,34,851,50,.401,.321
    2020,オリックス,.247,120,4467,3947,442,975,166,19,90,1449,422,95,39,89,21,361,20,49,811,86,.367,.316
    2020,西武,.238,120,4440,3898,479,926,161,24,107,1456,459,85,40,60,27,411,10,44,956,74,.374,.315
    2020,ロッテ,.235,120,4505,3840,461,902,171,11,90,1365,435,87,32,96,19,491,16,58,954,80,.355,.329
    2021,オリックス,.247,143,5258,4694,551,1160,178,19,133,1775,520,50,24,80,30,384,21,70,962,104,.378,.312
    2021,ソフトバンク,.247,143,5298,4688,564,1157,201,26,132,1806,542,92,30,93,38,452,20,27,1066,80,.385,.314
    2021,楽天,.243,143,5386,4641,532,1129,178,27,108,1685,508,45,36,105,32,538,11,70,975,100,.363,.329
    2021,西武,.239,143,5245,4623,521,1107,195,20,112,1678,496,84,45,83,46,442,11,51,1006,84,.363,.310
    2021,ロッテ,.239,143,5329,4612,584,1104,233,17,126,1749,558,107,36,106,42,514,16,55,1053,90,.379,.320
    2021,日本ハム,.231,143,5251,4617,454,1067,203,24,78,1552,437,77,36,89,32,468,11,45,1207,93,.336,.306
    2022,ソフトバンク,.255,143,5384,4772,555,1218,189,34,108,1799,529,86,43,110,31,420,10,51,1096,88,.377,.320
    2022,オリックス,.246,143,5379,4723,490,1164,220,26,89,1703,466,62,39,114,35,425,26,80,987,98,.361,.317
    2022,楽天,.243,143,5458,4738,533,1150,179,29,101,1690,511,97,50,95,30,535,13,60,1063,99,.357,.325
    2022,日本ハム,.234,143,5177,4661,463,1091,214,26,100,1657,441,95,65,98,27,352,18,39,1148,67,.356,.292
    2022,ロッテ,.231,143,5300,4655,501,1075,196,15,97,1592,474,132,37,94,47,433,11,69,1052,95,.342,.303
    2022,西武,.229,143,5201,4633,464,1062,191,25,118,1657,441,60,36,78,27,426,19,37,1095,93,.358,.298
    2023,オリックス,.250,143,5324,4782,508,1194,211,17,109,1766,482,52,32,83,25,371,8,63,986,106,.369,.311
    2023,ソフトバンク,.248,143,5454,4786,536,1185,195,15,104,1722,513,73,30,107,38,470,14,53,1053,88,.360,.319
    2023,楽天,.244,143,5369,4667,513,1140,168,25,104,1670,485,102,38,125,33,490,9,52,937,108,.358,.321
    2023,ロッテ,.239,143,5414,4744,505,1135,220,12,100,1679,475,73,21,116,39,453,20,62,1011,79,.354,.311
    2023,西武,.233,143,5222,4672,435,1088,188,21,90,1588,414,80,38,90,28,387,9,45,1045,97,.340,.296
    2023,日本ハム,.231,143,5248,4688,464,1082,195,18,100,1613,443,75,49,84,25,397,10,54,1111,78,.344,.297
    2024,ソフトバンク,.259,143,5436,4798,607,1244,242,31,114,1890,584,89,31,105,32,436,23,64,1085,93,.394,.327
    2024,ロッテ,.248,143,5386,4791,493,1186,217,15,75,1658,468,64,14,93,37,429,18,36,923,105,.346,.312
    2024,日本ハム,.245,143,5364,4815,532,1181,203,26,111,1769,504,91,52,115,33,348,21,53,1068,100,.367,.301
    2024,楽天,.242,143,5350,4746,492,1149,165,40,72,1610,459,90,26,126,43,389,12,45,977,81,.339,.303
    2024,オリックス,.238,143,5208,4673,402,1113,186,18,71,1548,390,61,32,92,36,355,16,52,915,100,.331,.297
    2024,西武,.212,143,5127,4589,350,971,171,29,60,1380,334,83,36,106,27,349,14,56,994,90,.301,.274"""

    # StringIOを使ってCSVデータをDataFrameに変換
    from io import StringIO
    df = pd.read_csv(StringIO(csv_data), sep=',')

    # 数値型への変換
    for col in ['打率', '長打率', '出塁率']:
        df[col] = df[col].astype(float)

    # 2020年のデータを143試合に換算して調整
    # 試合数を確認
    games_2020 = df[df['年度'] == 2020]['試合'].iloc[0]
    adjustment_factor = 143 / games_2020

    # 累積スタッツを対象に換算（打率、長打率、出塁率は変更しない）
    cumulative_stats = ['打席', '打数', '得点', '安打', '二塁打', '三塁打', '本塁打',
                         '塁打', '打点', '盗塁', '盗塁刺', '犠打', '犠飛', '四球',
                         '故意四', '死球', '三振', '併殺打']

    # 換算処理
    adjusted_df = df.copy()
    for idx, row in df[df['年度'] == 2020].iterrows():
        for stat in cumulative_stats:
            adjusted_value = round(row[stat] * adjustment_factor)
            adjusted_df.loc[idx, stat] = adjusted_value

    # 換算したデータフレームを返す
    return adjusted_df

def plot_team_metrics_by_year(df: pd.DataFrame, metric: str) -> plt.Figure:
    """
    チーム別の指標推移をプロット
    """
    fig, ax = plt.subplots(figsize=(12, 8))

    for team in df['チーム'].unique():
        team_data = df[df['チーム'] == team]
        ax.plot(team_data['年度'], team_data[metric], 'o-', label=team, color=TEAM_COLORS.get(team, 'gray'), linewidth=2)

    ax.set_xlabel('年度')
    ax.set_ylabel(metric)
    ax.set_title(f'{metric}の推移 (2020-2024)')
    ax.grid(True, linestyle='--', alpha=0.7)
    ax.legend(loc='best')

    # X軸の目盛りを年度に合わせる
    ax.set_xticks(range(2020, 2025))

    # Y軸のフォーマットを調整（小数点表示）
    if metric == '打率' or metric == '長打率' or metric == '出塁率':
        from matplotlib.ticker import FormatStrFormatter
        ax.yaxis.set_major_formatter(FormatStrFormatter('%.3f'))

    plt.tight_layout()
    return fig

def plot_metrics_correlation(df: pd.DataFrame, x_metric: str, y_metric: str) -> plt.Figure:
    """
    二つの指標の相関関係をプロット
    """
    fig, ax = plt.subplots(figsize=(12, 8))

    for team in df['チーム'].unique():
        team_data = df[df['チーム'] == team]
        ax.scatter(team_data[x_metric], team_data[y_metric], label=team, color=TEAM_COLORS.get(team, 'gray'), s=100, alpha=0.7)

    # 各点に年度をアノテーション
    for idx, row in df.iterrows():
        ax.annotate(str(int(row['年度'])),
                   (row[x_metric], row[y_metric]),
                   xytext=(5, 0),
                   textcoords='offset points',
                   fontsize=8)

    # 回帰直線を追加
    X = df[x_metric].values.reshape(-1, 1)
    y = df[y_metric].values
    model = LinearRegression()
    model.fit(X, y)

    # 予測線
    x_range = np.linspace(df[x_metric].min(), df[x_metric].max(), 100)
    y_pred = model.predict(x_range.reshape(-1, 1))
    ax.plot(x_range, y_pred, 'r--', linewidth=2, label=f'回帰直線 (R²={model.score(X, y):.3f})')

    ax.set_xlabel(x_metric)
    ax.set_ylabel(y_metric)
    ax.set_title(f'{x_metric} vs {y_metric} 相関関係')
    ax.grid(True, linestyle='--', alpha=0.7)
    ax.legend(loc='best')

    # X軸のフォーマットを調整（小数点表示）
    if x_metric == '打率' or x_metric == '長打率' or x_metric == '出塁率':
        from matplotlib.ticker import FormatStrFormatter
        ax.xaxis.set_major_formatter(FormatStrFormatter('%.3f'))

    plt.tight_layout()
    return fig

def plot_league_average_trends(df: pd.DataFrame, metrics: List[str]) -> plt.Figure:
    """
    リーグ平均の推移をプロット
    """
    # 年度別の平均値を計算
    yearly_avg = df.groupby('年度')[metrics].mean().reset_index()

    fig, ax = plt.subplots(figsize=(12, 8))

    for metric in metrics:
        ax.plot(yearly_avg['年度'], yearly_avg[metric], 'o-', label=metric, linewidth=2)

    ax.set_xlabel('年度')
    ax.set_title('リーグ平均指標の推移 (2020-2024)')
    ax.grid(True, linestyle='--', alpha=0.7)
    ax.legend(loc='best')

    # X軸の目盛りを年度に合わせる
    ax.set_xticks(range(2020, 2025))

    # Y軸のフォーマットを調整（小数点表示）
    from matplotlib.ticker import FormatStrFormatter
    ax.yaxis.set_major_formatter(FormatStrFormatter('%.3f'))

    plt.tight_layout()
    return fig

def plot_team_metrics_heatmap(df: pd.DataFrame, metrics: List[str]) -> plt.Figure:
    """
    チーム別の指標をヒートマップで表示
    """
    # チーム別の平均値を計算
    team_avg = df.groupby('チーム')[metrics].mean().reset_index()

    # データをピボットしてヒートマップ用に整形
    pivot_data = team_avg.set_index('チーム')

    # カスタムカラーマップの作成（青から赤）
    colors = ["#4575b4", "#91bfdb", "#e0f3f8", "#fee090", "#fc8d59", "#d73027"]
    cmap = LinearSegmentedColormap.from_list("custom_cmap", colors)

    fig, ax = plt.subplots(figsize=(14, 8))

    # 表示フォーマットを指定
    fmt_dict = {
        '打率': '{:.3f}',
        '長打率': '{:.3f}',
        '出塁率': '{:.3f}',
    }

    # デフォルトのフォーマット
    def fmt(x, metric):
        if metric in fmt_dict:
            return fmt_dict[metric].format(x)
        else:
            return '{:.1f}'.format(x)

    # ヒートマップをメトリクスごとにプロット
    sns.heatmap(pivot_data, annot=True, cmap=cmap, linewidths=0.5, fmt='.3f', ax=ax)

    ax.set_title('チーム別 主要指標平均値 (2020-2024)')
    plt.tight_layout()
    return fig

def analyze_team_trends(df: pd.DataFrame) -> Dict[str, Dict[str, Any]]:
    """
    チームごとの成績変化を分析
    """
    trends = {}

    for team in df['チーム'].unique():
        team_data = df[df['チーム'] == team].sort_values('年度')

        # 初年と最終年のデータ
        first_year = team_data.iloc[0]
        last_year = team_data.iloc[-1]

        # 指標の変化
        trends[team] = {
            '打率変化': last_year['打率'] - first_year['打率'],
            '本塁打変化': last_year['本塁打'] - first_year['本塁打'],
            '得点変化': last_year['得点'] - first_year['得点'],
            '出塁率変化': last_year['出塁率'] - first_year['出塁率'],
            '長打率変化': last_year['長打率'] - first_year['長打率']
        }

        # 変化の傾向
        batting_avg_data = team_data[['年度', '打率']].values
        X = batting_avg_data[:, 0].reshape(-1, 1)
        y = batting_avg_data[:, 1]
        model = LinearRegression()
        model.fit(X, y)

        trends[team]['打率トレンド'] = model.coef_[0]

    return trends

def calculate_league_correlations(df: pd.DataFrame) -> Dict[str, float]:
    """
    指標間の相関係数を計算
    """
    metrics = ['打率', '本塁打', '得点', '四球', '三振', '長打率', '出塁率']
    corr_matrix = df[metrics].corr()

    # 得点との相関に注目
    correlations = {}
    for metric in metrics:
        if metric != '得点':
            correlations[f'{metric}と得点の相関'] = corr_matrix.loc[metric, '得点']

    return correlations

def create_performance_radar_chart(df: pd.DataFrame) -> plt.Figure:
    """
    チーム別のパフォーマンスをレーダーチャートで表示
    """
    # チーム別の平均値を計算
    team_avg = df.groupby('チーム')[['打率', '本塁打', '得点', '四球', '盗塁']].mean()

    # 値を0-1にスケーリング
    from sklearn.preprocessing import MinMaxScaler
    scaler = MinMaxScaler()
    scaled_data = scaler.fit_transform(team_avg)
    scaled_df = pd.DataFrame(scaled_data, index=team_avg.index, columns=team_avg.columns)

    # レーダーチャートの準備
    categories = ['打率', '本塁打', '得点', '四球', '盗塁']
    N = len(categories)

    # 角度の計算
    angles = [n / float(N) * 2 * np.pi for n in range(N)]
    angles += angles[:1]  # 閉じたポリゴンにするために最初の点を追加

    # チャート作成
    fig, ax = plt.subplots(figsize=(12, 10), subplot_kw=dict(polar=True))

    # 各チームのプロット
    for team in scaled_df.index:
        values = scaled_df.loc[team].values.flatten().tolist()
        values += values[:1]  # 閉じたポリゴンにするために最初の値を追加
        ax.plot(angles, values, linewidth=2, label=team, color=TEAM_COLORS.get(team, 'gray'))
        ax.fill(angles, values, alpha=0.1, color=TEAM_COLORS.get(team, 'gray'))

    # カテゴリーラベルの設定
    ax.set_xticks(angles[:-1])
    ax.set_xticklabels(categories)

    # グリッド線の設定
    ax.set_rlabel_position(0)
    plt.yticks([0.25, 0.5, 0.75], ["0.25", "0.5", "0.75"], color="grey", size=8)
    plt.ylim(0, 1)

    # タイトルと凡例
    plt.title('チーム別パフォーマンス比較 (2020-2024)')
    plt.legend(loc='upper right', bbox_to_anchor=(0.1, 0.1))

    return fig

def main():
    # データ読み込み
    df = load_data()

    print("パリーグ打撃成績分析 (2020-2024)")
    print("-" * 50)
    print("※ 2020年の統計は143試合換算値に調整済み")

    # 1. チーム別打率の推移
    fig1 = plot_team_metrics_by_year(df, '打率')
    fig1.savefig('team_batting_avg_trends.png')

    # 2. チーム別本塁打の推移
    fig2 = plot_team_metrics_by_year(df, '本塁打')
    fig2.savefig('team_homerun_trends.png')

    # 3. 打率と得点の相関
    fig3 = plot_metrics_correlation(df, '打率', '得点')
    fig3.savefig('batting_avg_runs_correlation.png')

    # 4. 本塁打と得点の相関
    fig4 = plot_metrics_correlation(df, '本塁打', '得点')
    fig4.savefig('homerun_runs_correlation.png')

    # 5. リーグ平均推移
    fig5 = plot_league_average_trends(df, ['打率', '長打率', '出塁率'])
    fig5.savefig('league_avg_trends.png')

    # 6. チーム別指標ヒートマップ
    fig6 = plot_team_metrics_heatmap(df, ['打率', '長打率', '出塁率', '本塁打', '得点', '四球', '三振'])
    fig6.savefig('team_metrics_heatmap.png')

    # 7. パフォーマンスレーダーチャート
    fig7 = create_performance_radar_chart(df)
    fig7.savefig('team_performance_radar.png')

    # 8. チーム別の変化傾向分析
    trends = analyze_team_trends(df)
    print("\nチーム別の5年間変化:")
    for team, trend in trends.items():
        print(f"\n{team}:")
        for metric, value in trend.items():
            if metric == '打率トレンド':
                direction = "上昇" if value > 0 else "下降"
                print(f"  打率トレンド: {value:.5f}/年 ({direction})")
            elif '打率' in metric or '出塁率' in metric or '長打率' in metric:
                print(f"  {metric}: {value:.3f}")
            else:
                print(f"  {metric}: {value:.1f}")

    # 9. 相関分析
    correlations = calculate_league_correlations(df)
    print("\n指標間の相関:")
    for metric, corr in correlations.items():
        print(f"  {metric}: {corr:.3f}")

    # 10. 年度別チーム順位の変動
    rank_changes = {}
    for year in df['年度'].unique():
        year_data = df[df['年度'] == year].sort_values('打率', ascending=False)
        year_data['打率順位'] = range(1, len(year_data) + 1)

        for i, row in year_data.iterrows():
            team = row['チーム']
            if team not in rank_changes:
                rank_changes[team] = {}
            rank_changes[team][year] = row['打率順位']

    print("\n打率順位の推移:")
    for team, ranks in rank_changes.items():
        rank_str = ", ".join([f"{year}: {rank}位" for year, rank in sorted(ranks.items())])
        print(f"  {team}: {rank_str}")

    # 11. リーグ全体のトレンド
    yearly_avg = df.groupby('年度')[['打率', '本塁打', '得点', '長打率', '出塁率']].mean().reset_index()

    print("\nリーグ全体の傾向:")
    for metric in ['打率', '本塁打', '得点', '長打率', '出塁率']:
        first_year = yearly_avg.iloc[0][metric]
        last_year = yearly_avg.iloc[-1][metric]
        change = last_year - first_year
        pct_change = change / first_year * 100

        direction = "上昇" if change > 0 else "下降"

        if metric in ['打率', '長打率', '出塁率']:
            print(f"  {metric}: {first_year:.3f} → {last_year:.3f} ({direction} {abs(pct_change):.1f}%)")
        else:
            print(f"  {metric}: {first_year:.1f} → {last_year:.1f} ({direction} {abs(pct_change):.1f}%)")

if __name__ == "__main__":
    main()
```

# 実行結果の例
```plaintext
% python3 sample.py
パリーグ打撃成績分析 (2020-2024)
--------------------------------------------------
※ 2020年の統計は143試合換算値に調整済み

チーム別の5年間変化:

楽天:
  打率変化: -0.016
  本塁打変化: -61.0
  得点変化: -172.0
  出塁率変化: -0.038
  長打率変化: -0.062
  打率トレンド: -0.00310/年 (下降)

日本ハム:
  打率変化: -0.004
  本塁打変化: 5.0
  得点変化: -55.0
  出塁率変化: -0.029
  長打率変化: 0.003
  打率トレンド: -0.00080/年 (下降)

ソフトバンク:
  打率変化: 0.010
  本塁打変化: -36.0
  得点変化: -26.0
  出塁率変化: 0.006
  長打率変化: -0.007
  打率トレンド: 0.00210/年 (上昇)

オリックス:
  打率変化: -0.009
  本塁打変化: -36.0
  得点変化: -125.0
  出塁率変化: -0.019
  長打率変化: -0.036
  打率トレンド: -0.00150/年 (下降)

西武:
  打率変化: -0.026
  本塁打変化: -68.0
  得点変化: -221.0
  出塁率変化: -0.041
  長打率変化: -0.073
  打率トレンド: -0.00580/年 (下降)

ロッテ:
  打率変化: 0.013
  本塁打変化: -32.0
  得点変化: -56.0
  出塁率変化: -0.017
  長打率変化: -0.009
  打率トレンド: 0.00260/年 (上昇)

指標間の相関:
  打率と得点の相関: 0.767
  本塁打と得点の相関: 0.812
  四球と得点の相関: 0.625
  三振と得点の相関: 0.177
  長打率と得点の相関: 0.918
  出塁率と得点の相関: 0.854

打率順位の推移:
  楽天: 2020: 1位, 2021: 3位, 2022: 3位, 2023: 3位, 2024: 4位
  日本ハム: 2020: 2位, 2021: 6位, 2022: 4位, 2023: 6位, 2024: 3位
  ソフトバンク: 2020: 3位, 2021: 2位, 2022: 1位, 2023: 2位, 2024: 1位
  オリックス: 2020: 4位, 2021: 1位, 2022: 2位, 2023: 1位, 2024: 5位
  西武: 2020: 5位, 2021: 4位, 2022: 6位, 2023: 5位, 2024: 6位
  ロッテ: 2020: 6位, 2021: 5位, 2022: 5位, 2023: 4位, 2024: 2位

リーグ全体の傾向:
  打率: 0.246 → 0.241 (下降 2.2%)
  本塁打: 121.8 → 83.8 (下降 31.2%)
  得点: 588.5 → 479.3 (下降 18.5%)
  長打率: 0.377 → 0.346 (下降 8.1%)
  出塁率: 0.325 → 0.302 (下降 7.1%)
```

# グラフ
### 打率と得点の相関
![batting_avg_runs_correlation.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/603047/2ada1a4c-5f3c-4658-9eca-05921b5edd83.png)
打率と得点には明確な正の相関（R²=0.588）があります。これは打率が高いチームほど得点生産力も高い傾向があることを示しています。特筆すべき点として：
- 特異点の存在: 2020年の楽天とソフトバンクは、打率の割に得点が多く、効率的な攻撃を展開していたことがわかります。これはコロナ禍の特殊なシーズンにおいて、両チームが特に効果的な攻撃戦略を採用していた可能性があります
- 2024年西武の低迷: 打率の低さが得点不足に直結しており、打線全体の不振が明らかです

### 本塁打と得点の相関
![homerun_runs_correlation.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/603047/6b75830e-fc14-4c5b-97f9-d689b64fe177.png)
本塁打と得点の相関（R²=0.659）は打率と得点の相関よりも強く、パ・リーグでは長打力が得点生産に大きく寄与していることが明らかです。このことから
- 近年の日本球界でも、メジャーリーグ同様に"フライボール革命"の影響が見られ、効率的な得点を目指すチームほど長打力を重視している可能性があります
- 2020年の楽天とソフトバンクは、本塁打数の割に得点が多く、長打だけでなく小技や走塁なども効果的に活用していたと考えられます
- 対照的に、2024年の西武は本塁打数が少なく、それに伴い得点も少ないという単純な相関関係が見られます

### リーグ平均指標の推移
![league_avg_trends.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/603047/a37b7015-e747-407b-b36c-5f2dec51b7bf.png)
リーグ全体のトレンドとして、長打率と出塁率の低下が顕著です

- 長打率: 2020年の約0.378から2024年の約0.346へと約8.1%減少。これは特に投手力の向上、ボールの変更（所謂「デッドボール化」）などの要因が考えられます
- 出塁率: 2020年の約0.325から2024年の約0.302へと約7.1%減少。打者が四球を選ぶよりも積極的に振っている可能性や、投手の制球力向上などが影響していると考えられます
- 打率: 2020年の約0.246から一旦下落した後、0.240前後で比較的安定しています。これは長打を犠牲にしてでも確実に打球を飛ばす戦略へのシフトを示唆しているかもしれません

全体的にリーグの攻撃力は低下傾向にあり、近年言われている「投高打低」の状況が数値からも確認できます。

### 打率の推移
![team_batting_avg_trends.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/603047/d08ee3e0-cd02-44a0-8fa0-73e56386c309.png)
チーム別の打率推移から、いくつかの興味深いパターンが浮かび上がります
- ソフトバンク: 2020年から2024年で打率が0.249から0.259へと上昇。リーグ全体が下降トレンドの中、逆行する形で打率を向上させています。これは選手の世代交代や打撃コーチの方針転換など、チーム内の積極的な改革の成果かもしれません
- 楽天: 2020年に0.258とリーグ最高打率だったものの、その後下落し0.242前後で安定。浅村や茂木といった主力打者の成績変動が影響していると考えられます
- 西武: 最も顕著な下降トレンドを示し、2020年の0.238から2024年には0.212まで低下。山川や森といった主力打者の不振や移籍が大きく影響していると思われます
- ロッテ: 着実な上昇傾向を示し、2020年の最下位から2024年には2位まで打率を向上。若手選手の成長や打撃コーチの指導が功を奏したと考えられます

### 本塁打の推移
![team_homerun_trends.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/603047/1aeec3c6-8d9e-4c94-8de8-3e12a54da5b3.png)
本塁打数の推移では、リーグ全体の長打力低下傾向の中でチームごとの差異が見られます
- 全チーム共通: 2020年と比較して2024年は概ね減少傾向
- 西武: 2020年の107本から2024年には60本と、最も大きな減少（約44%減）。これは山川穂高選手の不振や、メヒア選手の退団などが要因として考えられます
- 日本ハム: 2021年の78本から徐々に回復し、2024年には111本と健闘。万波中正選手の成長などが寄与していると思われます
- ソフトバンク: 2020年に150本近くを記録した後減少しているものの、2024年でも114本と比較的高い水準を維持。近藤選手や山川選手の加入や柳田悠岐選手の復調や若手の台頭が背景にあると考えられます

### チーム別主要指標平均値
![team_metrics_heatmap.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/603047/7ae4ed37-9446-4ea9-b57a-ce68e9577f94.png)
5年間の平均値を見ると、チームごとの特性が明確になります
- ソフトバンク: 打率、長打率、得点のすべてでトップクラスの数値を維持。これはバランスの良い打線構成と、首位打者級の打者（柳田、松田など）の存在によるものでしょう
- 楽天: 出塁率が最も高く（0.324）、四球の多さが特徴。選球眼の良い打者（浅村、茂木など）の存在と、チームとしての選球方針が見て取れます
- 西武: すべての主要指標で最下位クラスであり、特に出塁率の低さ（0.299）が目立ちます。かつての強力打線からの転落が数字に表れています
- ロッテ: 特徴的なのは三振の多さ（1053.0）と盗塁の多さ。積極的な攻撃姿勢とスピード重視の戦略が見て取れます

### チーム別パフォーマンス比較
![team_performance_radar.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/603047/eef119bb-b42b-4fd0-af4b-c7dcbf60eafd.png)
レーダーチャートからは各チームの攻撃戦略の特色が視覚的に理解できます
- ソフトバンク（黄色）: すべての指標でバランスよく高水準。特に打率と得点で優位性があります。いわゆる「総合力」の高さが特徴です
- 楽天（赤色）: 四球の多さが突出しており、選球眼の良さとチーム戦略が表れています
- ロッテ（黒色）: 盗塁数の多さが特徴的で、スピード野球の要素が見られます。特に近年は- 益田や佐々木など投手力も向上し、少ない得点で勝つスタイルに変化しています
- 西武（薄い青）: 全体的に低い数値で、特に2024年の大幅な落ち込みが影響しています。かつての「破壊力のある打線」から様変わりしています
（特徴的なチームを抜粋）

# 主要な発見と総合考察
### リーグ全体のトレンド
- **打撃力の全体的低下**: 5年間で打率は2.2%、本塁打は31.2%、得点は18.5%と大幅に減少しています。これは「統一球」の変更や投手力の向上などが要因と考えられます
- **長打力の顕著な低下**: 長打率の8.1%減と本塁打数の31.2%減は、単打中心の野球へのシフトを示唆しています
- **得点と相関の強い指標**: 長打率（R²=0.918）と出塁率（R²=0.854）が得点と最も強い相関を示しています。チーム戦略においては、単純な打率よりもこれらの指標向上に注力すべきことが示唆されます

### チーム別の特徴的変化
- **上昇傾向のチーム**: ソフトバンクとロッテが5年間で打率を向上させており、リーグの下降トレンドに逆行しています。特にロッテは0.013ポイントと最も大きな打率向上を見せました
- **下降傾向のチーム**: 西武が最も顕著な低下（打率-0.026、本塁打-68、得点-221）を示しており、チーム再建の困難さを表しています
- **特徴的な戦略変化**: 楽天は四球の多さを維持しつつも得点力が低下しており、選球眼の良さを得点に結びつける課題があると考えられます

### データから見る成功の要因
- **バランスの良さ**: ソフトバンクは各指標でバランスよく高水準を維持しており、5年間で最も安定した成績を収めています
- **戦略転換の成功**: ロッテは盗塁を活かしたスピード野球と、近年の打率向上の組み合わせで着実に競争力を高めています
- **若手の台頭**: 日本ハムは2021年の低迷から回復し、特に本塁打数の増加が見られます。これは若手選手の成長や新戦力の活躍が要因と考えられます

# 今後の分析の展望
本分析では打撃成績に焦点を当てましたが、より包括的な理解のためには以下のような発展的分析が考えられます
- **投手成績との相関分析**: 投手力と打撃力の関係性や、バランスの良いチーム構成の重要性
- **選手個人の成績分析**: チーム成績に特に影響を与えた主要選手の貢献度
- **セ・リーグとの比較**: 両リーグの打撃傾向の違いや共通点
- **勝率との相関**: どの打撃指標が最も勝率と相関があるか
- **機械学習による予測モデル**: 過去データから将来の成績を予測する試み

# おわりに
本記事では、Pythonによるデータ分析を通じて、パ・リーグの5年間の打撃成績を可視化し、その変化や傾向を探りました。全体として「投高打低」の傾向が数値からも確認できましたが、その中でもチームごとの戦略や特色が明らかになりました。

なお、本記事の分析や解釈は、限られたデータと特定の分析手法に基づいています。実際のチーム評価にはより多くの要因が関係していることをご理解いただければ幸いです。より正確な情報については、公式統計やチーム発表の情報をご確認ください。