← back to dhilgart__NCAA_eval

Function bodies 354 total

All specs Real LLM only Function bodies
_prob_color function · python · L108-L120 (13 LOC)
dashboard/lib/bracket_renderer.py
def _prob_color(prob: float) -> str:
    """Return a CSS color interpolating green→neutral→red by win probability."""
    if prob >= 0.5:
        t = (prob - 0.5) * 2.0
        r = int(108 + (40 - 108) * t)
        g = int(117 + (167 - 117) * t)
        b = int(125 + (69 - 125) * t)
    else:
        t = prob * 2.0
        r = int(220 + (108 - 220) * t)
        g = int(53 + (117 - 53) * t)
        b = int(69 + (125 - 69) * t)
    return f"#{r:02x}{g:02x}{b:02x}"
_esc function · python · L123-L125 (3 LOC)
dashboard/lib/bracket_renderer.py
def _esc(text: str) -> str:
    """HTML-escape a string."""
    return html.escape(text, quote=True)
_winner_prob function · python · L128-L136 (9 LOC)
dashboard/lib/bracket_renderer.py
def _winner_prob(
    winner: int,
    left: int,
    right: int,
    prob_matrix: npt.NDArray[np.float64],
) -> float:
    """Return the win probability for *winner* beating the opponent."""
    opponent = right if winner == left else left
    return float(prob_matrix[winner, opponent])
_resolve_round_winners function · python · L139-L159 (21 LOC)
dashboard/lib/bracket_renderer.py
def _resolve_round_winners(
    prev_teams: list[int],
    round_winners: list[int],
    prob_matrix: npt.NDArray[np.float64],
) -> list[tuple[int, float]]:
    """Pair previous-round survivors, look up winner probabilities.

    Args:
        prev_teams: Team indices from the previous round (or seeds).
        round_winners: Winner team indices for this round's games.
        prob_matrix: Pairwise probability matrix.

    Returns:
        List of ``(winner_index, win_prob)`` tuples.
    """
    result: list[tuple[int, float]] = []
    for g_idx, winner in enumerate(round_winners):
        left = prev_teams[g_idx * 2]
        right = prev_teams[g_idx * 2 + 1]
        result.append((winner, _winner_prob(winner, left, right, prob_matrix)))
    return result
_build_region_rounds function · python · L162-L189 (28 LOC)
dashboard/lib/bracket_renderer.py
def _build_region_rounds(
    region_idx: int,
    rounds: list[list[int]],
    prob_matrix: npt.NDArray[np.float64],
) -> list[list[tuple[int, float]]]:
    """Build rounds for one region: seeds + R64 winners through E8 winner.

    Returns:
        List of 5 round entries.  Index 0 is the seed row (16 teams,
        probability=1.0); indices 1-4 are R64, R32, S16, E8 winners.
    """
    start = _REGION_STARTS[region_idx]
    seed_teams = list(range(start, start + 16))
    region_rounds: list[list[tuple[int, float]]] = [[(t, 1.0) for t in seed_teams]]

    # Track survivors through 4 intra-region rounds
    prev: list[int] = seed_teams
    games_per_round = [8, 4, 2, 1]
    round_offsets = [region_idx * g for g in games_per_round]
    for r_idx in range(4):
        g = games_per_round[r_idx]
        off = round_offsets[r_idx]
        winners_list = rounds[r_idx][off : off + g]
        rnd = _resolve_round_winners(prev, winners_list, prob_matrix)
        region_rounds.append(rnd)
    
_team_cell function · python · L192-L213 (22 LOC)
dashboard/lib/bracket_renderer.py
def _team_cell(  # noqa: PLR0913
    team_idx: int,
    prob: float,
    bracket_team_ids: tuple[int, ...],
    team_labels: Mapping[int, str],
    seed_map: Mapping[int, int],
    *,
    is_seed: bool = False,
) -> str:
    """Render one team cell as HTML."""
    label = _esc(team_labels.get(team_idx, str(team_idx)))
    tid = bracket_team_ids[team_idx]
    seed = seed_map.get(tid, 0)
    bg = _prob_color(prob) if not is_seed else "#1e2130"
    prob_str = "" if is_seed else f"<span class='prob'>{prob:.0%}</span>"
    return (
        f"<div class='team' style='background:{bg};'>"
        f"<span class='seed'>{seed}</span>"
        f"<span class='name'>{label}</span>"
        f"{prob_str}"
        f"</div>"
    )
_render_region_html function · python · L216-L237 (22 LOC)
dashboard/lib/bracket_renderer.py
def _render_region_html(  # noqa: PLR0913
    region_rounds: list[list[tuple[int, float]]],
    region_name: str,
    bracket_team_ids: tuple[int, ...],
    team_labels: Mapping[int, str],
    seed_map: Mapping[int, int],
    *,
    mirror: bool = False,
) -> str:
    """Render a region bracket (5 columns: seeds + 4 rounds)."""
    cols: list[str] = []
    for i, rd in enumerate(region_rounds):
        cells = "".join(
            _team_cell(t, p, bracket_team_ids, team_labels, seed_map, is_seed=(i == 0)) for t, p in rd
        )
        cols.append(f"<div class='round'>{cells}</div>")

    if mirror:
        cols = list(reversed(cols))

    inner = "".join(cols)
    return f"<div class='region' data-region='{region_name}'>{inner}</div>"
