SPB Git
14.2 KB · 171 lines python
1"""Chapitre 18 — Modèles hédoniques : vérification des chiffres cités dans le texte, les tableaux et les solutions.2Modèle de référence : traite.cas1.hedonique (non redéfini). Tolérances déclarées dans chaque assertion."""3import sys, pathlib, math4import numpy as np, pytest5sys.path.insert(0, str(pathlib.Path(__file__).resolve().parents[1] / "cases"))6import ch18_hedonique as c187from traite import hedonique as h89@pytest.fixture(scope="module")10def res():11    return c18.main(verbose=False)1213def test_modele_reference_retrouve_les_lois(res):14    m = res["modele"]; coef = res["coef"]15    assert m["n"] == 600 and m["k"] == 1616    assert m["r2"] == pytest.approx(0.853, abs=0.001) and m["r2_ajuste"] == pytest.approx(0.849, abs=0.001)17    assert m["sigma"] == pytest.approx(0.083, abs=0.0005)18    # 15 paramètres de génération couverts par l'IC à 95 %19    assert int(coef["couvre"].sum()) == 15 and int(coef["vrai"].notna().sum()) == 1520    assert coef.loc["ln_S", "coef"] == pytest.approx(0.5391, abs=0.0005) and coef.loc["ln_S", "se_hc1"] == pytest.approx(0.0127, abs=0.0005)21    assert coef.loc["ln_T", "coef"] == pytest.approx(0.0937, abs=0.0005)22    assert coef.loc["qualite", "coef"] == pytest.approx(0.0611, abs=0.0005)23    assert coef.loc["garage", "coef"] == pytest.approx(0.0324, abs=0.0005)24    assert coef.loc["sect_Buckingham", "coef"] == pytest.approx(-0.2504, abs=0.0005) and coef.loc["sect_Buckingham", "se_hc1"] == pytest.approx(0.0718, abs=0.0005)25    assert coef.loc["const", "coef"] == pytest.approx(13.2074, abs=0.0005)26    assert math.exp(coef.loc["const", "coef"]) == pytest.approx(544_400, rel=0.001)27    assert m["croissance_mensuelle"] * 100 == pytest.approx(0.364, abs=0.002)28    assert ((1 + m["croissance_mensuelle"]) ** 12 - 1) * 100 == pytest.approx(4.46, abs=0.02)29    assert c18._g * 100 == pytest.approx(4.43, abs=0.01)3031def test_diagnostics(res):32    d = res["diag"]33    assert d["breusch_pagan"]["LM"] == pytest.approx(16.09, abs=0.05) and d["breusch_pagan"]["ddl"] == 15 and d["breusch_pagan"]["p"] == pytest.approx(0.38, abs=0.01)34    assert d["jarque_bera"]["stat"] == pytest.approx(2.11, abs=0.05) and d["jarque_bera"]["p"] == pytest.approx(0.35, abs=0.01)35    assert d["skew"] == pytest.approx(-0.07, abs=0.005) and d["kurtosis"] == pytest.approx(2.75, abs=0.01)36    assert d["res_min"] == pytest.approx(-0.238, abs=0.001) and d["res_max"] == pytest.approx(0.224, abs=0.001)37    assert d["pct_abs_sup_15"] == pytest.approx(7.0, abs=0.05)38    v = d["vif"]39    assert v["age"] == pytest.approx(11.85, abs=0.05) and v["dist_centre"] == pytest.approx(43.7, abs=0.1)40    assert v["sect_Buckingham"] == pytest.approx(30.3, abs=0.1) and v["sect_Masson-Angers"] == pytest.approx(28.0, abs=0.1)41    assert v["sect_Aylmer"] == pytest.approx(8.0, abs=0.05) and v["sect_Gatineau"] == pytest.approx(4.15, abs=0.05)42    assert (v[["ln_S", "ln_T", "qualite", "garage", "sous_sol", "piscine", "etages", "mois"]] < 2).all()43    ck = d["cook"]44    assert ck["seuil"] == pytest.approx(4 / 600) and ck["n_sup"] == 29 and ck["D"].max() == pytest.approx(0.022, abs=0.0005)45    assert res["ventes"]["id_vente"].iloc[int(np.argmax(ck["D"]))] == "G0541"46    mo = d["moran"]47    assert mo["I"] == pytest.approx(-0.013, abs=0.0005) and mo["z"] == pytest.approx(-0.59, abs=0.01) and mo["p"] == pytest.approx(0.56, abs=0.01)4849def test_variantes(res):50    v = res["variantes"]51    attendu = {"semi-log (référence)": (0.853, 39907, 6.68, 637953), "linéaire": (0.839, 40973, 6.83, 615985), "log-log": (0.847, 40662, 6.74, 627260),52               "Box–Cox": (0.853, 39844, 6.65, 635171), "semi-log sans secteurs": (0.766, 50379, 8.16, 664342), "semi-log, indicatrices trimestrielles": (0.856, 39587, 6.57, 649980)}53    for nom, (r2, rmse, cod, sujet) in attendu.items():54        assert v[nom]["r2"] == pytest.approx(r2, abs=0.001), nom55        assert v[nom]["rmse"] == pytest.approx(rmse, abs=1), nom56        assert v[nom]["ratios"]["COD"] == pytest.approx(cod, abs=0.01), nom57        assert v[nom]["sujet"] == pytest.approx(sujet, abs=1), nom58    assert v["semi-log, indicatrices trimestrielles"]["k"] == 29 and v["log-log"]["k"] == 1559    bc = v["Box–Cox"]60    assert bc["lambda"] == pytest.approx(0.157, abs=0.001) and bc["ic95"][0] == pytest.approx(-0.036, abs=0.001) and bc["ic95"][1] == pytest.approx(0.353, abs=0.001)61    b = bc["boxcox"]62    assert 2 * (b["ll_max"] - b["ll_0"]) == pytest.approx(2.5, abs=0.1) and 2 * (b["ll_max"] - b["ll_1"]) == pytest.approx(66, abs=1)63    # linéaire : prix implicite constant du m² habitable64    XL = v["linéaire"]["X"]; assert v["linéaire"]["mco"]["beta"][list(XL.columns).index("S")] == pytest.approx(1798, abs=1)65    # omission des secteurs66    cS = v["semi-log sans secteurs"]["coef"]67    assert cS["dist_centre"] == pytest.approx(-0.0117, abs=0.0001) and cS["ln_T"] == pytest.approx(0.0419, abs=0.0001)68    assert v["semi-log sans secteurs"]["sigma"] == pytest.approx(0.104, abs=0.0005)69    # écart du sujet entre variantes bien spécifiées70    assert 627_000 < min(v["log-log"]["sujet"], v["Box–Cox"]["sujet"]) and max(v["semi-log, indicatrices trimestrielles"]["sujet"], v["semi-log (référence)"]["sujet"]) < 650_0007172def test_prix_implicites(res):73    pi = res["prix_implicites"].set_index("caracteristique")74    assert pi["P_sujet"].iloc[0] == pytest.approx(637_953, abs=1)75    assert pi.loc["Superficie habitable", "prix_implicite"] == pytest.approx(2084, abs=1) and pi.loc["Superficie habitable", "se"] == pytest.approx(49, abs=1)76    assert pi.loc["Superficie du terrain", "prix_implicite"] == pytest.approx(96, abs=1)77    assert pi.loc["Âge (une année de plus, à 28 ans)", "prix_implicite"] == pytest.approx(-2308, abs=1)78    assert pi.loc["Conditions du marché (un mois)", "prix_implicite"] == pytest.approx(2319, abs=1)79    assert pi.loc["Qualité (un échelon de plus)", "prix_implicite"] == pytest.approx(40182, abs=1) and pi.loc["Qualité (un échelon de plus)", "kennedy"] == pytest.approx(40176, abs=1)80    assert pi.loc["Garage (une place de plus)", "prix_implicite"] == pytest.approx(21031, abs=1)81    assert pi.loc["Sous-sol fini (contribution : fini vs non fini)", "prix_implicite"] == pytest.approx(14183, abs=1)82    assert pi.loc["Piscine (ajout)", "prix_implicite"] == pytest.approx(17023, abs=1)83    assert pi.loc["Cottage à étages vs plain-pied", "prix_implicite"] == pytest.approx(19502, abs=1)84    assert pi.loc["Secteur Aylmer vs Hull", "prix_implicite"] == pytest.approx(56886, abs=1) and pi.loc["Secteur Aylmer vs Hull", "kennedy"] == pytest.approx(56701, abs=1)85    assert pi.loc["Secteur Gatineau vs Hull", "prix_implicite"] == pytest.approx(-14070, abs=1)86    # identité : prix implicite HP = P̂ (exp β − 1) ; Kennedy < HP pour β > 087    assert (pi.loc[pi["type"] == "indicatrice", "kennedy"].abs() - pi.loc[pi["type"] == "indicatrice", "prix_implicite"].abs() <= 0).sum() >= 58889def test_prediction_et_intervalle(res):90    p = res["prediction"]91    assert p["ln_pred"] == pytest.approx(13.3627, abs=0.0001)92    assert p["naif"] == pytest.approx(635_821, abs=1) and p["duan"] == pytest.approx(637_953, abs=1) and p["lognormal"] == pytest.approx(638_016, abs=1)93    assert p["facteur_duan"] == pytest.approx(1.00335, abs=0.00001) and p["facteur_lognormal"] == pytest.approx(1.00345, abs=0.00001)94    assert p["sigma2"] == pytest.approx(0.00689, abs=0.00001) and p["xVx"] == pytest.approx(0.000105, abs=0.000002)95    assert p["xVx"] / (p["sigma2"] + p["xVx"]) == pytest.approx(0.015, abs=0.001)96    assert p["sd_pred"] == pytest.approx(0.0836, abs=0.0001)97    assert p["ic95"][0] == pytest.approx(539_682, abs=1) and p["ic95"][1] == pytest.approx(749_087, abs=1)98    assert p["ic80"][0] == pytest.approx(571_192, abs=1) and p["ic80"][1] == pytest.approx(707_764, abs=1)99    assert (p["ic95"][1] / p["duan"] - 1) * 100 == pytest.approx(17.4, abs=0.05) and (1 - p["ic95"][0] / p["duan"]) * 100 == pytest.approx(15.4, abs=0.05)100    assert p["pred_2024_07"] == pytest.approx(580_375, abs=1) and p["ratio_role"] == pytest.approx(0.806, abs=0.001)101    # identité : intervalle symétrique en log autour de ln_pred102    assert math.log(p["ic95"][1]) - p["ln_pred"] == pytest.approx(p["ln_pred"] - math.log(p["ic95"][0]), abs=1e-9)103104def test_ratios_et_validation_croisee(res):105    r = res["ratios_modele"]106    assert r["COD"] == pytest.approx(6.68, abs=0.01) and r["PRD"] == pytest.approx(1.007, abs=0.001) and r["PRB"] == pytest.approx(-0.058, abs=0.001) and r["ratio_median"] == pytest.approx(0.997, abs=0.001)107    cv = res["cv"]108    assert cv["rmse"] == pytest.approx(40_888, abs=1) and cv["ratios"]["COD"] == pytest.approx(6.83, abs=0.01) and cv["ratios"]["PRD"] == pytest.approx(1.007, abs=0.001)109    assert cv["erreur_abs_pct_mediane"] == pytest.approx(5.67, abs=0.01)110    ro = res["role"]["ratios"]111    assert ro["ratio_median"] == pytest.approx(0.925, abs=0.001) and ro["COD"] == pytest.approx(6.12, abs=0.01) and ro["PRD"] == pytest.approx(0.999, abs=0.001) and ro["PRB"] == pytest.approx(0.064, abs=0.001)112    brut = h.statistiques_ratio(res["ventes"]["valeur_role_2025"].to_numpy(float), res["ventes"]["prix_vente"].to_numpy(float))113    assert brut["ratio_median"] == pytest.approx(0.914, abs=0.001) and brut["COD"] == pytest.approx(6.90, abs=0.01)114115def test_indice_et_sensibilite(res):116    idx = res["indice"]117    assert len(idx) == 15 and idx["trimestre"].iloc[0] == "2023T1" and idx["trimestre"].iloc[-1] == "2026T3"118    assert idx["indice_dummies"].iloc[-1] == pytest.approx(122.0, abs=0.05) and idx["indice_tendance"].iloc[-1] == pytest.approx(116.5, abs=0.05)119    assert idx["n_ventes"].iloc[-1] == 22 and idx["n_ventes"].sum() == 600120    assert idx["haut"].iloc[-1] / idx["indice_dummies"].iloc[-1] - 1 == pytest.approx(0.036, abs=0.001)121    s = res["sensibilite"].set_index("echantillon")122    assert s.loc["Secteur Hull seulement", "n"] == 186 and s.loc["Secteur Hull seulement", "sujet"] == pytest.approx(625_967, abs=1)123    assert s.loc["Ventes des 18 derniers mois", "n"] == 236 and s.loc["Ventes des 18 derniers mois", "sujet"] == pytest.approx(640_098, abs=1)124    assert s.loc["Cottages à étages seulement", "n"] == 330 and s.loc["Cottages à étages seulement", "sujet"] == pytest.approx(629_272, abs=1)125    assert s.loc["Sans les observations influentes (Cook > 4/n)", "n"] == 571 and s.loc["Sans les observations influentes (Cook > 4/n)", "sujet"] == pytest.approx(636_872, abs=1)126    assert s.loc["Superficie de 110 à 230 m²", "n"] == 495 and s.loc["Superficie de 110 à 230 m²", "sujet"] == pytest.approx(635_048, abs=1)127    assert (s["beta_ln_S"].between(0.539, 0.556)).all()128    assert (s["sujet"].max() / s["sujet"].min() - 1) * 100 == pytest.approx(2.26, abs=0.05)129130def test_residus_des_comparables(res):131    rc = res["residus_comparables"]; ids = list(res["comparables"]["id_vente"])132    assert ids == ["G0574", "G0404", "G0468", "G0477", "G0575"]133    assert dict(zip(ids, np.round(rc.values, 3))) == {"G0574": 0.007, "G0404": -0.161, "G0468": -0.104, "G0477": -0.111, "G0575": -0.084}134    assert (math.exp(rc.mean()) - 1) * 100 == pytest.approx(-8.7, abs=0.05)135136def test_solutions_exercices(res):137    e = res["exercices"]138    # 18.1139    assert e["1"]["garage_hp"] * 100 == pytest.approx(3.29, abs=0.005) and e["1"]["buck_hp"] * 100 == pytest.approx(-22.12, abs=0.01)140    assert e["1"]["buck_kennedy"] * 100 == pytest.approx(-22.32, abs=0.01) and e["1"]["mois_annuel"] * 100 == pytest.approx(4.41, abs=0.01)141    assert (1.1 ** 0.539 - 1) * 100 == pytest.approx(5.27, abs=0.01)142    # 18.2143    assert e["2"]["pS_marginal"] == pytest.approx(2084, abs=0.5) and e["2"]["pS_10m2_exact"] == pytest.approx(20558, abs=1) and e["2"]["pS_10m2_lineaire"] == pytest.approx(20841, abs=1)144    assert e["2"]["age28"] == pytest.approx(-2325, abs=1) and e["2"]["age60"] == pytest.approx(-1263, abs=1) and e["2"]["age_min"] == pytest.approx(98.1, abs=0.1)145    assert e["2"]["piscine_hp"] == pytest.approx(17002, abs=1)146    # 18.3147    assert e["3"]["bc_1"] == 499_999 and e["3"]["bc_05"] == pytest.approx(1412.2, abs=0.1) and e["3"]["bc_0"] == pytest.approx(13.122, abs=0.001)148    # 18.4149    assert e["4"]["naif"] == pytest.approx(635_839, abs=1) and e["4"]["duan"] == pytest.approx(638_000, abs=1) and e["4"]["lognormal"] == pytest.approx(638_032, abs=1)150    assert e["4"]["sd"] == pytest.approx(0.0836, abs=0.0001) and e["4"]["part_xVx"] * 100 == pytest.approx(1.4, abs=0.05)151    assert e["4"]["bas"] == pytest.approx(539_742, abs=1) and e["4"]["haut"] == pytest.approx(749_044, abs=1)152    assert e["4"]["demi_largeur_pct"] == pytest.approx(17.8, abs=0.05) and (1 - e["4"]["bas"] / e["4"]["naif"]) * 100 == pytest.approx(15.1, abs=0.05)153    assert math.sqrt(0.0830 ** 2 + 0.00001) == pytest.approx(0.0831, abs=0.0001)154    # 18.5155    r5 = e["5"]156    assert r5["ratio_median"] == pytest.approx(0.906, abs=0.001) and r5["ratio_moyen"] == pytest.approx(0.902, abs=0.001)157    assert r5["COD"] == pytest.approx(2.68, abs=0.01) and r5["PRD"] == pytest.approx(1.011, abs=0.001) and r5["PRB"] == pytest.approx(-0.075, abs=0.001) and r5["PRB_se"] == pytest.approx(0.009, abs=0.001)158    assert r5["ratio_pondere"] == pytest.approx(0.8926, abs=0.0001) and np.round(r5["ratios"], 3)[0] == 0.955 and np.round(r5["ratios"], 3)[-1] == 0.840159    # 18.6160    assert e["6"]["sujet_sans"] / e["6"]["sujet_ref"] - 1 == pytest.approx(0.041, abs=0.001)161    assert e["6"]["sigma_sans_qualite"] == pytest.approx(0.098, abs=0.0005) and e["6"]["r2_sans_qualite"] == pytest.approx(0.797, abs=0.001)162    assert e["6"]["lnS_sans_qualite"] == pytest.approx(0.528, abs=0.001) and e["6"]["sujet_sans_qualite"] == pytest.approx(636_689, abs=1)163    # 18.7164    assert e["7"]["p_S"] == pytest.approx(2084, abs=0.5) and e["7"]["p_S_120"] == pytest.approx(2414, abs=0.5) and e["7"]["p_S_220"] == pytest.approx(1825, abs=0.5)165    assert 638_000 * (120 / 165) ** 0.539 == pytest.approx(537_400, abs=50) and 638_000 * (220 / 165) ** 0.539 == pytest.approx(745_000, abs=50)166    # 18.8167    assert e["8"]["g_mensuel"] * 100 == pytest.approx(0.364, abs=0.001) and e["8"]["g_annuel"] * 100 == pytest.approx(4.46, abs=0.01)168    assert e["8"]["ecart_abs_moyen_pct"] == pytest.approx(2.57, abs=0.01) and e["8"]["ecart_max_pct"] == pytest.approx(5.03, abs=0.01)169    assert e["8"]["G0404_tendance"] == pytest.approx(28_774, abs=1) and e["8"]["G0404_indices"] == pytest.approx(44_760, abs=1)170    assert e["8"]["indice_2025T2"] == pytest.approx(112.13, abs=0.005) and e["8"]["indice_fin"] == pytest.approx(122.04, abs=0.005)171
Files · 317d87e96c9fbe3102824f20418c9f14e795f254
Menu

Theme