|
<!DOCTYPE html> |
|
<html lang="en"> |
|
<head> |
|
<meta charset="UTF-8"> |
|
<meta name="viewport" content="width=device-width, initial-scale=1.0"> |
|
<title>Baseball Game Prediction Model</title> |
|
<script src="https://cdn.tailwindcss.com"></script> |
|
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css"> |
|
<style> |
|
.gradient-bg { |
|
background: linear-gradient(135deg, #1a2a6c 0%, #b21f1f 50%, #fdbb2d 100%); |
|
} |
|
.parameter-card:hover { |
|
transform: translateY(-5px); |
|
box-shadow: 0 10px 25px rgba(0, 0, 0, 0.2); |
|
} |
|
.result-gauge { |
|
background: linear-gradient(90deg, #e53e3e 0%, #f6e05e 50%, #48bb78 100%); |
|
height: 20px; |
|
border-radius: 10px; |
|
} |
|
.formula-box { |
|
background-color: rgba(255, 255, 255, 0.1); |
|
backdrop-filter: blur(10px); |
|
border-radius: 0.5rem; |
|
} |
|
.tooltip { |
|
position: relative; |
|
display: inline-block; |
|
} |
|
.tooltip .tooltiptext { |
|
visibility: hidden; |
|
width: 300px; |
|
background-color: #333; |
|
color: #fff; |
|
text-align: center; |
|
border-radius: 6px; |
|
padding: 10px; |
|
position: absolute; |
|
z-index: 1; |
|
bottom: 125%; |
|
left: 50%; |
|
transform: translateX(-50%); |
|
opacity: 0; |
|
transition: opacity 0.3s; |
|
font-size: 0.9rem; |
|
} |
|
.tooltip:hover .tooltiptext { |
|
visibility: visible; |
|
opacity: 1; |
|
} |
|
</style> |
|
</head> |
|
<body class="min-h-screen gradient-bg text-white"> |
|
<div class="container mx-auto px-4 py-8"> |
|
|
|
<header class="text-center mb-12"> |
|
<h1 class="text-4xl md:text-5xl font-bold mb-4">Advanced Baseball Game Prediction Model</h1> |
|
<p class="text-xl text-gray-200 max-w-3xl mx-auto"> |
|
A sophisticated logistic regression model that calculates win probabilities based on multiple performance metrics |
|
</p> |
|
</header> |
|
|
|
|
|
<div class="grid grid-cols-1 lg:grid-cols-3 gap-8"> |
|
|
|
<div class="lg:col-span-2 bg-white/10 backdrop-blur-md rounded-xl p-6 shadow-xl"> |
|
<h2 class="text-2xl font-bold mb-6 flex items-center"> |
|
<i class="fas fa-calculator mr-3"></i> Game Parameters |
|
</h2> |
|
|
|
<div class="grid grid-cols-1 md:grid-cols-2 gap-6"> |
|
|
|
<div class="parameter-card bg-gray-800/50 p-4 rounded-lg transition-all duration-300"> |
|
<div class="flex justify-between items-center mb-2"> |
|
<h3 class="font-semibold">Team Performance (TP)</h3> |
|
<div class="tooltip"> |
|
<i class="fas fa-info-circle text-blue-300"></i> |
|
<span class="tooltiptext"> |
|
Weighted average of win percentage: TP = 0.7 × (Last 10 Games Win %) + 0.3 × (Season Win %) |
|
</span> |
|
</div> |
|
</div> |
|
<div class="flex items-center space-x-4"> |
|
<div class="w-1/2"> |
|
<label class="block text-sm mb-1">Home Team</label> |
|
<input type="number" id="home-tp" class="w-full bg-gray-700 rounded px-3 py-2" step="0.01" min="0" max="1" value="0.65"> |
|
</div> |
|
<div class="w-1/2"> |
|
<label class="block text-sm mb-1">Away Team</label> |
|
<input type="number" id="away-tp" class="w-full bg-gray-700 rounded px-3 py-2" step="0.01" min="0" max="1" value="0.50"> |
|
</div> |
|
</div> |
|
</div> |
|
|
|
|
|
<div class="parameter-card bg-gray-800/50 p-4 rounded-lg transition-all duration-300"> |
|
<div class="flex justify-between items-center mb-2"> |
|
<h3 class="font-semibold">Starting Pitcher (SPS)</h3> |
|
<div class="tooltip"> |
|
<i class="fas fa-info-circle text-blue-300"></i> |
|
<span class="tooltiptext"> |
|
SPS = (League Avg. ERA / Pitcher ERA) × (League Avg. WHIP / Pitcher WHIP) |
|
</span> |
|
</div> |
|
</div> |
|
<div class="flex items-center space-x-4"> |
|
<div class="w-1/2"> |
|
<label class="block text-sm mb-1">Home Team</label> |
|
<input type="number" id="home-sps" class="w-full bg-gray-700 rounded px-3 py-2" step="0.1" min="0" value="1.2"> |
|
</div> |
|
<div class="w-1/2"> |
|
<label class="block text-sm mb-1">Away Team</label> |
|
<input type="number" id="away-sps" class="w-full bg-gray-700 rounded px-3 py-2" step="0.1" min="0" value="0.9"> |
|
</div> |
|
</div> |
|
</div> |
|
|
|
|
|
<div class="parameter-card bg-gray-800/50 p-4 rounded-lg transition-all duration-300"> |
|
<div class="flex justify-between items-center mb-2"> |
|
<h3 class="font-semibold">Bullpen Strength (BS)</h3> |
|
<div class="tooltip"> |
|
<i class="fas fa-info-circle text-blue-300"></i> |
|
<span class="tooltiptext"> |
|
Bullpen ERA and WHIP compared to league average (similar to SPS calculation) |
|
</span> |
|
</div> |
|
</div> |
|
<div class="flex items-center space-x-4"> |
|
<div class="w-1/2"> |
|
<label class="block text-sm mb-1">Home Team</label> |
|
<input type="number" id="home-bs" class="w-full bg-gray-700 rounded px-3 py-2" step="0.1" min="0" value="1.1"> |
|
</div> |
|
<div class="w-1/2"> |
|
<label class="block text-sm mb-1">Away Team</label> |
|
<input type="number" id="away-bs" class="w-full bg-gray-700 rounded px-3 py-2" step="0.1" min="0" value="0.95"> |
|
</div> |
|
</div> |
|
</div> |
|
|
|
|
|
<div class="parameter-card bg-gray-800/50 p-4 rounded-lg transition-all duration-300"> |
|
<div class="flex justify-between items-center mb-2"> |
|
<h3 class="font-semibold">Batting Strength (BAT)</h3> |
|
<div class="tooltip"> |
|
<i class="fas fa-info-circle text-blue-300"></i> |
|
<span class="tooltiptext"> |
|
BAT = 0.5 × OPS + 0.3 × wOBA + 0.2 × Runs/Game |
|
</span> |
|
</div> |
|
</div> |
|
<div class="flex items-center space-x-4"> |
|
<div class="w-1/2"> |
|
<label class="block text-sm mb-1">Home Team</label> |
|
<input type="number" id="home-bat" class="w-full bg-gray-700 rounded px-3 py-2" step="0.01" min="0" value="0.85"> |
|
</div> |
|
<div class="w-1/2"> |
|
<label class="block text-sm mb-1">Away Team</label> |
|
<input type="number" id="away-bat" class="w-full bg-gray-700 rounded px-3 py-2" step="0.01" min="0" value="0.78"> |
|
</div> |
|
</div> |
|
</div> |
|
|
|
|
|
<div class="parameter-card bg-gray-800/50 p-4 rounded-lg transition-all duration-300"> |
|
<div class="flex justify-between items-center mb-2"> |
|
<h3 class="font-semibold">Injury Impact (INJ)</h3> |
|
<div class="tooltip"> |
|
<i class="fas fa-info-circle text-blue-300"></i> |
|
<span class="tooltiptext"> |
|
INJ = Total WAR of Injured Players / Team's Total WAR |
|
</span> |
|
</div> |
|
</div> |
|
<div class="flex items-center space-x-4"> |
|
<div class="w-1/2"> |
|
<label class="block text-sm mb-1">Home Team</label> |
|
<input type="number" id="home-inj" class="w-full bg-gray-700 rounded px-3 py-2" step="0.01" min="0" max="1" value="0.05"> |
|
</div> |
|
<div class="w-1/2"> |
|
<label class="block text-sm mb-1">Away Team</label> |
|
<input type="number" id="away-inj" class="w-full bg-gray-700 rounded px-3 py-2" step="0.01" min="0" max="1" value="0.10"> |
|
</div> |
|
</div> |
|
</div> |
|
|
|
<div class="parameter-card bg-gray-800/50 p-4 rounded-lg transition-all duration-300"> |
|
<div class="flex justify-between items-center mb-2"> |
|
<h3 class="font-semibold">Head-to-Head (H2H)</h3> |
|
<div class="tooltip"> |
|
<i class="fas fa-info-circle text-blue-300"></i> |
|
<span class="tooltiptext"> |
|
Home team's win percentage against the opponent in the last 5 meetings |
|
</span> |
|
</div> |
|
</div> |
|
<input type="number" id="h2h" class="w-full bg-gray-700 rounded px-3 py-2" step="0.01" min="0" max="1" value="0.60"> |
|
</div> |
|
|
|
<div class="parameter-card bg-gray-800/50 p-4 rounded-lg transition-all duration-300"> |
|
<div class="flex justify-between items-center mb-2"> |
|
<h3 class="font-semibold">Weather Factor (WTH)</h3> |
|
<div class="tooltip"> |
|
<i class="fas fa-info-circle text-blue-300"></i> |
|
<span class="tooltiptext"> |
|
1.0 for neutral, 0.9 for rain (pitcher-friendly), 1.1 for strong wind (hitter-friendly) |
|
</span> |
|
</div> |
|
</div> |
|
<select id="weather" class="w-full bg-gray-700 rounded px-3 py-2"> |
|
<option value="1.0">Neutral Conditions</option> |
|
<option value="0.9">Rain (Pitcher-friendly)</option> |
|
<option value="1.1">Strong Wind (Hitter-friendly)</option> |
|
</select> |
|
</div> |
|
|
|
<div class="parameter-card bg-gray-800/50 p-4 rounded-lg transition-all duration-300"> |
|
<div class="flex justify-between items-center mb-2"> |
|
<h3 class="font-semibold">Home Field Advantage</h3> |
|
<div class="tooltip"> |
|
<i class="fas fa-info-circle text-blue-300"></i> |
|
<span class="tooltiptext"> |
|
Binary parameter: 1 for home team, 0 for away. Coefficient typically ~0.24 (54% win probability) |
|
</span> |
|
</div> |
|
</div> |
|
<div class="flex items-center"> |
|
<input type="checkbox" id="hfa" class="mr-2" checked> |
|
<label>Home Field Advantage Active</label> |
|
</div> |
|
</div> |
|
</div> |
|
|
|
<div class="mt-8"> |
|
<button id="calculate-btn" class="w-full bg-blue-600 hover:bg-blue-700 text-white font-bold py-3 px-4 rounded-lg transition duration-300 flex items-center justify-center"> |
|
<i class="fas fa-chart-line mr-2"></i> Calculate Win Probability |
|
</button> |
|
</div> |
|
</div> |
|
|
|
|
|
<div class="bg-white/10 backdrop-blur-md rounded-xl p-6 shadow-xl"> |
|
<h2 class="text-2xl font-bold mb-6 flex items-center"> |
|
<i class="fas fa-chart-pie mr-3"></i> Prediction Results |
|
</h2> |
|
|
|
<div id="results-container" class="text-center"> |
|
<div class="mb-8"> |
|
<h3 class="text-xl font-semibold mb-4">Win Probability</h3> |
|
<div class="flex justify-between items-center mb-2"> |
|
<span id="home-team" class="font-medium">Home Team</span> |
|
<span id="away-team" class="font-medium">Away Team</span> |
|
</div> |
|
<div class="result-gauge mb-2"> |
|
<div id="probability-gauge" class="h-full rounded-lg" style="width: 50%;"></div> |
|
</div> |
|
<div class="flex justify-between"> |
|
<span id="home-percent" class="text-xl font-bold">50%</span> |
|
<span id="away-percent" class="text-xl font-bold">50%</span> |
|
</div> |
|
</div> |
|
|
|
<div class="mb-8"> |
|
<h3 class="text-xl font-semibold mb-4">Key Factors</h3> |
|
<div class="space-y-3"> |
|
<div class="flex justify-between"> |
|
<span>Team Performance</span> |
|
<span id="tp-impact" class="font-medium">+0.15</span> |
|
</div> |
|
<div class="flex justify-between"> |
|
<span>Starting Pitcher</span> |
|
<span id="sps-impact" class="font-medium">+0.80</span> |
|
</div> |
|
<div class="flex justify-between"> |
|
<span>Batting Strength</span> |
|
<span id="bat-impact" class="font-medium">+0.07</span> |
|
</div> |
|
<div class="flex justify-between"> |
|
<span>Injury Impact</span> |
|
<span id="inj-impact" class="font-medium">-0.05</span> |
|
</div> |
|
<div class="flex justify-between"> |
|
<span>Home Field Advantage</span> |
|
<span id="hfa-impact" class="font-medium">+0.24</span> |
|
</div> |
|
</div> |
|
</div> |
|
|
|
<div class="formula-box p-4 mb-6"> |
|
<h3 class="text-lg font-semibold mb-2">Probability Formula</h3> |
|
<div class="text-sm bg-gray-800/50 p-3 rounded"> |
|
<p class="mb-1">P(Home Win) = 1 / (1 + e<sup>-z</sup>)</p> |
|
<p>Where z = β₀ + Σ(βᵢ × Xᵢ)</p> |
|
</div> |
|
</div> |
|
|
|
<div class="bg-yellow-500/20 border border-yellow-500/50 rounded-lg p-4"> |
|
<h3 class="font-semibold mb-2 flex items-center"> |
|
<i class="fas fa-lightbulb mr-2"></i> Model Interpretation |
|
</h3> |
|
<p class="text-sm"> |
|
Probabilities above 70% indicate strong favorites, while 55-60% suggests a slight edge. |
|
Values between 45-55% represent essentially even matchups. |
|
</p> |
|
</div> |
|
</div> |
|
</div> |
|
</div> |
|
|
|
|
|
<div class="mt-12 bg-gray-800/50 rounded-xl p-6 backdrop-blur-md"> |
|
<h2 class="text-2xl font-bold mb-4 flex items-center"> |
|
<i class="fas fa-square-root-alt mr-3"></i> Model Details |
|
</h2> |
|
|
|
<div class="grid grid-cols-1 md:grid-cols-2 gap-6"> |
|
<div> |
|
<h3 class="text-xl font-semibold mb-3">Logistic Regression Formula</h3> |
|
<div class="bg-black/30 p-4 rounded-lg mb-4"> |
|
<p class="text-center text-lg font-mono"> |
|
P(Home Win) = 1 / (1 + e<sup>-(β₀ + ΣβᵢXᵢ)</sup>) |
|
</p> |
|
</div> |
|
<p class="mb-3"> |
|
This logistic function converts the weighted sum of input parameters into a probability between 0 and 1. |
|
</p> |
|
<p> |
|
Each parameter (Xᵢ) represents the difference between home and away team metrics, except for binary factors like home field advantage. |
|
</p> |
|
</div> |
|
|
|
<div> |
|
<h3 class="text-xl font-semibold mb-3">Parameter Weights (β coefficients)</h3> |
|
<div class="bg-gray-700/50 rounded-lg p-4"> |
|
<div class="space-y-2"> |
|
<div class="flex justify-between"> |
|
<span>Intercept (β₀)</span> |
|
<span class="font-mono">0.50</span> |
|
</div> |
|
<div class="flex justify-between"> |
|
<span>Team Performance (β₁)</span> |
|
<span class="font-mono">2.00</span> |
|
</div> |
|
<div class="flex justify-between"> |
|
<span>Starting Pitcher (β₂)</span> |
|
<span class="font-mono">1.20</span> |
|
</div> |
|
<div class="flex justify-between"> |
|
<span>Batting Strength (β₃)</span> |
|
<span class="font-mono">1.50</span> |
|
</div> |
|
<div class="flex justify-between"> |
|
<span>Bullpen Strength (β₄)</span> |
|
<span class="font-mono">0.80</span> |
|
</div> |
|
<div class="flex justify-between"> |
|
<span>Home Field (β₅)</span> |
|
<span class="font-mono">0.24</span> |
|
</div> |
|
<div class="flex justify-between"> |
|
<span>Injury Impact (β₆)</span> |
|
<span class="font-mono">-0.50</span> |
|
</div> |
|
</div> |
|
</div> |
|
</div> |
|
</div> |
|
</div> |
|
|
|
|
|
<div class="mt-8 bg-blue-900/20 rounded-xl p-6 backdrop-blur-md border border-blue-500/30"> |
|
<h2 class="text-2xl font-bold mb-4 flex items-center"> |
|
<i class="fas fa-list-ol mr-3"></i> Example Calculation |
|
</h2> |
|
<div class="space-y-4"> |
|
<p> |
|
Given differences: TP = +0.15, SPS = +0.8, HFA = 1, INJ = -0.1 |
|
</p> |
|
<div class="bg-gray-800/50 p-4 rounded-lg"> |
|
<p class="font-mono mb-2"> |
|
z = 0.5 + (2.0 × 0.15) + (1.2 × 0.8) + (0.24 × 1) + (-0.5 × -0.1) |
|
</p> |
|
<p class="font-mono"> |
|
z = 0.5 + 0.3 + 0.96 + 0.24 + 0.05 = 2.05 |
|
</p> |
|
</div> |
|
<div class="bg-gray-800/50 p-4 rounded-lg"> |
|
<p class="font-mono"> |
|
P(Home Win) = 1 / (1 + e<sup>-2.05</sup>) ≈ 88.6% |
|
</p> |
|
</div> |
|
</div> |
|
</div> |
|
</div> |
|
|
|
<script> |
|
document.getElementById('calculate-btn').addEventListener('click', calculateProbability); |
|
|
|
function calculateProbability() { |
|
|
|
const homeTP = parseFloat(document.getElementById('home-tp').value) || 0; |
|
const awayTP = parseFloat(document.getElementById('away-tp').value) || 0; |
|
const homeSPS = parseFloat(document.getElementById('home-sps').value) || 0; |
|
const awaySPS = parseFloat(document.getElementById('away-sps').value) || 0; |
|
const homeBS = parseFloat(document.getElementById('home-bs').value) || 0; |
|
const awayBS = parseFloat(document.getElementById('away-bs').value) || 0; |
|
const homeBAT = parseFloat(document.getElementById('home-bat').value) || 0; |
|
const awayBAT = parseFloat(document.getElementById('away-bat').value) || 0; |
|
const homeINJ = parseFloat(document.getElementById('home-inj').value) || 0; |
|
const awayINJ = parseFloat(document.getElementById('away-inj').value) || 0; |
|
const h2h = parseFloat(document.getElementById('h2h').value) || 0; |
|
const weather = parseFloat(document.getElementById('weather').value) || 1.0; |
|
const hfa = document.getElementById('hfa').checked ? 1 : 0; |
|
|
|
|
|
const tpDiff = homeTP - awayTP; |
|
const spsDiff = homeSPS - awaySPS; |
|
const bsDiff = homeBS - awayBS; |
|
const batDiff = homeBAT - awayBAT; |
|
const injDiff = awayINJ - homeINJ; |
|
|
|
|
|
const beta0 = 0.50; |
|
const betaTP = 2.00; |
|
const betaSPS = 1.20; |
|
const betaBS = 0.80; |
|
const betaBAT = 1.50; |
|
const betaHFA = 0.24; |
|
const betaINJ = -0.50; |
|
const betaH2H = 0.60; |
|
const betaWTH = 0.30; |
|
|
|
|
|
let z = beta0; |
|
z += betaTP * tpDiff; |
|
z += betaSPS * spsDiff; |
|
z += betaBS * bsDiff; |
|
z += betaBAT * batDiff; |
|
z += betaHFA * hfa; |
|
z += betaINJ * injDiff; |
|
z += betaH2H * (h2h - 0.5); |
|
z += betaWTH * (weather - 1.0); |
|
|
|
|
|
const probability = 1 / (1 + Math.exp(-z)); |
|
const homePercent = (probability * 100).toFixed(1); |
|
const awayPercent = (100 - probability * 100).toFixed(1); |
|
|
|
|
|
document.getElementById('home-percent').textContent = `${homePercent}%`; |
|
document.getElementById('away-percent').textContent = `${awayPercent}%`; |
|
document.getElementById('probability-gauge').style.width = `${homePercent}%`; |
|
|
|
|
|
document.getElementById('tp-impact').textContent = (betaTP * tpDiff).toFixed(2); |
|
document.getElementById('sps-impact').textContent = (betaSPS * spsDiff).toFixed(2); |
|
document.getElementById('bat-impact').textContent = (betaBAT * batDiff).toFixed(2); |
|
document.getElementById('inj-impact').textContent = (betaINJ * injDiff).toFixed(2); |
|
document.getElementById('hfa-impact').textContent = hfa ? betaHFA.toFixed(2) : "0.00"; |
|
} |
|
|
|
|
|
calculateProbability(); |
|
</script> |
|
<p style="border-radius: 8px; text-align: center; font-size: 12px; color: #fff; margin-top: 16px;position: fixed; left: 8px; bottom: 8px; z-index: 10; background: rgba(0, 0, 0, 0.8); padding: 4px 8px;">Made with <img src="https://enzostvs-deepsite.hf.space/logo.svg" alt="DeepSite Logo" style="width: 16px; height: 16px; vertical-align: middle;display:inline-block;margin-right:3px;filter:brightness(0) invert(1);"><a href="https://enzostvs-deepsite.hf.space" style="color: #fff;text-decoration: underline;" target="_blank" >DeepSite</a> - 🧬 <a href="https://enzostvs-deepsite.hf.space?remix=LMLK/advanced-baseball-game-prediction" style="color: #fff;text-decoration: underline;" target="_blank" >Remix</a></p></body> |
|
</html> |