Source code for keeks.bet_log

"""Load a recorded bet log from CSV."""

import csv
import math
import os
from pathlib import Path

_OUTCOMES = {"0": 0, "1": 1, "true": 1, "false": 0}


[docs] def load_bet_log( path: str | os.PathLike[str], *, probability_column: str = "probability", outcome_column: str = "outcome", ) -> tuple[list[float], list[int]]: """Read chronological probabilities and 0/1 outcomes from a CSV file. Row order is preserved. Probabilities must be finite and in ``[0, 1]``; outcomes may be ``0``, ``1``, ``true`` or ``false`` (case-insensitive). Any problem raises ``ValueError`` naming the file column and the 1-based data row (the header is not counted). Parameters ---------- path : str or os.PathLike CSV file with a header row. probability_column, outcome_column : str Header names of the two columns to read. Returns ------- tuple[list[float], list[int]] ``(probabilities, outcomes)``, ready for ``calibration_report``, ``HistoricalBinarySimulator`` or ``compare_strategies``. Examples -------- >>> import tempfile >>> from pathlib import Path >>> with tempfile.TemporaryDirectory() as folder: ... log = Path(folder) / "log.csv" ... _ = log.write_text("probability,outcome\\n0.6,1\\n0.4,false\\n") ... load_bet_log(log) ([0.6, 0.4], [1, 0]) """ probabilities = [] outcomes = [] with Path(path).open(newline="", encoding="utf-8") as source: reader = csv.DictReader(source) for column in (probability_column, outcome_column): if column not in (reader.fieldnames or []): raise ValueError( f"{path}: column {column!r} not found; " f"available columns: {list(reader.fieldnames or [])}" ) for row_number, row in enumerate(reader, start=1): probabilities.append( _parse_probability( row[probability_column], probability_column, row_number ) ) outcomes.append( _parse_outcome(row[outcome_column], outcome_column, row_number) ) return probabilities, outcomes
def _parse_probability(raw: str | None, column: str, row_number: int) -> float: text = (raw or "").strip() where = f"column {column!r}, row {row_number}" if not text: raise ValueError(f"{where}: probability is empty") try: value = float(text) except ValueError: raise ValueError(f"{where}: probability {text!r} is not a number") from None if not math.isfinite(value) or not 0.0 <= value <= 1.0: raise ValueError(f"{where}: probability {text!r} must be finite in [0, 1]") return value def _parse_outcome(raw: str | None, column: str, row_number: int) -> int: text = (raw or "").strip() try: return _OUTCOMES[text.lower()] except KeyError: raise ValueError( f"column {column!r}, row {row_number}: outcome {text!r} must be " "0, 1, true or false" ) from None