Want this analysis on your repo? https://repobility.com/scan/
render_bracket_html function · python · L240-L313 (74 LOC)
dashboard/lib/bracket_renderer.py
def render_bracket_html(
    bracket_team_ids: tuple[int, ...],
    most_likely_winners: tuple[int, ...],
    team_labels: Mapping[int, str],
    seed_map: Mapping[int, int],
    prob_matrix: npt.NDArray[np.float64],
) -> str:
    """Render a 64-team bracket as a self-contained HTML/CSS string.

    The bracket shows the most-likely path through all 6 rounds with
    win probabilities color-coded green (favorites) to red (upsets).

    Args:
        bracket_team_ids: 64 team IDs in bracket-position order (leaf order).
        most_likely_winners: 63 team **indices** (into bracket_team_ids) in
            round-major order: games 0-31 = R64, 32-47 = R32, 48-55 = S16,
            56-59 = E8, 60-61 = F4, 62 = Championship.
        team_labels: Mapping of bracket index → display label (e.g. "[1] Duke").
        seed_map: Mapping of team_id → seed number.
        prob_matrix: Pairwise win probability matrix, shape (64, 64).

    Returns:
        Self-contained HTML string with embedded CSS.
get_data_dir function · python · L56-L58 (3 LOC)
dashboard/lib/filters.py
def get_data_dir() -> Path:
    """Resolve the project ``data/`` directory."""
    return Path(__file__).resolve().parent.parent.parent / "data"
load_available_years function · python · L62-L80 (19 LOC)
dashboard/lib/filters.py
def load_available_years(data_dir: str) -> list[int]:
    """Return sorted list of available season years.

    Args:
        data_dir: String path to the project data directory.

    Returns:
        Descending-sorted list of season years, or empty list if the data
        directory does not exist or cannot be read.
    """
    path = Path(data_dir)
    if not path.exists():
        return []
    try:
        repo = ParquetRepository(path)
        seasons = repo.get_seasons()
        return sorted((s.year for s in seasons), reverse=True)
    except OSError:
        return []
load_available_runs function · python · L84-L101 (18 LOC)
dashboard/lib/filters.py
def load_available_runs(data_dir: str) -> list[dict[str, object]]:
    """Return serialised metadata for every saved model run.

    Args:
        data_dir: String path to the project data directory.

    Returns:
        List of dicts (one per run), serialised via ``ModelRun.model_dump()``,
        or empty list if the data directory does not exist or cannot be read.
    """
    path = Path(data_dir)
    if not path.exists():
        return []
    try:
        store = RunStore(path)
        return [run.model_dump() for run in store.list_runs()]
    except OSError:
        return []
load_leaderboard_data function · python · L105-L154 (50 LOC)
dashboard/lib/filters.py
def load_leaderboard_data(data_dir: str) -> list[dict[str, object]]:
    """Load leaderboard data: run metadata joined with metric summaries.

    Args:
        data_dir: String path to the project data directory.

    Returns:
        List of dicts (serializable for st.cache_data) with keys:
        run_id, model_type, timestamp, start_year, end_year, year,
        log_loss, brier_score, roc_auc, ece.
    """
    path = Path(data_dir)
    if not path.exists():
        return []
    try:
        store = RunStore(path)
        runs = store.list_runs()
        summaries = store.load_all_summaries()
        if summaries.empty:
            return []
        runs_meta = pd.DataFrame(
            [
                {
                    "run_id": r.run_id,
                    "model_type": r.model_type,
                    "timestamp": str(r.timestamp),
                    "start_year": r.start_year,
                    "end_year": r.end_year,
                }
                for r in runs
 
load_fold_predictions function · python · L158-L179 (22 LOC)
dashboard/lib/filters.py
def load_fold_predictions(data_dir: str, run_id: str) -> list[dict[str, object]]:
    """Load fold-level CV predictions for a run.

    Args:
        data_dir: String path to the project data directory.
        run_id: The model run identifier.

    Returns:
        List of dicts with keys [year, game_id, team_a_id, team_b_id,
        pred_win_prob, team_a_won], or empty list if unavailable.
    """
    path = Path(data_dir)
    if not path.exists():
        return []
    try:
        store = RunStore(path)
        df = store.load_fold_predictions(run_id)
        if df is None:
            return []
        return cast(list[dict[str, object]], df.to_dict("records"))
    except OSError:
        return []
load_feature_importances function · python · L183-L218 (36 LOC)
dashboard/lib/filters.py
def load_feature_importances(data_dir: str, run_id: str) -> list[dict[str, object]]:
    """Load feature importances for a run (XGBoost only).

    Args:
        data_dir: String path to the project data directory.
        run_id: The model run identifier.

    Returns:
        List of dicts ``{"feature": name, "importance": value}`` sorted
        descending by importance. Empty list for non-XGBoost models,
        legacy runs, or errors.
    """
    path = Path(data_dir)
    if not path.exists():
        return []
    try:
        store = RunStore(path)
        run = store.load_run(run_id)
        if run.model_type != "xgboost":
            return []
        model = store.load_model(run_id)
        if model is None:
            return []
        feature_names = store.load_feature_names(run_id) or []
        clf = getattr(model, "_clf", None)
        importances = getattr(clf, "feature_importances_", None)
        if importances is None or not feature_names or len(feature_names) != le
load_available_scorings function · python · L222-L228 (7 LOC)
dashboard/lib/filters.py
def load_available_scorings() -> list[str]:
    """Return registered scoring-rule names.

    Returns:
        Sorted list of scoring-format names (e.g. ``["fibonacci", "standard", …]``).
    """
    return list_scorings()
About: code-quality intelligence by Repobility · https://repobility.com
load_tourney_seeds function · python · L237-L266 (30 LOC)
dashboard/lib/filters.py
def load_tourney_seeds(data_dir: str, season: int) -> list[dict[str, object]]:
    """Load tournament seeds for a season from the Kaggle CSV.

    Args:
        data_dir: String path to the project data directory.
        season: Tournament season year.

    Returns:
        List of serialised seed dicts with keys: season, team_id, seed_str,
        region, seed_num, is_play_in.  Empty list if unavailable.
    """
    csv_path = Path(data_dir) / "kaggle" / "MNCAATourneySeeds.csv"
    if not csv_path.exists():
        return []
    try:
        table = TourneySeedTable.from_csv(csv_path)
        seeds = table.all_seeds(season)
        return [
            {
                "season": s.season,
                "team_id": s.team_id,
                "seed_str": s.seed_str,
                "region": s.region,
                "seed_num": s.seed_num,
                "is_play_in": s.is_play_in,
            }
            for s in seeds
        ]
    except (OSError, ValueError):
        return [
_load_team_names_uncached function · python · L269-L286 (18 LOC)
dashboard/lib/filters.py
def _load_team_names_uncached(data_dir: str) -> dict[int, str]:
    """Load team ID → team name mapping (uncached internal helper).

    Args:
        data_dir: String path to the project data directory.

    Returns:
        Mapping of team_id to team_name.  Empty dict if unavailable.
    """
    path = Path(data_dir)
    if not path.exists():
        return {}
    try:
        repo = ParquetRepository(path)
        teams = repo.get_teams()
        return {t.team_id: t.team_name for t in teams}
    except OSError:
        return {}
load_team_names function · python · L290-L299 (10 LOC)
dashboard/lib/filters.py
def load_team_names(data_dir: str) -> dict[int, str]:
    """Load team ID → team name mapping from the repository.

    Args:
        data_dir: String path to the project data directory.

    Returns:
        Mapping of team_id to team_name.  Empty dict if unavailable.
    """
    return _load_team_names_uncached(data_dir)
BracketSimulationResult class · python · L303-L318 (16 LOC)
dashboard/lib/filters.py
class BracketSimulationResult:
    """Container for bracket simulation outputs (cache-friendly).

    Attributes:
        sim_result: Full simulation result with advancement probs and EP.
        bracket: The bracket structure with team ordering and seed map.
        most_likely: Greedy most-likely bracket picks.
        prob_matrix: Pairwise win probability matrix.
        team_labels: Mapping of bracket index → display label.
    """

    sim_result: SimulationResult
    bracket: BracketStructure
    most_likely: MostLikelyBracket
    prob_matrix: npt.NDArray[np.float64]
    team_labels: dict[int, str]
_build_provider_from_folds function · python · L321-L353 (33 LOC)
dashboard/lib/filters.py
def _build_provider_from_folds(
    store: RunStore,
    run_id: str,
    season: int,
    bracket: BracketStructure,
) -> MatrixProvider | None:
    """Build a MatrixProvider from fold predictions for stateless models.

    Returns ``None`` if fold predictions are missing or empty for the season.
    """
    fold_df = store.load_fold_predictions(run_id)
    if fold_df is None or fold_df.empty:
        logger.warning("No fold predictions for run %s", run_id)
        return None

    tourney_preds = fold_df[fold_df["year"] == season]
    if tourney_preds.empty:
        logger.warning("No predictions for season %d in run %s", season, run_id)
        return None

    n = len(bracket.team_ids)
    P = np.full((n, n), 0.5, dtype=np.float64)

    # Vectorized matrix fill: map team IDs to bracket indices, then assign
    a_indices = tourney_preds["team_a_id"].map(bracket.team_index_map)
    b_indices = tourney_preds["team_b_id"].map(bracket.team_index_map)
    valid = a_indices.notna() & b_in
_build_team_labels function · python · L356-L367 (12 LOC)
dashboard/lib/filters.py
def _build_team_labels(
    data_dir: str,
    bracket: BracketStructure,
) -> dict[int, str]:
    """Build bracket_index → ``"[seed] TeamName"`` label mapping."""
    team_names = _load_team_names_uncached(data_dir)
    labels: dict[int, str] = {}
    for team_id, idx in bracket.team_index_map.items():
        seed_num = bracket.seed_map.get(team_id, 0)
        name = team_names.get(team_id, str(team_id))
        labels[idx] = f"[{seed_num}] {name}"
    return labels
run_bracket_simulation function · python · L371-L462 (92 LOC)
dashboard/lib/filters.py
def run_bracket_simulation(  # noqa: PLR0913
    data_dir: str,
    run_id: str,
    season: int,
    scoring_name: str,
    method: str = "analytical",
    n_simulations: int = 10_000,
) -> BracketSimulationResult | None:
    """Orchestrate bracket construction, model loading, and simulation.

    Builds a 64-team bracket from seeds, loads the trained model, computes
    pairwise probabilities, and runs the tournament simulation.

    Args:
        data_dir: String path to the project data directory.
        run_id: Model run identifier.
        season: Tournament season year.
        scoring_name: Scoring rule name (e.g. ``"standard"``).
        method: ``"analytical"`` or ``"monte_carlo"``.
        n_simulations: Number of MC simulations (ignored for analytical).

    Returns:
        :class:`BracketSimulationResult` or ``None`` on failure.
    """
    path = Path(data_dir)
    csv_path = path / "kaggle" / "MNCAATourneySeeds.csv"
    if not csv_path.exists():
        logger.warning(
score_chosen_bracket function · python · L473-L510 (38 LOC)
dashboard/lib/filters.py
def score_chosen_bracket(
    sim_data: BracketSimulationResult,
    _scoring_rules: Sequence[ScoringRule],
    scoring_key: str,
) -> dict[str, BracketDistribution]:
    """Score the most-likely bracket against MC simulations.

    Results are cached via ``@st.cache_data``.  ``_scoring_rules`` is prefixed
    with ``_`` so Streamlit skips hashing the list of rule objects (which are
    not hashable); ``scoring_key`` provides the cache discriminator instead,
    ensuring different rules produce different cache entries.

    Calls :func:`score_bracket_against_sims` with the most-likely bracket's
    ``winners`` array and the ``sim_result.sim_winners``, then computes
    distribution statistics for each scoring rule.

    Args:
        sim_data: Bracket simulation result (must have MC sim_winners).
        _scoring_rules: Scoring rules to evaluate (not hashed by Streamlit).
        scoring_key: Cache-discriminating string (e.g. rule name or custom
            points repr) that uniquely i
