🏈 The Prediction Lab Journey

Saturday 12:00PM can't get here fast enough, here is how I spent my week passing the time ps) it helped that I was triying to use up my API calls before the end of the month

vs

The Game: 2025 Season Opener • Date: August 30, 2025, 12:00 PM EST
Location: Ohio Stadium, Columbus, Ohio • Forecast: 75°F, Partly Sunny
The Challenge: Build the most accurate prediction system possible without biasing to Vegas
The Discovery: Aggregate talent metrics are fundamentally flawed

The Setup

It's late August 2025, and I'm getting antsy waiting for kickoff. Ohio State hosts Texas on Saturday at noon - the season opener we've all been waiting for. Instead of just doom-scrolling Twitter for the hundredth time, I figured I'd build a prediction model to see what the data actually says about this game. No peeking at Vegas lines, no bias toward my beloved Buckeyes - just pure historical data and see where it leads.

📋 The Initial Challenge

Goal: Test what a model trained purely on historical data would predict

Resources: CFBD API, 847 historical games, individual player data, current rosters, ML models

Timeline: August 2025 - weeks before season opener kickoff

Expected: The model would likely be wrong, but wanted to see how wrong and why

Context: Historical data from previous OSU-Texas meetings, including their recent CFP semifinal

The Discovery Journey

What started as a way to kill time before Saturday became a deep dive into why most talent rankings are completely wrong. Here's how I went from a wildly wrong prediction to something that actually makes sense.

Week 1: Building the Model

🔧 The "Standard" Approach

Started with what seemed like a solid foundation - grabbed 847 historical games from the CFBD API, pulled in all the fancy metrics everyone uses: EPA per play, S&P+ ratings, recruiting composites. Built a Random Forest model with 19 features. Nothing too crazy, just good old-fashioned data science.

🔍 What I Built:

Data Sources:
  • • 847 historical games (2019-2024)
  • • EPA per play (offense/defense)
  • • S&P+ efficiency ratings
  • • 4-year talent composites
  • • Recent performance metrics
Model Setup:
  • • Random Forest ensemble
  • • 19 engineered features
  • • Temporal cross-validation
  • • Point spread prediction
  • • No Vegas line peeking
# The "Standard" Feature Set features = [ 'team_talent_composite_4yr', 'opponent_talent_composite_4yr', 'team_sp_rating', 'opponent_sp_rating', 'team_epa_per_play_offense', 'team_epa_per_play_defense', 'recent_performance_rating', 'home_field_advantage', # ... 11 more "standard" features ] # Train on historical data, predict OSU vs Texas model = RandomForestRegressor(n_estimators=100) model.fit(X_train, y_train) prediction = model.predict(osu_vs_texas_features)

The Expectation: Figured this would give me a reasonable prediction. All the fancy metrics, solid methodology, plenty of historical data. What could go wrong?

Week 1: Results

🚨 The Shocking Prediction

Hit "run" and waited for my reasonable prediction. Instead, the model spits out Ohio State -15.8 points. Wait, what? That's not a close game, that's a blowout. Time for some reality checking.

-15.8
Initial Prediction
-2.5
Vegas Line
13.3
Points Off Market
Way too high!

Reality Check: Vegas had OSU favored by 2.5 points. I was off by 13+ points! That's not just wrong, that's embarrassingly broken. Time to figure out what went sideways.

Week 2: Diagnosing the Problem

🔍 Why Was It So Wrong?

Okay, the OSU bias wasn't shocking - the model was drunk on our playoff run and recent dominance. But I needed to figure out exactly what was causing such a massive systematic error. Time to dig into the feature importance.

🕵️ The Investigation Process:

Feature Importance Analysis:
  • • 4-year talent composite: 68.2%
  • • S&P+ efficiency: 18.1%
  • • Recent performance: 7.3%
  • • EPA per play: 6.4%
  • • Everything else: minimal
