TeX 62.3%
Python 37.6%
1# Author: Simon-Pierre Boucher — simon-pierre.boucher@uqo.ca2import sys3from pathlib import Path45sys.path.insert(0, str(Path(__file__).resolve().parents[1] / "src"))6import numpy as np7import polars as pl8import pytest910from wp23 import evaluation as E11from wp23.data import month_add, months_between121314def test_month_arith():15 assert month_add("2024-11", 3) == "2025-02" and month_add("2024-02", -3) == "2023-11" and months_between("2021-01", "2022-01") == 12161718def test_dm_and_gw_detect_difference():19 rng = np.random.default_rng(0); y = rng.normal(size=300); la = (rng.normal(0, 1, 300)) ** 2; lb = (rng.normal(0, 1.5, 300)) ** 220 assert E.diebold_mariano(la, lb, h=3)["p"] < 0.01 and E.giacomini_white(la, lb, h=3)["p"] < 0.0521 same = E.diebold_mariano(la, la + rng.normal(0, 1e-3, 300), h=1); assert same["p"] > 0.05222324def test_mcs_keeps_best():25 rng = np.random.default_rng(1); T = 120; base = rng.normal(0, 1, T) ** 226 L = np.column_stack([base, base + 0.5 + rng.normal(0, 0.1, T), base + 0.01 * (rng.normal(size=T) - 0.0)])27 L[:, 2] = base + rng.normal(0, 0.1, T) * 0.0 + (rng.random(T) - 0.5) * 1e-328 r = E.model_confidence_set(L, ["a", "b", "c"], B=300); assert "b" not in r["in_mcs"] and "a" in r["in_mcs"]293031def test_scores_and_crps():32 rng = np.random.default_rng(2); y = rng.normal(size=500); Q = np.column_stack([y + q for q in (-1.6, -0.7, 0, 0.7, 1.6)])33 taus = np.array([0.05, 0.25, 0.5, 0.75, 0.95]); c = E.crps_from_quantiles(y, Q, taus); assert np.all(c >= 0)34 s = E.interval_scores(y, Q[:, 0], Q[:, 4], 0.1); assert s["coverage"] == 1.035 p = E.point_scores(y, y + 0.1, y + 1.0); assert abs(p["bias"] - 0.1) < 1e-9 and p["mase"] < 1363738@pytest.mark.skipif(not (Path(__file__).resolve().parents[2] / "sharp-hpi-quebec" / "data" / "processed" / "analytic_transactions.parquet").exists(), reason="WP22 data absent")39def test_task_a_features_use_only_past():40 from wp23 import data as D41 s = D.load_sales().filter(pl.col("region_code") == "07")42 tr, te = D.task_a_sets(s, "2023-06")43 assert tr["month"].max() <= "2023-06" and te["month"].min() > "2023-06"44 # market features of test rows are those of the origin: identical for all test sales of the same municipality45 g = te.group_by("mun_code").agg(pl.col("mun_q_3").n_unique().alias("u")); assert g["u"].max() == 146 # kNN features for test rows come from sales ≤ origin: recompute for one row and compare47 kn = D.knn_features_at(s, "2023-06", te.head(50)); j = te.head(50).join(kn, on="tx_id", suffix="_chk"); assert np.allclose(j["knn_q"], j["knn_q_chk"], equal_nan=True)484950def test_models_b_shapes():51 from wp23.models_b import NoChange, Momentum, AR2, RidgePanel, BHPF52 rng = np.random.default_rng(3); n = 30053 hist = [list(np.cumsum(rng.normal(0, 0.01, 13))) for _ in range(n)]54 df = pl.DataFrame({"class": ["house"] * n, "level": ["municipality"] * n, "geo_id": [f"M{i%20:03d}" for i in range(n)], "origin": [f"2023-{(i%12)+1:02d}" for i in range(n)], "h": [3] * n, "hist": hist,55 "target": rng.normal(0, 0.03, n), "region_code": [f"{i%4:02d}" for i in range(n)], "cx": rng.normal(size=n), "cy": rng.normal(size=n)})56 H = np.array(hist); feats = {"d1": H[:, -1] - H[:, -2], "d3": H[:, -1] - H[:, -4], "d6": H[:, -1] - H[:, -7], "d12": H[:, -1] - H[:, 0], "vol6": np.std(np.diff(H[:, -7:], axis=1), axis=1), "vol12": np.std(np.diff(H, axis=1), axis=1),57 "n_last": rng.poisson(5, n) + 0.0, "n_mean12": rng.poisson(5, n) + 0.0, "n_ratio": rng.uniform(0.5, 1.5, n), "se_last": rng.uniform(0.01, 0.05, n), "accel": rng.normal(0, 0.01, n), "month_of_year": rng.integers(1, 13, n) + 0.0, "origin_t": rng.integers(20, 40, n) + 0.0,58 "reg_d3": rng.normal(0, 0.02, n), "reg_d12": rng.normal(0, 0.05, n), "reg_vol12": rng.uniform(0.01, 0.03, n), "prov_d3": rng.normal(0, 0.02, n), "prov_d12": rng.normal(0, 0.05, n), "prov_vol12": rng.uniform(0.01, 0.03, n)}59 df = df.with_columns([pl.Series(k, v) for k, v in feats.items()])60 tr, te = df.head(240), df.tail(60)61 for m in [NoChange(), Momentum(), AR2(), RidgePanel(), BHPF()]:62 m.fit(tr); p = m.predict(te); Q = m.predict_quantiles(te); assert p.shape == (60,) and Q.shape == (60, 5) and np.all(np.diff(Q, axis=1) >= -1e-9)63