All rows scored by the Repobility analyzer (https://repobility.com)
build_custom_scoring function · python · L513-L527 (15 LOC)
dashboard/lib/filters.py
def build_custom_scoring(points_per_round: tuple[float, ...]) -> DictScoring:
    """Wrap a 6-element per-round point schedule in a :class:`DictScoring`.

    Args:
        points_per_round: Points for rounds 0–5 (R64 through Championship).
            Must contain exactly 6 elements.

    Returns:
        A ``DictScoring`` instance named ``"custom"``.

    Raises:
        ValueError: If ``points_per_round`` does not contain exactly 6 entries.
    """
    points_dict = dict(enumerate(points_per_round))
    return DictScoring(points=points_dict, scoring_name="custom")
export_bracket_csv function · python · L530-L586 (57 LOC)
dashboard/lib/filters.py
def export_bracket_csv(
    bracket: BracketStructure,
    most_likely: MostLikelyBracket,
    team_labels: dict[int, str],
    prob_matrix: npt.NDArray[np.float64],
) -> str:
    """Build a CSV string of the most-likely bracket picks for download.

    Returns one row per game (63 rows) in round-major order with columns:
    ``game_number``, ``round``, ``team_id``, ``team_name``, ``seed``,
    ``win_probability``.

    Args:
        bracket: Bracket structure with team ordering and seed map.
        most_likely: Greedy most-likely bracket picks.
        team_labels: Bracket index → display label mapping.
        prob_matrix: Pairwise win probability matrix.

    Returns:
        CSV string suitable for ``st.download_button(data=…)``.
    """
    buf = io.StringIO()
    buf.write("game_number,round,team_id,team_name,seed,win_probability\n")

    n_games = len(most_likely.winners)
    n_rounds = int(np.log2(n_games + 1))
    game_offset = 0
    games_in_round = n_games + 1  # 64 teams →
_game_win_probability function · python · L589-L620 (32 LOC)
dashboard/lib/filters.py
def _game_win_probability(  # noqa: PLR0913
    round_idx: int,
    game_in_round: int,
    game_offset: int,
    winners: tuple[int, ...],
    prob_matrix: npt.NDArray[np.float64],
    bracket: BracketStructure,
) -> float:
    """Compute the win probability for a specific game in the bracket.

    For Round of 64, this is the direct pairwise probability.
    For later rounds, it uses the pairwise probability between the two
    teams that advanced from the previous round.
    """
    if round_idx == 0:
        # R64: teams are seeded in bracket order, game g pits team 2g vs 2g+1
        team_a_idx = game_in_round * 2
        team_b_idx = game_in_round * 2 + 1
    else:
        # Later rounds: the two participants are the winners of the two
        # feeder games from the previous round (games 2g and 2g+1 in round r-1)
        prev_games_in_round = (len(winners) + 1) // (2**round_idx)
        prev_offset = game_offset - prev_games_in_round
        feeder_a = prev_offset + game_in_roun
_render_leaderboard function · python · L18-L129 (112 LOC)
dashboard/pages/1_Lab.py
def _render_leaderboard() -> None:
    """Render the backtest leaderboard page."""
    st.header("Backtest Leaderboard")

    data_dir = str(get_data_dir())
    raw = load_leaderboard_data(data_dir)

    if not raw:
        runs = load_available_runs(data_dir)
        if runs:
            st.warning("No backtest metrics available. Re-run training to generate metrics.")
        else:
            st.info(
                "No model runs available. Train a model first: `python -m ncaa_eval.cli train --model elo`"
            )
        return

    df = pd.DataFrame(raw)
    if df.empty:
        st.info("No model runs available. Train a model first: `python -m ncaa_eval.cli train --model elo`")
        return

    # -- Apply year filter -----------------------------------------------------
    selected_year = st.session_state.setdefault("selected_year", None)

    if selected_year is not None:
        year_df = df[df["year"] == selected_year]
        if year_df.empty:
            st.info(f"N
_render_results function · python · L27-L104 (78 LOC)
dashboard/pages/2_Presentation.py
def _render_results(sim_data: BracketSimulationResult, scoring: str) -> None:
    """Render all bracket visualisation sections from simulation results."""
    result = sim_data.sim_result
    bracket = sim_data.bracket
    most_likely = sim_data.most_likely

    # Champion summary
    champ_label = sim_data.team_labels.get(
        bracket.team_index_map.get(most_likely.champion_team_id, -1), "Unknown"
    )
    st.success(f"Predicted Champion: **{champ_label}** (log-likelihood: {most_likely.log_likelihood:.2f})")

    # Bracket tree
    st.subheader("Most-Likely Bracket")
    bracket_html = render_bracket_html(
        bracket_team_ids=bracket.team_ids,
        most_likely_winners=most_likely.winners,
        team_labels=sim_data.team_labels,
        seed_map=bracket.seed_map,
        prob_matrix=sim_data.prob_matrix,
    )
    components.html(bracket_html, height=700, scrolling=True)

    # Advancement heatmap
    st.subheader("Advancement Probabilities")
    fig_heatmap = plot_advan
_render_bracket_page function · python · L107-L182 (76 LOC)
dashboard/pages/2_Presentation.py
def _render_bracket_page() -> None:
    """Render the Bracket Visualizer page."""
    # Breadcrumbs
    col_nav, col_bc = st.columns([1, 3])
    with col_nav:
        st.page_link("pages/home.py", label="← Home")
    with col_bc:
        st.caption("Home > Presentation > Bracket Visualizer")

    st.header("Bracket Visualizer")

    # Validate required session state
    selected_year: int | None = st.session_state.get("selected_year")
    selected_run_id: str | None = st.session_state.get("selected_run_id")
    selected_scoring: str | None = st.session_state.get("selected_scoring")

    if selected_run_id is None:
        st.info("Select a model run from the sidebar to visualize bracket predictions.")
        return
    if selected_year is None:
        st.info("Select a tournament year from the sidebar.")
        return

    scoring = selected_scoring or "standard"
    data_dir = str(get_data_dir())

    # Check seeds available
    seeds_raw = load_tourney_seeds(data_dir, selected_yea
_render_reliability_section function · python · L30-L61 (32 LOC)
dashboard/pages/3_Model_Deep_Dive.py
def _render_reliability_section(data_dir: str, run_id: str, label: str) -> None:
    """Render metric explorer with year drill-down and reliability diagram."""
    st.subheader("Metric Explorer")
    st.caption("Drill-down by year. Round, seed matchup, and conference filters are post-MVP.")

    fold_preds_raw = load_fold_predictions(data_dir, run_id)
    if not fold_preds_raw:
        st.warning("No fold predictions available. Re-run training to generate diagnostic data.")
        return

    fold_df = pd.DataFrame(fold_preds_raw)
    available_years = sorted(fold_df["year"].unique().tolist())
    year_options: list[str] = ["All Years (Aggregate)"] + [str(y) for y in available_years]
    selected_year_str = st.selectbox("Fold Year", options=year_options, key="deep_dive_year")

    if selected_year_str == "All Years (Aggregate)":
        filtered = fold_df
        title_suffix = "All Years"
    else:
        filtered = fold_df[fold_df["year"] == int(selected_year_str)]
        title_su
_render_metric_summary function · python · L64-L85 (22 LOC)
dashboard/pages/3_Model_Deep_Dive.py
def _render_metric_summary(data_dir: str, run_id: str) -> None:
    """Render per-year metric summary table."""
    st.subheader("Per-Year Metric Summary")
    lb_raw = load_leaderboard_data(data_dir)
    if not lb_raw:
        st.info("No leaderboard data available.")
        return

    lb_df = pd.DataFrame(lb_raw)
    run_metrics = lb_df[lb_df["run_id"] == run_id].copy()
    if run_metrics.empty:
        st.info("No metric summary available for this run.")
        return

    display_cols = ["year"] + _METRIC_COLS
    display_df = run_metrics[display_cols].sort_values("year").reset_index(drop=True)
    styled = (
        display_df.style.background_gradient(cmap="RdYlGn_r", subset=["log_loss", "brier_score", "ece"])
        .background_gradient(cmap="RdYlGn", subset=["roc_auc"])
        .format({"log_loss": "{:.4f}", "brier_score": "{:.4f}", "roc_auc": "{:.4f}", "ece": "{:.4f}"})
    )
    st.dataframe(styled, use_container_width=True)
Source: Repobility analyzer · https://repobility.com
_render_feature_importance function · python · L88-L110 (23 LOC)
dashboard/pages/3_Model_Deep_Dive.py
def _render_feature_importance(data_dir: str, run_id: str, model_type: str) -> None:
    """Render feature importance bar chart (XGBoost only)."""
    st.subheader("Feature Importance")
    importances = load_feature_importances(data_dir, run_id)
    if not importances:
        if model_type == "xgboost":
            st.info("Feature importance not available. Re-run training to persist model artifacts.")
        else:
            st.info("Feature importance is not available for stateful models.")
        return

    feature_names = [d["feature"] for d in importances]
    importance_values = [d["importance"] for d in importances]
    fig = go.Figure(go.Bar(x=importance_values, y=feature_names, orientation="h", marker_color=COLOR_GREEN))
    fig.update_layout(
        template=TEMPLATE,
        title="Feature Importance (Gain)",
        xaxis_title="Importance",
        yaxis_title="Feature",
        yaxis=dict(autorange="reversed"),
        height=min(max(400, len(feature_names) * 25), 
_render_deep_dive function · python · L113-L146 (34 LOC)
dashboard/pages/3_Model_Deep_Dive.py
def _render_deep_dive() -> None:
    """Render the Model Deep Dive page."""
    run_id: str | None = st.session_state.get("selected_run_id")

    if run_id is None:
        st.info("Select a model run from the Leaderboard to view diagnostics.")
        st.page_link("pages/1_Lab.py", label="Go to Leaderboard")
        return

    data_dir = str(get_data_dir())
    runs = load_available_runs(data_dir)
    run = next((r for r in runs if r["run_id"] == run_id), None)
    if run is None:
        st.warning(f"Run {run_id} not found.")
        return

    model_type = str(run["model_type"])
    label = f"{model_type}-{run_id[:8]}"

    # Breadcrumbs and navigation
    col_nav, col_bc = st.columns([1, 3])
    with col_nav:
        st.page_link("pages/1_Lab.py", label="← Back to Leaderboard")
    with col_bc:
        st.caption(f"Home > Lab > {label}")

    st.header(f"Model Deep Dive: {label}")

    _render_reliability_section(data_dir, run_id, label)
    _render_metric_summary(data_dir, run_i
_render_outcome_summary function · python · L31-L59 (29 LOC)
dashboard/pages/4_Pool_Scorer.py
def _render_outcome_summary(dist: BracketDistribution) -> None:
    """Render point outcome summary metrics as ``st.metric`` cards."""
    min_score = float(dist.scores.min()) if len(dist.scores) > 0 else 0.0
    max_score = float(dist.scores.max()) if len(dist.scores) > 0 else 0.0

    st.subheader("Outcome Summary")
    row1 = st.columns(3)
    with row1[0]:
        st.metric("Median", f"{dist.percentiles.get(50, 0.0):.1f} pts")
    with row1[1]:
        st.metric("Mean", f"{dist.mean:.1f} pts")
    with row1[2]:
        st.metric("Std Dev", f"{dist.std:.1f} pts")

    row2 = st.columns(2)
    with row2[0]:
        st.metric("Min", f"{min_score:.1f} pts")
    with row2[1]:
        st.metric("Max", f"{max_score:.1f} pts")

    row3 = st.columns(4)
    with row3[0]:
        st.metric("5th %ile", f"{dist.percentiles.get(5, 0.0):.1f} pts")
    with row3[1]:
        st.metric("25th %ile", f"{dist.percentiles.get(25, 0.0):.1f} pts")
    with row3[2]:
        st.metric("75th %ile", f"{dist.
_render_distribution_chart function · python · L62-L65 (4 LOC)
dashboard/pages/4_Pool_Scorer.py
def _render_distribution_chart(dist: BracketDistribution, scoring_label: str) -> None:
    """Render score distribution histogram via ``plot_score_distribution``."""
    fig = plot_score_distribution(dist, title=f"Bracket Score Distribution — {scoring_label}")
    st.plotly_chart(fig, use_container_width=True)
_render_results function · python · L68-L120 (53 LOC)
dashboard/pages/4_Pool_Scorer.py
def _render_results(
    sim_data: BracketSimulationResult,
    scoring_label: str,
    use_custom: bool,
    custom_points: tuple[float, ...],
) -> None:
    """Run scoring, display outcome analysis, and offer CSV export."""
    # Determine which scoring rule to use
    if use_custom:
        scoring_rule = build_custom_scoring(custom_points)
        # Cache key encodes custom points so different schedules get different entries
        scoring_key = f"custom:{custom_points}"
    else:
        scoring_cls = get_scoring(scoring_label)
        sig = inspect.signature(scoring_cls)
        if "seed_map" in sig.parameters:
            scoring_rule = scoring_cls(sim_data.bracket.seed_map)
        else:
            scoring_rule = scoring_cls()
        scoring_key = scoring_label

    rule_name: str = scoring_rule.name

    # Score bracket against simulations (cached by sim_data + scoring_key)
    with st.spinner("Scoring bracket against simulations..."):
        distributions = score_chosen_b
_render_scoring_config function · python · L128-L151 (24 LOC)
dashboard/pages/4_Pool_Scorer.py
def _render_scoring_config() -> tuple[bool, tuple[float, ...]]:
    """Render scoring configuration UI and return (use_custom, custom_points)."""
    st.subheader("Scoring Configuration")
    use_custom = st.checkbox("Use custom scoring", value=False, key="pool_use_custom")

    custom_points: tuple[float, ...] = (1.0, 2.0, 4.0, 8.0, 16.0, 32.0)
    if use_custom:
        cols = st.columns(6)
        round_labels = ["R64", "R32", "S16", "E8", "F4", "NCG"]
        custom_values: list[float] = []
        defaults = [1, 2, 4, 8, 16, 32]
        for i, (col, label) in enumerate(zip(cols, round_labels)):
            with col:
                val = st.number_input(
                    label,
                    min_value=0,
                    value=defaults[i],
                    step=1,
                    key=f"pool_pts_{i}",
                )
                custom_values.append(float(val))
        custom_points = tuple(custom_values)

    return use_custom, custom_points
_run_simulation function · python · L154-L184 (31 LOC)
dashboard/pages/4_Pool_Scorer.py
def _run_simulation(
    data_dir: str,
    run_id: str,
    season: int,
    scoring: str,
    n_sims: int,
) -> None:
    """Run MC simulation and store result in session state."""
    with st.spinner("Running Monte Carlo simulation..."):
        sim_data = run_bracket_simulation(
            data_dir=data_dir,
            run_id=run_id,
            season=season,
            scoring_name=scoring,
            method="monte_carlo",
            n_simulations=n_sims,
        )

    if sim_data is None:
        st.warning(
            "Could not simulate bracket. Ensure the selected model has been "
            f"trained and has data for {season}."
        )
        return

    if sim_data.sim_result.sim_winners is None:
        st.error("Monte Carlo simulation did not produce sim_winners. Cannot score bracket.")
        return

    st.session_state["pool_sim_data"] = sim_data
    st.session_state["pool_sim_key"] = (run_id, season, n_sims)
_render_pool_scorer_page function · python · L187-L245 (59 LOC)
dashboard/pages/4_Pool_Scorer.py
def _render_pool_scorer_page() -> None:
    """Render the Pool Scorer page."""
    # Breadcrumbs (AC matching existing pages)
    col_nav, col_bc = st.columns([1, 3])
    with col_nav:
        st.page_link("pages/home.py", label="← Home")
    with col_bc:
        st.caption("Home > Presentation > Pool Scorer")

    st.header("Pool Scorer & Point Outcome Analysis")

    # Validate required session state (empty states — AC #2.8)
    selected_year: int | None = st.session_state.get("selected_year")
    selected_run_id: str | None = st.session_state.get("selected_run_id")
    selected_scoring: str | None = st.session_state.get("selected_scoring")

    if selected_run_id is None:
        st.info("Select a model run from the sidebar to analyze pool scoring.")
        return
    if selected_year is None:
        st.info("Select a tournament year from the sidebar.")
        return

    scoring = selected_scoring or "standard"
    data_dir = str(get_data_dir())

    # Check seeds available
    
Want this analysis on your repo? https://repobility.com/scan/
lint function · python · L16-L19 (4 LOC)
noxfile.py
def lint(session: nox.Session) -> None:
    """Run Ruff linting with auto-fix and format checking."""
    session.run("ruff", "check", ".", "--fix")
    session.run("ruff", "format", "--check", ".")
typecheck function · python · L23-L34 (12 LOC)
noxfile.py
def typecheck(session: nox.Session) -> None:
    """Run mypy strict type checking on source and test files."""
    session.run(
        "mypy",
        "--strict",
        "--show-error-codes",
        "--namespace-packages",
        "src/ncaa_eval",
        "tests",
        "noxfile.py",
        "sync.py",
    )
tests function · python · L38-L40 (3 LOC)
noxfile.py
def tests(session: nox.Session) -> None:
    """Run the full pytest test suite."""
    session.run("pytest", "--tb=short")
docs function · python · L44-L47 (4 LOC)
noxfile.py
def docs(session: nox.Session) -> None:
    """Generate Sphinx HTML documentation from source docstrings."""
    session.run("sphinx-apidoc", "-f", "-e", "-o", "docs/api", "src/ncaa_eval")
    session.run("sphinx-build", "-b", "html", "docs", "docs/_build/html")
_instantiate_model function · python · L24-L40 (17 LOC)
src/ncaa_eval/cli/main.py
def _instantiate_model(model_cls: type[Model], config_path: Path | None) -> Model:
    """Instantiate a model, optionally overriding its config from JSON.

    When *config_path* is given, creates a default instance to discover
    the config class, then validates the JSON overrides through that
    Pydantic model and reinstantiates.
    """
    if config_path is not None:
        if not config_path.exists():
            console.print(f"[red]Error: Config file not found: {config_path}[/red]")
            raise typer.Exit(code=1)
        override = json.loads(config_path.read_text())
        default = model_cls()
        config_cls = type(default.get_config())
        config_obj = config_cls(**override)
        return model_cls(config_obj)  # type: ignore[call-arg]
    return model_cls()
train function · python · L44-L76 (33 LOC)
src/ncaa_eval/cli/main.py
def train(  # noqa: PLR0913
    model: str = typer.Option(..., "--model", help="Registered model plugin name"),
    start_year: int = typer.Option(2015, "--start-year", help="First season year (inclusive)"),
    end_year: int = typer.Option(2025, "--end-year", help="Last season year (inclusive)"),
    data_dir: Path = typer.Option(Path("data/"), "--data-dir", help="Local Parquet data directory"),
    output_dir: Path = typer.Option(Path("data/"), "--output-dir", help="Output directory for run artifacts"),
    config: Path | None = typer.Option(None, "--config", help="Path to JSON config override"),
) -> None:
    """Train a model on NCAA basketball data and persist run artifacts."""
    if start_year > end_year:
        console.print(f"[red]Error: --start-year ({start_year}) must be ≤ --end-year ({end_year})[/red]")
        raise typer.Exit(code=1)

    try:
        model_cls = get_model(model)
    except ModelNotFoundError:
        available = list_models()
        console.print(f"[re
_get_git_hash function · python · L28-L39 (12 LOC)
src/ncaa_eval/cli/train.py
def _get_git_hash() -> str:
    """Return the short git hash of HEAD, or ``"unknown"`` on failure."""
    try:
        result = subprocess.run(
            ["git", "rev-parse", "--short", "HEAD"],
            capture_output=True,
            text=True,
            check=True,
        )
        return result.stdout.strip()
    except (subprocess.CalledProcessError, FileNotFoundError):
        return "unknown"
_build_fold_predictions function · python · L42-L70 (29 LOC)
src/ncaa_eval/cli/train.py
def _build_fold_predictions(result: BacktestResult) -> pd.DataFrame | None:
    """Build a fold predictions DataFrame from backtest results.

    Args:
        result: Backtest result containing fold results with game metadata.

    Returns:
        DataFrame with columns [year, game_id, team_a_id, team_b_id,
        pred_win_prob, team_a_won], or None if no fold predictions exist.
    """
    fold_frames: list[pd.DataFrame] = []
    for fr in result.fold_results:
        if fr.predictions.empty:
            continue
        fold_frames.append(
            pd.DataFrame(
                {
                    "year": fr.year,
                    "game_id": fr.test_game_ids.values,
                    "team_a_id": fr.test_team_a_ids.values,
                    "team_b_id": fr.test_team_b_ids.values,
                    "pred_win_prob": fr.predictions.values,
                    "team_a_won": fr.actuals.values,
                }
            )
        )
    if not fold_frames:
        retur
About: code-quality intelligence by Repobility · https://repobility.com
run_training function · python · L73-L246 (174 LOC)
src/ncaa_eval/cli/train.py
def run_training(  # noqa: PLR0913, C901, PLR0912
    model: Model,
    *,
    start_year: int,
    end_year: int,
    data_dir: Path,
    output_dir: Path,
    model_name: str,
    console: Console | None = None,
) -> ModelRun:
    """Execute the full train → predict → persist pipeline.

    Args:
        model: An instantiated model (stateful or stateless).
        start_year: First season year (inclusive) for training.
        end_year: Last season year (inclusive) for training.
        data_dir: Path to the local Parquet data store.
        output_dir: Path where run artifacts are persisted.
        model_name: Registered plugin name (used in the ModelRun record).
        console: Rich Console instance for terminal output. Defaults to a
            fresh ``Console()`` so callers (e.g. tests) can suppress output
            by passing ``Console(quiet=True)``.

    Returns:
        The persisted run metadata record.
    """
    _console = console or Console()

    repo = ParquetRepos
feature_cols function · python · L69-L78 (10 LOC)
src/ncaa_eval/evaluation/backtest.py
def feature_cols(df: pd.DataFrame) -> list[str]:
    """Return feature column names (everything not in METADATA_COLS).

    Args:
        df: DataFrame whose columns are inspected.

    Returns:
        List of column names that are not metadata.
    """
    return [c for c in df.columns if c not in METADATA_COLS]
FoldResult class · python · L82-L109 (28 LOC)
src/ncaa_eval/evaluation/backtest.py
class FoldResult:
    """Result of evaluating a single cross-validation fold.

    Attributes:
        year: The test season year for this fold.
        predictions: Predicted probabilities for tournament games.
        actuals: Actual binary outcomes for tournament games.
        metrics: Mapping of metric name to computed value.
        elapsed_seconds: Wall-clock time for the fold evaluation.
        test_game_ids: Game IDs from the test fold (aligned to predictions).
        test_team_a_ids: team_a IDs from the test fold.
        test_team_b_ids: team_b IDs from the test fold.
    """

    year: int
    predictions: pd.Series
    actuals: pd.Series
    metrics: Mapping[str, float]
    elapsed_seconds: float
    test_game_ids: pd.Series = dataclasses.field(
        default_factory=lambda: pd.Series(dtype=object),
    )
    test_team_a_ids: pd.Series = dataclasses.field(
        default_factory=lambda: pd.Series(dtype="int64"),
    )
    test_team_b_ids: pd.Series = dataclasses.field
page 1 / 8next ›