Spaces:
Runtime error
Runtime error
Delete app.py
Browse files
app.py
DELETED
|
@@ -1,133 +0,0 @@
|
|
| 1 |
-
import gradio as gr
|
| 2 |
-
import pandas as pd
|
| 3 |
-
import numpy as np
|
| 4 |
-
import xgboost as xgb
|
| 5 |
-
import plotly.graph_objects as go
|
| 6 |
-
from scipy.stats import gaussian_kde
|
| 7 |
-
from pybaseball import pitching_stats, batting_stats, cache
|
| 8 |
-
|
| 9 |
-
cache.enable()
|
| 10 |
-
|
| 11 |
-
class AdvancedProEngine:
|
| 12 |
-
def __init__(self):
|
| 13 |
-
# Data Pull
|
| 14 |
-
p_raw = pitching_stats(2015, 2025, ind=1)
|
| 15 |
-
h_raw = batting_stats(2015, 2025, ind=1)
|
| 16 |
-
|
| 17 |
-
self.p_metrics = ["ERA", "SO", "WHIP", "Stuff+"]
|
| 18 |
-
self.h_metrics = ["AVG", "HR", "OPS", "wRC+"]
|
| 19 |
-
self.p_feats = ['Age', 'IP', 'FIP', 'SwStr%']
|
| 20 |
-
self.h_feats = ['Age', 'PA', 'OBP', 'ISO']
|
| 21 |
-
|
| 22 |
-
# Safe Data Cleaning
|
| 23 |
-
self.p_db = p_raw[self.p_feats + self.p_metrics + ['Name', 'Season']].dropna()
|
| 24 |
-
self.h_db = h_raw[self.h_feats + self.h_metrics + ['Name', 'Season']].dropna()
|
| 25 |
-
|
| 26 |
-
self.models = {}
|
| 27 |
-
self._train_models()
|
| 28 |
-
|
| 29 |
-
def _train_models(self):
|
| 30 |
-
# Training with raw numpy arrays to prevent DataFrame attribute errors
|
| 31 |
-
for m in self.p_metrics:
|
| 32 |
-
self.models[f"P_{m}"] = xgb.train({'objective': 'reg:squarederror'}, xgb.DMatrix(self.p_db[self.p_feats].values, label=self.p_db[m].values))
|
| 33 |
-
for m in self.h_metrics:
|
| 34 |
-
self.models[f"H_{m}"] = xgb.train({'objective': 'reg:squarederror'}, xgb.DMatrix(self.h_db[self.h_feats].values, label=self.h_db[m].values))
|
| 35 |
-
|
| 36 |
-
def simulate_2026(self, name, role, metric, n_sims=2500):
|
| 37 |
-
db = self.p_db if role == "Pitcher" else self.h_db
|
| 38 |
-
feats = self.p_feats if role == "Pitcher" else self.h_feats
|
| 39 |
-
player_latest = db[db['Name'] == name].sort_values('Season').iloc[-1:]
|
| 40 |
-
if player_latest.empty: return None
|
| 41 |
-
|
| 42 |
-
base_vals = player_latest[feats].values[0]
|
| 43 |
-
base_vals[0] += 1 # 2026 Aging
|
| 44 |
-
|
| 45 |
-
# Monte Carlo: Injecting 6% variance noise into physical traits
|
| 46 |
-
noise = np.random.normal(1.0, 0.06, (n_sims, len(base_vals)))
|
| 47 |
-
sim_inputs = base_vals * noise
|
| 48 |
-
return self.models[f"{role[0]}_{metric}"].predict(xgb.DMatrix(sim_inputs))
|
| 49 |
-
|
| 50 |
-
engine = AdvancedProEngine()
|
| 51 |
-
|
| 52 |
-
def run_ui(role, name):
|
| 53 |
-
if not name: return "", go.Figure()
|
| 54 |
-
|
| 55 |
-
metrics = engine.p_metrics if role == "Pitcher" else engine.h_metrics
|
| 56 |
-
primary_metric = metrics[0]
|
| 57 |
-
|
| 58 |
-
# 1. KPI Scorecards
|
| 59 |
-
kpi_html = "<div style='display: flex; flex-wrap: wrap; gap: 8px; justify-content: center;'>"
|
| 60 |
-
for m in metrics:
|
| 61 |
-
sims = engine.simulate_2026(name, role, m, n_sims=300)
|
| 62 |
-
mu = np.mean(sims)
|
| 63 |
-
val = f"{mu:.2f}" if mu < 10 else f"{int(mu)}"
|
| 64 |
-
kpi_html += f"<div style='background:#fff; border:1px solid #eee; padding:12px; border-radius:10px; min-width:80px; text-align:center;'>"\
|
| 65 |
-
f"<div style='font-size:0.7em; color:#777;'>{m}</div><div style='font-size:1.3em; color:#1A73E8; font-weight:bold;'>{val}</div></div>"
|
| 66 |
-
kpi_html += "</div>"
|
| 67 |
-
|
| 68 |
-
# 2. Monte Carlo Pulse with CONFIDENCE INTERVALS
|
| 69 |
-
full_sims = engine.simulate_2026(name, role, primary_metric)
|
| 70 |
-
low, med, high = np.percentile(full_sims, [5, 50, 95]) # The 90% Confidence Interval
|
| 71 |
-
|
| 72 |
-
kde = gaussian_kde(full_sims)
|
| 73 |
-
x = np.linspace(min(full_sims), max(full_sims), 250)
|
| 74 |
-
y = kde(x)
|
| 75 |
-
|
| 76 |
-
fig = go.Figure()
|
| 77 |
-
# The Probability Mountain
|
| 78 |
-
fig.add_trace(go.Scatter(x=x, y=y, fill='tozeroy', line_color='#1A73E8', fillcolor='rgba(26, 115, 232, 0.15)', name="Probability"))
|
| 79 |
-
|
| 80 |
-
# The Confidence Interval (The Ribbon)
|
| 81 |
-
fig.add_vrect(
|
| 82 |
-
x0=low, x1=high,
|
| 83 |
-
fillcolor="#1A73E8", opacity=0.15,
|
| 84 |
-
layer="below", line_width=0,
|
| 85 |
-
annotation_text=f"90% Zone: {low:.2f} - {high:.2f}",
|
| 86 |
-
annotation_position="top left"
|
| 87 |
-
)
|
| 88 |
-
|
| 89 |
-
# The Midpoint Pill (Projected Value)
|
| 90 |
-
fig.add_vline(x=med, line_dash="dash", line_color="#1A73E8", annotation_text="Projected")
|
| 91 |
-
|
| 92 |
-
fig.update_layout(
|
| 93 |
-
title=f"2026 {primary_metric} Confidence Pulse",
|
| 94 |
-
paper_bgcolor='rgba(0,0,0,0)', plot_bgcolor='rgba(0,0,0,0)',
|
| 95 |
-
height=300, margin=dict(l=10,r=10,t=50,b=10),
|
| 96 |
-
xaxis=dict(showgrid=False, zeroline=False), yaxis=dict(visible=False)
|
| 97 |
-
)
|
| 98 |
-
|
| 99 |
-
return kpi_html, fig
|
| 100 |
-
|
| 101 |
-
# --- UI with Algorithm Breakdown ---
|
| 102 |
-
with gr.Blocks(theme=gr.themes.Soft(), css=".gradio-container {max-width: 550px !important;}") as demo:
|
| 103 |
-
gr.Markdown("## π 2026 Player Intelligence")
|
| 104 |
-
|
| 105 |
-
with gr.Row():
|
| 106 |
-
role_select = gr.Radio(["Pitcher", "Hitter"], value="Pitcher", label="Role")
|
| 107 |
-
player_select = gr.Dropdown(choices=sorted(engine.p_db['Name'].unique().tolist()), label="Search Player", interactive=True)
|
| 108 |
-
|
| 109 |
-
scorecards = gr.HTML()
|
| 110 |
-
pulse_plot = gr.Plot()
|
| 111 |
-
|
| 112 |
-
# Dynamic Updates
|
| 113 |
-
role_select.change(lambda r: gr.update(choices=sorted(engine.p_db['Name'].unique().tolist() if r=="Pitcher" else engine.h_db['Name'].unique().tolist())), inputs=role_select, outputs=player_select)
|
| 114 |
-
player_select.change(run_ui, inputs=[role_select, player_select], outputs=[scorecards, pulse_plot])
|
| 115 |
-
|
| 116 |
-
# Algorithm Breakdown
|
| 117 |
-
with gr.Accordion("π ALGORITHM BREAKDOWN", open=False):
|
| 118 |
-
gr.Markdown("""
|
| 119 |
-
### π§ 1. The Brain: XGBoost
|
| 120 |
-
We use an **Extreme Gradient Boosting** ensemble to model player performance.
|
| 121 |
-
- **Math:** We minimize the Objective Function $$Obj(\\theta) = \\sum L(y_i, \\hat{y}_i) + \\sum \\Omega(f_k)$$
|
| 122 |
-
- **Logic:** This handles the non-linear relationship between physical traits (like Velocity) and results (like Strikeouts).
|
| 123 |
-
""")
|
| 124 |
-
|
| 125 |
-
gr.Markdown("""
|
| 126 |
-
### π² 2. The Stress Test: Monte Carlo
|
| 127 |
-
A single guess is risky. We run **2,500 parallel 2026 seasons** for every player.
|
| 128 |
-
- **The Probability Pulse:** The mountain chart above shows the distribution of all outcomes.
|
| 129 |
-
- **Confidence Intervals (The Ribbon):** We highlight the **5th to 95th percentile**. This defines the realistic 'Floor' and 'Ceiling' for 2026 based on historical volatility.
|
| 130 |
-
""")
|
| 131 |
-
|
| 132 |
-
|
| 133 |
-
demo.launch()
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|