Fix Players page KeyError + use_container_width deprecation
- Players page: fix regression_chart xg_col default (goals_p90 -> xg_p90) - charts.py: make regression_chart defensive against missing columns - All pages: replace use_container_width=True with width='stretch' - Verified: all 5 pages render with zero errors via Playwright browser test
This commit is contained in:
@@ -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__":
|
||||
|
||||
@@ -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__":
|
||||
|
||||
@@ -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.")
|
||||
|
||||
|
||||
@@ -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.")
|
||||
|
||||
@@ -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,
|
||||
|
||||
Reference in New Issue
Block a user