SPB Git

spb/wp26_uqo

Public
2.8 KB · 70 lines python
1# Author: Simon-Pierre Boucher — simon-pierre.boucher@uqo.ca2"""Baselines on a small synthetic feature matrix built here (independent of the feature pipeline)."""3from __future__ import annotations45import numpy as np6import polars as pl7import pytest89from agentic_valuation.baselines.gbm import AVM, LightGBMModel10from agentic_valuation.baselines.hedonic import HedonicRidge, HierarchicalHedonic11from agentic_valuation.data.synthetic import generate121314@pytest.fixture(scope="module")15def fm():16    df = generate(4000, seed=1)17    df = df.with_columns(18        pl.col("price").log().alias("log_price"),19        pl.col("floor_area_m2").log().alias("log_floor_area"),20        (2026 - pl.col("year_built")).alias("age"),21        pl.col("roll_total_value").log().alias("log_roll_total"),22        (pl.col("lat") * 100).floor().cast(pl.Utf8).alias("grid1k"),23        (pl.col("lat") * 20).floor().cast(pl.Utf8).alias("grid5k"),24        pl.col("region_name").alias("region_group"),25        pl.when(pl.col("date") < pl.date(2024, 7, 1)).then(pl.lit("train")).when(pl.col("date") < pl.date(2025, 4, 1)).then(pl.lit("validation")).otherwise(pl.lit("test")).alias("split"),26    ).drop(["exclude_reason", "borough_name", "roll_reference_date", "parcel_id"])27    return df282930def _split(fm):31    return (fm.filter(pl.col("split") == s) for s in ("train", "validation", "test"))323334def test_hedonic_beats_naive(fm):35    tr, va, te = _split(fm)36    m = HedonicRidge().fit(tr, va)37    p = m.predict(te)38    ae = np.abs(np.log(p.point) - te["log_price"].to_numpy()).mean()39    naive = np.abs(te["log_price"].to_numpy() - tr["log_price"].mean()).mean()40    assert ae < 0.75 * naive41    assert p.quantiles.shape == (te.height, 5)42    assert np.all(np.diff(p.quantiles, axis=1) >= 0)434445def test_hierarchical_runs(fm):46    tr, va, te = _split(fm)47    m = HierarchicalHedonic().fit(tr, va)48    p = m.predict(te)49    assert np.isfinite(p.point).all()505152def test_lightgbm_and_avm(fm):53    tr, va, te = _split(fm)54    m = LightGBMModel(n_trials=2, max_rounds=300).fit(tr, va)55    p = m.predict(te)56    ae = np.abs(np.log(p.point) - te["log_price"].to_numpy()).mean()57    assert ae < 0.3058    avm = AVM(n_trials=1)59    avm.point.max_rounds = 30060    avm.fit(tr, va)61    # conformal guarantee holds on the calibration partition; under the synthetic trend drift the test-period62    # coverage is lower (documented behaviour, measured in the paper's drift section)63    q = avm.predict(va)64    y = va["log_price"].to_numpy()65    cov = np.mean((np.log(q.quantiles[:, 0]) <= y) & (y <= np.log(q.quantiles[:, 4])))66    assert 0.85 <= cov <= 0.9567    q = avm.predict(te)68    rec = q.to_records("test", "D_avm", "t")69    assert set(rec.columns) == {"tx_id", "split", "arm", "committee", "aggregation", "point", "q05", "q25", "q50", "q75", "q95", "cost_usd", "latency_s", "tokens", "run_id"}70
Files · main
Menu

Theme