League of Legends Winrate Prediction Model

By Eldar · Published July 17, 2026

Predicts match outcomes using pre-game features like player skill and champion winrates, achieving 75.2% accuracy and revealing that player skill, not team composition, drives victory.

  • league-of-legends
  • classification
  • predictive-modeling
  • sports-analytics
  • feature-engineering
29 cells1 experiment3 views0 forks

Inside this notebook

# League of Legends Winrate Analysis & Winner Prediction Model **Dataset:** Season 15 Ranked Match Data — 6,830 games (EUN server, all ranks Iron–Challenger) **Source:** Kaggle — `jakubkrasuski/league-of-legends-ranked-match-data-season-15` ## Key Findings ### 🏆 Best Model: Logistic Regression - **Accuracy:** 75.2% (beats 51.9% blue-side baseline by +23.3%) - **ROC-AUC:** 81.9% - Uses 33 pre-game features: champion winrates per role, solo tier/rank, mastery points ### 🔬 Critical Insight: Champions ≠ Outcome A model using **only champion picks** (no player skill features) scored **48.5% accuracy** — worse than random. The full model's ~75% accuracy is almost entirely driven by **player skill differentials**, not team composition. This validates what high-elo players know: in a well-patched game, player skill trumps champion selection. ### 📊 Top Predictors | Feature | Importance | |---|---| | `tier_advantage` (blue team avg tier − red team avg tier) | 26.2% | | `wr_diff_avg` (avg champion winrate advantage) | 12.8% | | `teamA_avg_solo_rank` / `teamB_avg_solo_rank` | ~10% each | ### 🟦 Blue Side Advantage Blue side wins **51.9%** of games — a small but consistent edge (~1.9%). ### 📈 Strongest Champions (Season 15 EUN) | Champion | Win Rate | Games | |---|---|---| | Ivern | 62.4% | 93 | | Vex | 59.3% | 177 | | Nunu | 58.0% | 343 | | Kog'Maw | 58.0% | 300 | | Amumu | 56.8% | 570 |

# Setup: imports and data loading
import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
import seaborn as sns
from sklearn.model_selection import train_test_split, cross_val_score, GridSearchCV
from sklearn.preprocessing import LabelEncoder, StandardScaler
from sklearn.linear_model import LogisticRegression
from sklearn.ensemble import RandomForestClassifier, GradientBoostingClassifier
from sklearn.metrics import (classification_report, confusion_matrix, roc_auc_score, 
                             roc_curve, accuracy_score, precision_recall_curve, f1_score)
import warnings
warnings.filterwarnings('ignore')

plt.rcParams['figure.dpi'] = 120
plt.rcParams['figure.facecolor'] = 'white'

# Load enriched match data
…
Loaded 6,830 matches, 65 columns
Target distribution: {1: 3543, 0: 3287}
Target mean (blue side winrate): 0.519
# Download League of Legends Challenger Ranked Games dataset from Kaggle
%pip install kagglehub -q
import kagglehub
import pandas as pd
import os

# Download latest version of the dataset
path = kagglehub.dataset_download("gyejr95/league-of-legends-challenger-ranked-games2020")
print(f"Dataset downloaded to: {path}")
print(f"Files: {os.listdir(path)}")
Note: you may need to restart the kernel to use updated packages.
Downloading to /root/.cache/kagglehub/datasets/gyejr95/league-of-legends-challenger-ranked-games2020/3.archive...
Extracting files...
Dataset downloaded to: /root/.cache/kagglehub/datasets/gyejr95/league-of-legends-challenger-ranked-games2020/versions/3
Files: ['Challenger_Ranked_Games.csv', 'GrandMaster_Ranked_Games.csv', 'Master_Ranked_Games.csv']
# Explore the dataset structure
import pandas as pd
import os

base = "/root/.cache/kagglehub/datasets/gyejr95/league-of-legends-challenger-ranked-games2020/versions/3"

for fname in ['Challenger_Ranked_Games.csv', 'GrandMaster_Ranked_Games.csv', 'Master_Ranked_Games.csv']:
    path = os.path.join(base, fname)
    df = pd.read_csv(path, nrows=1)
    print(f"\n{'='*70}")
    print(f"FILE: {fname} ({os.path.getsize(path) / 1e6:.1f} MB)")
    print(f"COLUMNS ({len(df.columns)}):")
    for c in df.columns:
        print(f"  • {c}")
    # Also get row count quickly
    import subprocess
    result = subprocess.run(['wc', '-l', path], capture_output=True, text=True)
    rows = int(result.stdout.strip().split()[0]) - 1  # subtract header
