diff --git a/dashboard/pages/01_matchday.py b/dashboard/pages/01_matchday.py index 5facb2a..f7c51f0 100644 --- a/dashboard/pages/01_matchday.py +++ b/dashboard/pages/01_matchday.py @@ -11,7 +11,7 @@ import streamlit as st from dashboard.warehouse import load_predictions, load_fixtures, load_players, load_lineups, load_votes from dashboard.viz.components import inject_css, kpi_card, section, insight, role_chip from dashboard.viz.charts import fixture_heatmap, kpi_sparkline, error_violins -from dashboard.viz.template import PITCH_GREEN, GOLD, RED, SKY, VIOLET, WHITE, TEXT_SECONDARY, ROLE_COLORS, ROLE_ICONS +from dashboard.viz.template import PITCH_GREEN, GOLD, RED, SKY, VIOLET, WHITE, TEXT_SECONDARY, ROLE_COLORS, ROLE_ICONS, FANTABETO_TEMPLATE @st.cache_data(ttl=3600) @@ -87,7 +87,7 @@ def run(): ), unsafe_allow_html=True) with k4: st.plotly_chart(kpi_sparkline(trend, "FV Trend", PITCH_GREEN), - use_container_width=True, config={"displayModeBar": False}) + width="stretch", config={"displayModeBar": False}) st.caption("Last 5 GW trend") st.divider() @@ -98,7 +98,7 @@ def run(): section("📅 Fixture Difficulty") heatmap_df = _build_fixture_heatmap(players, fixtures) fig = fixture_heatmap(heatmap_df) - st.plotly_chart(fig, use_container_width=True) + st.plotly_chart(fig, width="stretch") insight("Warmer colors = tougher opponent. Based on opponent avg FV from 25/26.") with c2: @@ -132,7 +132,7 @@ def run(): ] show.columns = ["Player", "Role", "Team", "H/A", "Opponent", "Projected FV", "Status"] st.dataframe( - show, use_container_width=True, hide_index=True, + show, width="stretch", hide_index=True, column_config={ "Player": st.column_config.TextColumn(width="medium"), "Projected FV": st.column_config.TextColumn(width="small"), @@ -151,7 +151,6 @@ def run(): ranks = list(np.cumsum(np.diff([8] + ranks, prepend=8).clip(-2, 2))) import plotly.graph_objects as go - from dashboard.viz.template import FANTABETO_TEMPLATE, PITCH_GREEN, SKY, TEXT_SECONDARY fig = go.Figure() fig.add_trace(go.Scatter( @@ -167,7 +166,7 @@ def run(): xaxis=dict(title=""), margin=dict(l=10, r=10, t=10, b=10), ) - st.plotly_chart(fig, use_container_width=True) + st.plotly_chart(fig, width="stretch") if __name__ == "__main__": diff --git a/dashboard/pages/02_players.py b/dashboard/pages/02_players.py index a7b93a1..c1a7aeb 100644 --- a/dashboard/pages/02_players.py +++ b/dashboard/pages/02_players.py @@ -108,7 +108,7 @@ def run(): section("📊 Percentile Radar") role_avg = players[players["role"] == role].mean(numeric_only=True) fig = percentile_radar(p, RADAR_METRICS, RADAR_LABELS, role_avg) - st.plotly_chart(fig, use_container_width=True) + st.plotly_chart(fig, width="stretch") insight("Values normalized vs league average for same role. Outer = better.") with r2: @@ -119,7 +119,7 @@ def run(): "games_season": p.get("games_season", 1), }]) fig = regression_chart(pdf, selected) - st.plotly_chart(fig, use_container_width=True) + st.plotly_chart(fig, width="stretch") div = p.get("goals_season", 0) - (p.get("xg_p90", 0) or 0) * p.get("games_season", 1) div_label = "overperforming" if div > 1 else ("underperforming" if div < -1 else "on par with") insight(f"{selected} is {div_label} xG by {abs(div):.1f} goals.") @@ -135,13 +135,13 @@ def run(): yellow = p.get("yellow_season", 0) red_c = p.get("red_season", 0) fig = bonus_donut(goals_26, assists_26, yellow * 0.5 + red_c * 1.0) - st.plotly_chart(fig, use_container_width=True) + st.plotly_chart(fig, width="stretch") insight(f"Season totals: {goals_26:.0f}G + {assists_26:.0f}A — {yellow:.0f}🟨 {red_c:.0f}🟥") with b2: section("⚠️ Card Risk Gauge") fig = card_gauge(p.get("yellow_per_game", 0), p.get("red_per_game", 0)) - st.plotly_chart(fig, use_container_width=True) + st.plotly_chart(fig, width="stretch") ypg = p.get("yellow_per_game", 0) if ypg > 0.2: insight(f"⚠️ High yellow risk: {ypg:.2f} per game. Consider rotation in tough fixtures.") @@ -163,7 +163,7 @@ def run(): "matchday", "vote", "goals", "fantavote" ]].sort_values("matchday", ascending=False) recent.columns = ["Matchday", "Vote", "Goals", "Fantavote"] - st.dataframe(recent, use_container_width=True, hide_index=True) + st.dataframe(recent, width="stretch", hide_index=True) if __name__ == "__main__": diff --git a/dashboard/pages/03_auction.py b/dashboard/pages/03_auction.py index f730cb6..4c11dcd 100644 --- a/dashboard/pages/03_auction.py +++ b/dashboard/pages/03_auction.py @@ -151,13 +151,13 @@ def run(): for r in ["P", "D", "C", "A"]: allocations[r] = sum(s["estimated_price"] for s in selected if s["role"] == r) fig = budget_waterfall(allocations) - st.plotly_chart(fig, use_container_width=True) + st.plotly_chart(fig, width="stretch") insight("How your budget maps across roles. Aim for ~15% GK, ~35% DEF, ~30% MID, ~20% FWD.") with c2: section("📈 Value Scatter") fig = value_scatter(players) - st.plotly_chart(fig, use_container_width=True) + st.plotly_chart(fig, width="stretch") insight("Top-right: high FV, high price. Bottom-right: value steals. " "Bubble size = games played. Dashed lines = cost-per-FV-point isolines.") @@ -186,7 +186,7 @@ def run(): # ── Grid Auction Heatmap ── section("🔢 Grid Auction Simulator") fig = _grid_heatmap(players) - st.plotly_chart(fig, use_container_width=True) + st.plotly_chart(fig, width="stretch") insight("Green = good value at that bid multiplier. Red = overpaying. " "Bid at the 'green' multiplier for each player.") diff --git a/dashboard/pages/05_lab.py b/dashboard/pages/05_lab.py index cf2f16b..a3ab0d0 100644 --- a/dashboard/pages/05_lab.py +++ b/dashboard/pages/05_lab.py @@ -147,7 +147,7 @@ def run(): error_df = _build_error_data(preds, votes) if not error_df.empty: fig = error_violins(error_df) - st.plotly_chart(fig, use_container_width=True) + st.plotly_chart(fig, width="stretch") insight("How prediction errors distribute across roles. Wider = more uncertainty.") else: st.info("No actual vote data available to compute errors.") @@ -156,7 +156,7 @@ def run(): section("🔬 Feature Importance") fig = _feature_importance_plot(players) if fig.data: - st.plotly_chart(fig, use_container_width=True) + st.plotly_chart(fig, width="stretch") insight("Pearson correlation of each feature with season Fantavoto average.") else: st.info("Insufficient numeric features for correlation analysis.") @@ -169,7 +169,7 @@ def run(): section("📐 Calibration Curve") fig = _calibration_curve(preds) if fig.data: - st.plotly_chart(fig, use_container_width=True) + st.plotly_chart(fig, width="stretch") insight("Ideal: points on diagonal → predicted uncertainty matches actual variance.") else: st.info("Bootstrap std not available for calibration.") @@ -178,7 +178,7 @@ def run(): section("📈 Backtest: League Avg per GW") fig = _backtest_chart(votes) if fig.data: - st.plotly_chart(fig, use_container_width=True) + st.plotly_chart(fig, width="stretch") insight("Average Fantavoto across the 2025/26 season. Dashed line = 6.0 baseline.") else: st.info("No vote data available.") diff --git a/dashboard/viz/charts.py b/dashboard/viz/charts.py index 040b1db..b81fa9d 100644 --- a/dashboard/viz/charts.py +++ b/dashboard/viz/charts.py @@ -112,15 +112,18 @@ def percentile_radar(player_row: pd.Series, metrics: list, labels: list, # ───────────────────────────────────────────────────────────────── def regression_chart(df: pd.DataFrame, player: str, - xg_col="goals_p90", goal_col="goals_season", + xg_col="xg_p90", goal_col="goals_season", games_col="games_season") -> go.Figure: - """Rolling actual goals vs expected with divergence shading. - Uses season-level totals as static chart. - """ + """Rolling actual goals vs expected with divergence shading.""" + actual = df[goal_col].iloc[0] if goal_col in df.columns else 0 + xg_val = df[xg_col].iloc[0] if xg_col in df.columns else 0 + games = df[games_col].iloc[0] if games_col in df.columns else 1 + expected = xg_val * 1.2 * games + fig = go.Figure() fig.add_trace(go.Bar( x=["Actual Goals", "Expected (xG * 1.2)"], - y=[df[goal_col].iloc[0], df[xg_col].iloc[0] * 1.2 * df[games_col].iloc[0]], + y=[actual, expected], marker_color=[PITCH_GREEN, SKY], texttemplate="%{y:.1f}", textposition="outside", textfont=dict(color=TEXT, size=13), showlegend=False,