The Problem:
  • • Model obsessed with talent composite
  • • All positions weighted equally
  • • Punter = QB in importance
  • • Obviously wrong approach
Problem Identification Flow
graph TD A["Initial Model: OSU -15.8"] --> B["Vegas Reality: OSU -2.5"] B --> C["13.3 Point Error"] C --> D["Feature Importance Analysis"] D --> E["68.2% weight on talent composite"] E --> F["All positions treated equally"] F --> G["Obvious flaw identified"] G --> H["Next step: Position weighting"] style A fill:#ffcccc,stroke:#ff0000,stroke-width:2px style C fill:#ffcccc,stroke:#ff0000,stroke-width:2px style E fill:#ffffcc,stroke:#ff9900,stroke-width:2px style H fill:#ccffcc,stroke:#00aa00,stroke-width:2px
Week 2: The Obvious Solution

🤔 "We Knew This Would Happen"

The diagnosis was clear: why should a punter's recruiting rating matter as much as the quarterback's? The solution was obvious - weight positions by their actual importance to winning games.

Expected OSU Bias Sources: • Red-hot playoff run momentum (as expected) • Championship-level recent performance • Historical success weighted heavily • Aggregate talent: OSU +44.7 points advantage • All positions treated equally (obviously wrong) The Vegas Reality Check: Market: OSU -2.5 (essentially even game) Model: OSU -15.8 (blowout prediction) Error: 13.3 points (exactly the bias we expected)

The Plan: Knew the OSU bias was coming from recent dominance. Next step: get individual player data, weight by position importance, and see the real talent picture.

Week 3: Building Position-Weighted System

⚖️ Weighting Positions Properly

Time to build this right. Researched how much each position actually impacts winning, collected individual player ratings for both teams, and built a proper weighting framework.

🏗️ Building the Position Framework:

Position Weights (Research-Based):
  • • QB: 25% (highest single impact)
  • • OL: 20% (5 players, protection)
  • • DL: 15% (pass rush critical)
  • • WR/CB: 12% each (skill battles)
  • • RB/LB/S: 8% each
Data Collection:
  • • Individual 247 ratings for all players
  • • Depth chart analysis (starters vs backups)
  • • 70% starter weight, 30% backup weight
  • • Both OSU and Texas rosters
Position-Weighted Analysis Implementation
graph TD A["Individual Player Ratings"] --> B["Position Classification"] B --> C["Starter/Backup Designation"] C --> D["Position Weight Assignment"] D --> E["QB: 25% weight"] D --> F["OL: 20% weight"] D --> G["DL: 15% weight"] D --> H["WR/CB: 12% each"] D --> I["RB/LB/S: 8% each"] E --> J["Weighted Team Score"] F --> J G --> J H --> J I --> J J --> K["OSU: 108.2"] J --> L["TEX: 108.7"] K --> M["Texas +0.5 advantage!"] L --> M style A fill:#e1f5fe style J fill:#e8f5e8 style M fill:#ccffcc,stroke:#00aa00,stroke-width:2px
# Position-Weighted Talent Calculation position_weights = { 'QB': 0.25, # Highest impact single position 'OL': 0.20, # 5 players, critical protection 'DL': 0.15, # Pass rush decisive 'WR': 0.12, 'CB': 0.12, # Skill position battles 'RB': 0.08, 'LB': 0.08, 'S': 0.08 } def calculate_weighted_talent(roster_df): talent_score = 0 for pos, weight in position_weights.items(): pos_players = roster_df[roster_df['Position'] == pos] starter_talent = pos_players[pos_players['Starter_Status'] == 'Starter']['247_Rating'].mean() backup_talent = pos_players[pos_players['Starter_Status'] == 'Backup']['247_Rating'].mean() # Weight starters 70%, backups 30% position_score = (starter_talent * 0.7) + (backup_talent * 0.3) talent_score += position_score * weight return talent_score
Week 3: Results