…
======================================================================
FILE: Challenger_Ranked_Games.csv (4.5 MB)
COLUMNS (50):
  • gameId
  • gameDuraton
  • blueWins
  • blueFirstBlood
  • blueFirstTower
  • blueFirstBaron
  • blueFirstDragon
  • blueFirstInhibitor
  • blueDragonKills
  • blueBaronKills
  • blueTowerKills
  • blueInhibitorKills
  • blueWardPlaced
  • blueWardkills
  • blueKills
  • blueDeath
  • blueAssist
  • blueChampionDamageDealt
  • blueTotalGold
  • blueTotalMinionKills…
# Try downloading the season 15 match dataset and the diamond games dataset
%pip install kagglehub -q
import kagglehub
import os

# Check dataset structures first
for ds_name in [
    "jakubkrasuski/league-of-legends-ranked-match-data-season-15",
    "benfattori/league-of-legends-diamond-games-first-15-minutes",
    "jakejoeanderson/league-of-legends-diamond-matches-ff15"
]:
    try:
        path = kagglehub.dataset_download(ds_name)
        files = os.listdir(path)
        print(f"\n=== {ds_name} ===")
        print(f"Path: {path}")
        for f in files:
            fpath = os.path.join(path, f)
…
Note: you may need to restart the kernel to use updated packages.
Downloading to /root/.cache/kagglehub/datasets/jakubkrasuski/league-of-legends-ranked-match-data-season-15/1.archive...
Extracting files...

=== jakubkrasuski/league-of-legends-ranked-match-data-season-15 ===
Path: /root/.cache/kagglehub/datasets/jakubkrasuski/league-of-legends-ranked-match-data-season-15/versions/1
  League of Legends Ranked Match Data  Season 15 (EUN).csv (31.1 MB)
Downloading to /root/.cache/kagglehub/datasets/…
# Explore columns of all datasets
import pandas as pd
import os

datasets = {
    "Season 15 (EUN)": "/root/.cache/kagglehub/datasets/jakubkrasuski/league-of-legends-ranked-match-data-season-15/versions/1/League of Legends Ranked Match Data  Season 15 (EUN).csv",
    "Diamond First15": "/root/.cache/kagglehub/datasets/benfattori/league-of-legends-diamond-games-first-15-minutes/versions/1/MatchTimelinesFirst15.csv",
    "Diamond FF15": "/root/.cache/kagglehub/datasets/jakejoeanderson/league-of-legends-diamond-matches-ff15/versions/4/LoL_15_Diamond.csv",
}

for name, path in datasets.items():
    df = pd.read_csv(path, nrows=1)
    print(f"\n{'='*70}")
    print(f"DATASET: {name}")
    print(f"COLUMNS ({len(df.columns)}):")
    for c in df.columns:
        print(f"  • {c}")
======================================================================
DATASET: Season 15 (EUN)
COLUMNS (69):
  • game_id
  • start_utc
  • duration
  • queue
  • platform_id
  • map_id
  • game_mode
  • game_version
  • participant_id
  • champion_id
  • champion_name
  • position
  • win
  • kills
  • deaths
  • assists
  • kda_ratio
  • kill_participation
  • gold_earned
  • gold_spent
  • gold_per_min
  • damage_dealt
  • damage_per_min
  • damage_to_champ
  • damage_champ_per_min
  • damage…
# Load the Season 15 dataset - perfect for team comp prediction!
import pandas as pd
import numpy as np
import os

path = "/root/.cache/kagglehub/datasets/jakubkrasuski/league-of-legends-ranked-match-data-season-15/versions/1/League of Legends Ranked Match Data  Season 15 (EUN).csv"
df = pd.read_csv(path)
print(f"Shape: {df.shape}")
print(f"Dtypes:\n{df.dtypes.value_counts()}")
print(f"\nChampions: {df['champion_name'].nunique()}")
print(f"Positions: {df['position'].value_counts().to_dict()}")
print(f"Unique games: {df['game_id'].nunique()}")
print(f"\nWin distribution:\n{df['win'].value_counts()}")
print(f"\nSample rows (first 3):")
df.head(3)

This is a preview. Open the live notebook to see all 29 cells with their charts and full outputs, or fork it into your own Clusy workspace.

League of Legends Winrate Prediction Model | Clusy