1. はじめに
先日東京六大学野球を見に行った時に友人から「全国大会になかなか出れないけど強い野球部っているよね、そういった野球部って実際どれくらい強いんだろうね?」という疑問をもらいました。
日本の大学野球は東都大学野球、東京六大学、関西学生野球……と連盟ごとに完全分離された構造になっており、ほとんど公式な交流戦がありません。そして、連盟間の公式な大会としては全日本大学野球選手権や明治神宮大会があり、各連盟・地域の代表が集まって全国大会を戦います。
連盟間の実力差ってどうやって測ればいいのだろうかということで、今回、そのヒントになりそうな RPI(Ratings Percentage Index) という仕組みを用いて、2024年春季リーグ+2024年全日本大学野球選手権の結果を用いて試算・ランキングを出してみました。
2. RPIとはなにか?
以下は私が調べた範囲での理解です。間違っている可能性もありますから、そこは注意して読んでください
RPI(Ratings Percentage Index) は、アメリカの大学スポーツ(NCAA)で使われている 「勝率 × 対戦相手の強さ × 相手の相手の強さ」 を組み合わせたチーム評価指標だそうです。
勝率だけでは見えない「どれほど強い相手と戦ってきたか」を数値に反映できるのが特徴です。
NCAAでは、バスケットボール・野球・ソフトボール・サッカーなど多数の競技で公式ランキング指標として広く使われてきました。
以下、参考にした記事からの引用です
“The rating percentage index, commonly known as the RPI, is a quantity used to rank sports teams based upon a team's wins and losses and its strength of schedule. It is one of the sports rating systems by which NCAA basketball, baseball, softball, hockey, soccer, lacrosse, and volleyball teams are ranked. ”
— Wikipedia: Rating percentage index(2024)[1]
2.1 RPIの計算式
RPIは次の式で計算されます[2]。
$$
RPI = 0.25 \times WP + 0.50 \times OWP + 0.25 \times OOWP
$$
| 記号 | 意味 | 説明 |
|---|---|---|
| WP | Winning Percentage | チーム自身の勝率 |
| OWP | Opponents’ Winning Percentage | 対戦相手の勝率(自分との試合を除外) |
| OOWP | Opponents’ Opponents’ Winning Percentage | 相手の相手の勝率 |
以下、参考にした記事からの引用です。
“It will still be calculated using three statistical parameters:
Division I winning percentage (25%)
Opponents’ winning percentage (50%)
Opponents’ opponents’ winning percentage (25%)”
— American Baseball Coaches Association (ABCA, 2013)[2]
2.2 なぜ“間接比較”ができるのか?
RPIの最大の特徴は、直接対戦していなくても比較できることです。
共通の相手を介して「推移的な関係」を利用します。
例:
| 試合 | 勝者 | 敗者 |
|---|---|---|
| 東都A大 vs 関西C大 | A大 | C大 |
| 東京六B大 vs 関西C大 | B大 | — |
A大とB大は対戦していませんが、
どちらも関西C大と戦っているため、C大を媒介に比較可能になります。
これがRPIの「ネットワーク伝播構造」だそうです。
3. 日本の大学野球での課題
NCAAは大量の交流戦があるため、カンファレンス間がネットワークとしてしっかり結ばれています。
しかし日本では……
東都と東京六大学は公式の交流戦はなし、関西・東海・九州…なども交流戦があるところとないところが混在しており、公式なつながりは 全日本選手権+明治神宮大会のみといった感じです。
つまり、日本では RPIネットワークが極端に疎 になるため、「完璧な全国ランキング」を作ることは構造的に難しいです。しかし、「まずは代表校同士の比較」「リーグ間強度のざっくり把握」にRPIは使えそうだと思いました。
4. 今回使ったデータ(2024年限定)
今回は試験的に、次のデータのみを用いて計算しています:
- 2024年春季リーグ戦(東都・六大学ほか)
- 2024年全日本大学野球選手権(代表校の対戦)
4.1 CSVフォーマット
date,team,opponent,team_score,opp_score,tournament,stage
列名 内容 例
date 試合日(YYYY/MM/DD) 2025/06/10
team チーム名(大学名) 慶應義塾大
opponent 対戦相手 東洋大
team_score チームの得点 3
opp_score 相手の得点 2
tournament 大会名(大会名+年度) 第73回全日本大学野球選手権大会
stage 試合区分 league, main, semifinal, final など
🧩 CSVの例
2024/05/25,東洋大,中央大,4,2,東都大学野球春季リーグ戦,league
2024/05/25,明治大,法政大,3,1,東京六大学春季リーグ戦,league
2024/06/12,慶應義塾大,東洋大,2,3,全日本大学野球選手権大会,main
2024/06/14,明治大,近畿大,5,4,全日本大学野球選手権大会,main
2024/11/20,青山学院大,法政大,4,1,明治神宮野球大会,final
5. PythonでRPIを計算する
以下のコードは、CSVを読み込み、WP・OWP・OOWPを計算してRPIを求めるために作成したものです。
-
今回使用したCSVの形式は Date, Team A, Team B, Score A, Score B, stageであり、stage は "league"(春季リーグ)と "main"(全日本選手権)で重みづけができ、今回は league=1.0、main=1.5 としています
-
日本の大学野球にはホーム/アウェイの概念がないため、NCAAとは異なり 全試合を中立扱い としています
-
本コードは時系列RPIに対応しており、CSVに含まれる「試合が行われた日」ごとに、その日までの全試合を使ってRPIを再計算します
import pandas as pd
INPUT_CSV = "入力データ.csv"
# ==== 列名 ====
DATE_COL = "Date"
TEAM_A_COL = "Team A"
TEAM_B_COL = "Team B"
SCORE_A_COL = "Score A"
SCORE_B_COL = "Score B"
STAGE_COL = "stage"
# ==== 生データ読み込み ====
raw = pd.read_csv(INPUT_CSV)
# 日付を datetime に
raw[DATE_COL] = pd.to_datetime(raw[DATE_COL], format="%Y/%m/%d", errors="coerce")
raw = raw.sort_values(DATE_COL)
# 型整備
raw[SCORE_A_COL] = pd.to_numeric(raw[SCORE_A_COL], errors="coerce").fillna(0).astype(int)
raw[SCORE_B_COL] = pd.to_numeric(raw[SCORE_B_COL], errors="coerce").fillna(0).astype(int)
raw[STAGE_COL] = raw[STAGE_COL].fillna("league").astype(str).str.lower()
# ==== 1試合 → 2行(両視点展開)====
records = []
for _, r in raw.iterrows():
records.append({
"date": r[DATE_COL],
"team": str(r[TEAM_A_COL]).strip(),
"opp": str(r[TEAM_B_COL]).strip(),
"team_score": int(r[SCORE_A_COL]),
"opp_score": int(r[SCORE_B_COL]),
"stage": r[STAGE_COL],
})
records.append({
"date": r[DATE_COL],
"team": str(r[TEAM_B_COL]).strip(),
"opp": str(r[TEAM_A_COL]).strip(),
"team_score": int(r[SCORE_B_COL]),
"opp_score": int(r[SCORE_A_COL]),
"stage": r[STAGE_COL],
})
df = pd.DataFrame(records).sort_values("date")
# 勝ち点
def outcome(row):
if row["team_score"] > row["opp_score"]:
return 1.0
elif row["team_score"] < row["opp_score"]:
return 0.0
else:
return 0.5
df["team_point"] = df.apply(outcome, axis=1)
# stage 重み
WEIGHT = {"league": 1.0, "main": 1.5}
df["weight"] = df["stage"].map(WEIGHT).fillna(1.0)
all_dates = sorted(df["date"].unique())
all_teams = sorted(df["team"].unique())
# ==== データに対して WP/OWP/OOWP/RPI を計算する ====
def compute_rpi_for_df(df_sub: pd.DataFrame):
teams_sub = df_sub["team"].unique()
def calc_wp(team):
g = df_sub[df_sub["team"] == team]
if g.empty:
return 0.0
return (g["team_point"] * g["weight"]).sum() / g["weight"].sum()
def calc_owp(team):
g_team = df_sub[df_sub["team"] == team]
vals = []
for _, r in g_team.iterrows():
opp = r["opp"]
g_opp = df_sub[(df_sub["team"] == opp) & (df_sub["opp"] != team)]
if g_opp.empty:
continue
wp_opp = (g_opp["team_point"] * g_opp["weight"]).sum() / g_opp["weight"].sum()
vals.append(wp_opp)
return sum(vals)/len(vals) if vals else 0.0
# まず WP, OWP を全部計算
wp = {t: calc_wp(t) for t in teams_sub}
owp = {t: calc_owp(t) for t in teams_sub}
def calc_oowp(team):
g_team = df_sub[df_sub["team"] == team]
vals = []
for _, r in g_team.iterrows():
opp = r["opp"]
if opp in owp:
vals.append(owp[opp])
return sum(vals)/len(vals) if vals else 0.0
oowp = {t: calc_oowp(t) for t in teams_sub}
rpi = {t: 0.25*wp[t] + 0.50*owp[t] + 0.25*oowp[t] for t in teams_sub}
return wp, owp, oowp, rpi
# ==== 日付ごとの時系列RPI =====
rows_ts = []
for d in all_dates:
df_sub = df[df["date"] <= d]
wp_d, owp_d, oowp_d, rpi_d = compute_rpi_for_df(df_sub)
for t in sorted(wp_d.keys()):
rows_ts.append({
"date": d.date().isoformat(),
"Team": t,
"WP": round(wp_d[t], 4),
"OWP": round(owp_d[t], 4),
"OOWP": round(oowp_d[t], 4),
"RPI": round(rpi_d[t], 4),
})
ts_df = pd.DataFrame(rows_ts)
# 時系列全体
ts_df.to_csv("rpi_timeseries_2024.csv", index=False, encoding="utf-8-sig")
# 最終日のスナップショット(= 現行コードの意味に相当)
last_date = ts_df["date"].max()
final_df = ts_df[ts_df["date"] == last_date].sort_values("RPI", ascending=False)
final_df.to_csv("rpi_final_2024.csv", index=False, encoding="utf-8-sig")
print(f"📈 時系列RPI → rpi_timeseries_2024.csv")
print(f"🏁 最終日RPI → rpi_final_2024.csv(date={last_date})")
6. 得られたランキング
今回は2024年春季リーグ+2024年全日本選手権の入力のみに基づく試算結果でのTOP10です。
(2024年全日本選手権が終了した時点でのRPIランキング)
| 順位 | Team | WP | OWP | OOWP | RPI |
|---|---|---|---|---|---|
| 1 | 東日本国際大学 | 0.9062 | 0.6395 | 0.5357 | 0.6802 |
| 2 | 青山学院大学 | 0.8333 | 0.6505 | 0.5653 | 0.6749 |
| 3 | 早稲田大学 | 0.8056 | 0.6562 | 0.5455 | 0.6659 |
| 4 | 天理大学 | 0.7812 | 0.6598 | 0.5324 | 0.6583 |
| 5 | 中部学院大学 | 0.8056 | 0.6240 | 0.5276 | 0.6453 |
| 6 | 仙台大学 | 0.8846 | 0.5913 | 0.5085 | 0.6439 |
| 7 | 九州産業大学 | 0.7879 | 0.6290 | 0.5160 | 0.6405 |
| 8 | 和歌山大学 | 0.7667 | 0.6281 | 0.5089 | 0.6329 |
| 9 | 上武大学 | 0.8750 | 0.5742 | 0.5078 | 0.6328 |
| 10 | 中京大学 | 0.6667 | 0.6496 | 0.5347 | 0.6251 |
なお、東京六大学野球の各大学の順位は以下でした。
- 早稲田大学:3位/178チーム中
- 明治大学:35位/178チーム中
- 慶應義塾大学:91位/178チーム中
- 法政大学:93位/178チーム中
- 立教大学:94位/178チーム中
- 東京大学:166位/178チーム中
7. まとめ(所感や日本でRPIを使うときの注意点など)
| 観点 | 内容 |
|---|---|
| 今回の計算 | 2024年春季リーグ+全日本選手権の一部データのみを使用 |
| 注意点 | 日本は交流戦が少なく、RPIネットワークが疎になりやすいので参考程度 |
| 活用できる場面 | 代表校同士の比較、リーグ間強度の大まかな把握 |
今回のRPI試算は、「2024年の春季のデータを使い、RPIモデルが日本の大学野球でも“どの程度”動くかを試してみた」という位置づけです。 とりあえずランキング形式にはしたけども、他にも強い大学あるよなあ・・・という感じは私もあります。 今後、複数年データの追加(NCAAは単年度で計算するそうなのですが、日本の場合は交流戦があまりないので、複数年を対象にしたいと思います)、明治神宮大会の統合、リーグ別補正係数の導入などを行い、より実用的な「日本版RPI」に近づけていきたいと思います。
参考記事やサイト
[1] Wikipedia, Rating percentage index, updated 2024 (2025/11/15にアクセス)
[2] American Baseball Coaches Association (ABCA) (2013) Ground Rules: Understanding the new RPI