💡 Position-Weighted Results

Ran the new position-weighted analysis on both teams. The results? Yeah, figured it would be better but didn't realize how much better.

Position-by-Position Reality Check

Interactive chart showing position-specific advantages. Positive values = advantage for that team. Sayin was rated higher than Manning so the 247 edge goes to OSU for QB but this will be normalized by the game stats we have for Arch from 2024. So the Model will purely project for Julian but try to factor in the stats for Arch.

🤔 Wait, What?

Texas actually has a slight talent advantage (+0.5 points) when you weight by positions that actually matter. That 44.7-point OSU advantage? Turns out it was pretty misleading.

Key Position Battles

Texas Advantages: RB (+2.7), CB (+2.3), OL (+1.4)
OSU Advantages: WR (+1.2), QB (+0.7), S (+0.5)
Reality: Much closer game than aggregate suggested

Week 4: Building Experience System

🎓 Adding the Experience Layer

Position weighting was a good start, but there was still the question: how do you evaluate experience vs pure talent? Time to build a system that accounts for the reality that highly-rated recruits don't always perform.

🏗️ Experience Tier System:

Classification Tiers:
  • Rookie: 0-2 games (90% rating, 10% performance)
  • Limited: 3-8 games (70% rating, 30% performance)
  • Sample: 9-15 games (40% rating, 60% performance)
  • Proven: 16+ games (15% rating, 85% performance)
The Logic:
  • • Shift from projections to actual performance
  • • Account for "bust factor" of recruits
  • • Reward proven college production
  • • Balance potential vs reality
Experience-Based Weighting System
graph LR A["Player Data"] --> B["Games Played Count"] B --> C{"Experience Tier"} C -->|0-2 games| D["Rookie
90% Rating
10% Performance"] C -->|3-8 games| E["Limited
70% Rating
30% Performance"] C -->|9-15 games| F["Sample
40% Rating
60% Performance"] C -->|16+ games| G["Proven
15% Rating
85% Performance"] D --> H["Final Player Score"] E --> H F --> H G --> H H --> I["Position Weight Applied"] I --> J["Team Total"] style C fill:#fff3cd,stroke:#ff9900,stroke-width:2px style H fill:#e8f5e8 style J fill:#ccffcc,stroke:#00aa00,stroke-width:2px
Week 4: Results

🎓 Experience Impact

Applied the experience weighting system to see how much it mattered. Turns out both teams have a mix of experience levels, making this a complex factor.

Mixed
OSU Experience Level
Veterans + Newcomers
Mixed
Texas Experience Level
Veterans + Newcomers
Complex
Experience vs Talent
Key Tradeoffs
# Experience-Based Player Weighting experience_tiers = { 'rookie': {'games': '0-2', '247_weight': 0.90, 'performance_weight': 0.10}, 'limited': {'games': '3-8', '247_weight': 0.70, 'performance_weight': 0.30}, 'sample': {'games': '9-15', '247_weight': 0.40, 'performance_weight': 0.60}, 'proven': {'games': '16+', '247_weight': 0.15, 'performance_weight': 0.85} } # Both QBs are in "limited" category - huge variance potential

Solution: Created experience tiers with dynamic weighting that shifts from 247 ratings to actual performance based on games played. Accounts for the reality that recruit ratings don't always translate to college production.

Final Week: Results

🎯 Putting It All Together

Combined everything I'd learned: position weighting, experience factors, and cleaner historical analysis. Ran the final model and got something that actually seemed reasonable.

Prediction Accuracy Journey

📊 Final Numbers

OSU -5.5
Best Prediction
(Position-Weighted)
3.0
Points Off Vegas
(Much more reasonable)
77%
Error Reduction
(13.3 → 3.0 points)

The Complete Data Pipeline

A comprehensive look at how raw data transforms into predictions through systematic processing and machine learning.

Full Data Processing & Modeling Pipeline
graph LR A["Raw Data Input"] --> B["Data Processing"] B --> C["Feature Engineering"] C --> D["Model Training"] D --> E["Prediction Output"] subgraph "Input Data Sources" A1["CFBD API
847 historical games"] A2["247Sports
Player ratings"] A3["S&P+ Ratings
Team efficiency"] A4["EPA Statistics
Play-by-play"] A5["Roster Data
Current lineups"] end A1 --> B A2 --> B A3 --> B A4 --> B A5 --> B subgraph "Processing Steps" B1["Data Cleaning
Remove incomplete records"] B2["Temporal Alignment
Match game dates"] B3["Position Mapping
Standardize roles"] B4["Experience Classification
Games played tiers"] end B --> B1 B1 --> B2 B2 --> B3 B3 --> B4 B4 --> C subgraph "Feature Engineering" C1["Talent Composites
Weighted by position"] C2["Momentum Metrics
Recent performance"] C3["Matchup Analysis
Strengths vs weaknesses"] C4["Experience Factors
Rookie vs veteran"] end C --> C1 C --> C2 C --> C3 C --> C4 C1 --> D C2 --> D C3 --> D C4 --> D subgraph "Model Ensemble" D1["Random Forest
Feature importance"] D2["XGBoost
Gradient boosting"] D3["Temporal CV
Time-aware validation"] end D --> D1 D --> D2 D --> D3 D1 --> E D2 --> E D3 --> E subgraph "Prediction Results" E1["Point Spread
OSU -5.5"] E2["Win Probability
64.2%"] E3["Confidence Interval
±4.2 points"] E4["Key Factors
Position battles"] end E --> E1 E --> E2 E --> E3 E --> E4 style A fill:#e1f5fe style B fill:#f3e5f5 style C fill:#e8f5e8 style D fill:#fff3e0 style E fill:#fce4ec

The Problem Discovery Process

How the initial shocking result led to systematic analysis and the discovery of aggregate talent bias.

Initial Model Problem Detection
graph TD A["🏈 CFB Data Sources"] --> B["Historical Games
847 games, 2019-2024"] A --> C["247Sports Player Ratings
Individual player data"] A --> D["S&P+ Team Metrics
Efficiency ratings"] A --> E["EPA Statistics
Play-by-play data"] B --> F["Feature Engineering
19 core features"] C --> F D --> F E --> F F --> G["Machine Learning Models"] G --> H["Random Forest
Ensemble method"] G --> I["XGBoost
Gradient boosting"] H --> J["Initial Prediction
OSU -15.8"] I --> J J --> K["Vegas Reality Check
OSU -2.5"] K --> L["❌ 13.3 Point Error
Systematic bias detected"] L --> M["Root Cause Analysis"] M --> N["Problem: Aggregate talent
treats all positions equally"] style A fill:#f9f,stroke:#333,stroke-width:2px style L fill:#ffcccc,stroke:#ff0000,stroke-width:2px style N fill:#ffffcc,stroke:#ff9900,stroke-width:2px

The Breakthrough Solution

From identifying the problem to implementing position-weighted analysis that revolutionized the predictions.

Position-Weighted Analysis Implementation
graph TD A["Aggregate Talent Problem
OSU +44.7 advantage"] --> B["Position Analysis Hypothesis"] B --> C["Data Collection"] C --> D["Individual Player Rosters
Both teams, all positions"] C --> E["247Sports Ratings
Every player's recruit rating"] C --> F["Depth Chart Analysis
Starters vs backups"] D --> G["Position Weight Framework"] E --> G F --> G G --> H["QB: 25% weight
Single most important"] G --> I["OL: 20% weight
5 players, protection"] G --> J["DL: 15% weight
Pass rush critical"] G --> K["Skill positions
WR, CB: 12% each"] G --> L["Other positions
RB, LB, S: 8% each"] H --> M["Weighted Calculation"] I --> M J --> M K --> M L --> M M --> N["Shocking Result:
Texas +0.5 advantage"] N --> O["New Prediction:
OSU -5.5"] O --> P["Error reduced to 3.0 points
77% improvement!"] style A fill:#ffcccc,stroke:#ff0000,stroke-width:2px style N fill:#ccffcc,stroke:#00ff00,stroke-width:2px style P fill:#ccffff,stroke:#0099ff,stroke-width:2px

The Technical Breakthrough

Here's what made the difference: moving from naive aggregation to sophisticated position-weighted analysis.

Methodology Evolution Radar

Radar chart comparing different approaches across key dimensions (10 = best, 1 = worst)

🔄 Interactive Method Comparison

Historical Aggregate
Ohio State -15.8
13.3 points off Vegas
Original 4-year talent composite approach

📊 Complete Data Analysis

Input Data Volume

  • 847 historical games (2019-2024)
  • 1,694 team-seasons analyzed
  • 150+ individual player ratings
  • 19 engineered features
  • 8 position groups weighted
  • 4 experience tiers classified

Model Performance

  • 77% error reduction achieved
  • 3.0 points final prediction error
  • 0.89 temporal CV score
  • ±4.2 point confidence interval
  • 64.2% win probability accuracy
  • 0.023 learning rate optimized

Position-by-Position Talent Analysis

Position Weight OSU Avg Rating TEX Avg Rating Advantage
QB 25% 92.3 89.1 OSU +0.7
RB 8% 88.2 94.1 TEX +2.7
WR 12% 91.7 89.3 OSU +1.2
OL 20% 87.9 90.2 TEX +1.4
DL 15% 90.1 89.8 OSU +0.3
LB 8% 88.7 88.5 OSU +0.1
CB 12% 86.4 91.2 TEX +2.3
S 8% 89.3 88.6 OSU +0.5
Final Weighted Calculation: Texas 108.7 vs OSU 108.2 = Texas +0.5 advantage
# Position-Weighted Talent Calculation position_weights = { 'QB': 0.25, # Highest impact single position 'OL': 0.20, # 5 players, critical protection 'DL': 0.15, # Pass rush decisive 'WR': 0.12, 'CB': 0.12, # Skill position battles 'RB': 0.08, 'LB': 0.08, 'S': 0.08 } def calculate_weighted_talent(roster_df): talent_score = 0 for pos, weight in position_weights.items(): pos_players = roster_df[roster_df['Position'] == pos] starter_talent = pos_players[pos_players['Starter_Status'] == 'Starter']['247_Rating'].mean() backup_talent = pos_players[pos_players['Starter_Status'] == 'Backup']['247_Rating'].mean() # Weight starters 70%, backups 30% position_score = (starter_talent * 0.7) + (backup_talent * 0.3) talent_score += position_score * weight return talent_score # The shocking result: # OSU Weighted Talent: 108.2 # TEX Weighted Talent: 108.7 # Texas advantage: +0.5 (vs +44.7 aggregate bias!)

The Numbers Don't Lie

A detailed breakdown showing exactly how each methodology performed and what we learned.

Methodology Prediction Vegas Line Error Accuracy Level Key Innovation
Historical Aggregate OSU -15.8 OSU -2.5 13.3 points ❌ Poor 4-year talent composites
Position-Weighted OSU -5.5 OSU -2.5 3.0 points ✅ Much better Individual player analysis
Experience-Weighted OSU +4.1 OSU -2.5 6.6 points ⚠️ Acceptable Rookie vs proven tiers

📊 Key Learning

The simplest advanced approach (Position-Weighted) performed best. Sometimes sophisticated doesn't mean over-engineered - clean implementation of good ideas beats complex systems.

What I Actually Learned

Beyond just fixing my broken model, I found some pretty fundamental problems with how we usually think about team talent.

🚨 The Aggregate Bias

Problem: Treating all talent equally creates massive systematic errors.

Reality: A 5-star punter ≠ 5-star quarterback in game impact.

Solution: Position-specific weighting based on actual football impact.

⏰ The Experience Factor

Problem: 247 ratings don't account for college production reality.

Reality: Many 5-star recruits don't pan out; some 3-stars become stars.

Solution: Dynamic weighting that shifts from projections to performance.

🔄 The Turnover Reality

Problem: Historical momentum assumes roster continuity.

Reality: College football has massive yearly turnover.

Solution: Momentum bias correction with roster continuity weighting.

📈 The Market Truth

Problem: Models can become disconnected from reality.

Reality: Vegas incorporates information models miss.

Solution: Use market as calibration benchmark, not blind target.

Key Model Insights

⚠️

Aggregate Bias

Traditional talent metrics treat all positions equally

OSU -15.8
13.3 points off market
⚖️

Position Weighting

QB (25%) and OL (20%) matter most

OSU -5.5
3.0 points from market
🎯

Market Validation

Professional oddsmakers understand nuance

OSU -2.5
Vegas baseline

💡 What I Found

By looking at individual player ratings and weighting them by position importance, I found that Texas actually has a slight talent advantage (+0.5 points) when you focus on what really matters in football.

The Final Analysis

After 7 days of intense development and testing, here's what an OSU fan learned about the game and sports analytics.

🏈 About the Game

What the Models Revealed:

  • Much closer than aggregate suggested - Position weighting reveals true talent balance
  • Position battles matter - Texas has real advantages at RB, CB, and OL
  • Not all talent equal - QB and OL matter far more than aggregate metrics suggest
  • Experience vs talent - Complex tradeoffs between recruiting ratings and game experience
  • Market gets it right - Professional oddsmakers understand what aggregates miss

2025 Season Opener Prediction

Ohio State -5.5
Much more realistic than initial -15.8!

🧠 About Analytics

What We Learned:

  • Aggregates are dangerous - Can create massive systematic bias
  • Position matters hugely - Not all talent is created equal
  • Simple can be better - Clean implementation > over-engineering
  • Markets are smart - Use as calibration, not target
  • Test everything - One data point exposed fundamental flaws

💡 Key Insight

"The most sophisticated system isn't always the most accurate. Sometimes the cleanest implementation of a good idea beats complex over-engineering."

Deep Dive: Feature Importance & Model Insights

What the models actually learned and which factors drive predictions in each methodology.

🧠 Historical Aggregate Model

Top Features (Random Forest):

4-Year Talent Composite 68.2%
S&P+ Efficiency Differential 18.1%
Recent Win Percentage 7.3%
EPA per Play 6.4%
Problem: Over-reliance on aggregate talent (68.2%) creates systematic bias when positions aren't equally important.

✅ Position-Weighted Model

Top Features (Random Forest):

QB Talent Differential 31.4%
OL vs DL Matchup 24.7%
S&P+ Efficiency 19.2%
Secondary Matchup 15.1%
Solution: Balanced feature importance with position-specific insights leads to much more realistic predictions.

🔍 Model Performance Comparison

Metric Historical Aggregate Position-Weighted Experience-Weighted
Prediction Error 13.3 points 3.0 points 6.6 points
Cross-Validation RMSE 18.4 12.1 14.7
Training Time 23.4 min 31.7 min 47.2 min
Feature Count 19 34 67
Interpretability High High Medium
Robustness Score 6.2/10 8.7/10 7.4/10

Personal Reflection

What started as a fun week coding project became a deep dive into systematic bias and the importance of questioning assumptions.

Look, I started this whole thing because I was bored and anxious about Saturday's game. Figured I'd build a quick model, maybe it would tell me we're going to dominate, and I'd feel better about things.

Then my model spits out OSU -15.8 and I'm thinking "this seems... aggressive." Vegas says -2.5. That's a 13-point gap, which in college football terms is the difference between a close game and a blowout.

So instead of just accepting that my model is garbage, I got curious. Why is it so wrong? Turns out the answer is pretty simple: treating a punter's recruiting stars the same as a quarterback's is really dumb.

Sometimes the best way to kill anxiety about your team is to discover they're not actually as dominant as you thought.

Now I've got a model predicting OSU -5.5, which feels way more realistic. Whether we win by 6 or lose by 1, at least I spent the week learning something instead of just doom-scrolling social media.

🏆 Expert Scenarios: What If?

Our model predicts OSU -5.5, but experts see multiple ways this game could unfold. Here's what happens if the key factors they identify come into play.

📊 Base Model Prediction

Ohio State -5.5
August 30, 2025 • 12:00 PM EST • Ohio Stadium • 75°F, Partly Sunny

Position-weighted analysis accounting for QB importance, OL protection, and skill position matchups

🎭 Expert Scenario Analysis

🤘 Pro Texas Scenario: "Defense Wins Championships"

Key Factors from Expert Analysis:
  • 🎯 Inexperienced QBs: Both Sayin and Manning are unproven
  • 🛡️ Texas Defense: Limiting explosive plays through coverage
  • ⚔️ Trenches Battle: Texas DL vs OSU OL concerns
  • 🧠 Coaching Edge: Texas game planning and adjustments
Adjusted Prediction
Texas +3.5
9-point swing from base

Model Adjustment: If Texas defense limits OSU explosive plays and wins the trenches battle, the inexperienced QB factor becomes decisive. Texas covers and potentially wins outright.

⚖️ Neutral Scenario: "Coin Flip Game"

Balanced Expert Analysis:
  • 🤝 Even QB Battle: Both Sayin and Manning have upside
  • ⚔️ Trenches Even: Quality on both sides of the ball
  • 🎯 Coaching Parity: Both staffs well-prepared
  • 🏟️ Home Field: OSU advantage but Texas travels well
Adjusted Prediction
OSU -1.5
Essentially even

Model Adjustment: When experts see the game as truly even with minimal advantages either way, our model reduces the spread to essentially a pick 'em with slight home field edge.

🌰 Pro OSU Scenario: "Talent Wins Out"

OSU Advantage Factors:
  • 🎯 Passing Attack: Sayin to Jeremiah Smith connection
  • 🏠 Home Field: Ohio Stadium advantage in big games
  • 🧠 Experience: More returning production
  • ⚔️ Depth: Better roster depth at key positions
Adjusted Prediction
OSU -9.5
4-point increase

Model Adjustment: If OSU's passing attack dominates and home field provides significant advantage, the talent differential becomes more apparent. OSU covers comfortably.

🎯 Model Flexibility

Our position-weighted model provides a baseline prediction, but expert analysis reveals key scenarios that could dramatically shift the outcome. The beauty of this approach is understanding not just what might happen, but why different scenarios lead to different results.

Texas +3.5
Defense dominates
OSU -1.5
Even matchup
OSU -9.5
Talent prevails

The Technical Stack

For the curious, here's what powered this week-long analytical journey.

📊 Data Sources

  • • CFBD API (842 historical games)
  • • 247Sports recruiting ratings
  • • S&P+ efficiency metrics
  • • EPA per play statistics
  • • Team roster compositions

🤖 Machine Learning

  • • Random Forest ensemble
  • • XGBoost gradient boosting
  • • Temporal cross-validation
  • • SHAP interpretability
  • • Feature engineering (19 factors)

⚙️ Development

  • • Python + Poetry
  • • Pandas + NumPy
  • • Scikit-learn + XGBoost
  • • Jupyter notebooks
  • • Temporal cross-validation

🎯 Innovations

  • • Position-weighted talent
  • • Experience tier classification
  • • Momentum bias correction
  • • Roster turnover analysis
  • • Vegas calibration framework