SPB Git
12.7 KB · 176 lines python
1"""Chapitre 20 — réconciliation et incertitude : identités, chiffres cités dans le texte et dans les solutions (tolérances déclarées)."""2import sys, pathlib, math3import numpy as np, pytest4sys.path.insert(0, str(pathlib.Path(__file__).resolve().parents[1] / "cases"))5import ch20_reconciliation as c206from traite import cas1, cout as ct78@pytest.fixture(scope="module")9def r():10    return c20.tout()1112# ---------------------------------------------------------------- indications héritées (ch. 16-18, 21)13def test_indications_heritees(r):14    b = r["base"]15    assert list(b["adj5"]["id_vente"]) == ["G0574", "G0404", "G0468", "G0477", "G0575"]16    assert b["adj5"]["prix_ajuste"].values == pytest.approx([640247, 541222, 572793, 568853, 584862], abs=1)17    assert b["V_comp"] == pytest.approx(575565, abs=1) and b["sd_comp_pondere"] == pytest.approx(33997, abs=1)18    assert b["poids_comp"] == pytest.approx([0.179, 0.327, 0.202, 0.159, 0.133], abs=0.001)19    assert b["V_hed"] == pytest.approx(637953, abs=1)20    assert b["ip_hed"]["bas"] == pytest.approx(539682, abs=1) and b["ip_hed"]["haut"] == pytest.approx(749087, abs=1)21    assert len(b["cand"]) == 1622    assert r["cout"]["CN"] == pytest.approx(397567.33, abs=0.01)2324def test_grille_rapide_reproduit_la_bibliotheque(r):25    """La cascade vectorisée du chapitre donne les mêmes prix ajustés et bruts que traite.cas1.ajustements_hedoniques (16 candidats)."""26    b = r["base"]; g = c20.grille_rapide(b["beta"], b["Xs"], b["Xc16"], b["P16"], b["G"])27    assert g["P_aj"] == pytest.approx(b["adj16"]["prix_ajuste"].values, rel=1e-9)28    assert g["brut"] == pytest.approx(b["adj16"]["ajust_brut_pct"].values, rel=1e-9)29    assert g["valeur"] != pytest.approx(b["V_comp"], abs=1)          # 16 candidats ≠ 5 retenus30    g5 = c20.grille_rapide(b["beta"], b["Xs"], b["Xc16"][:5], b["P16"][:5], b["G"])31    assert g5["valeur"] == pytest.approx(b["V_comp"], abs=1e-6) and g5["sd_pondere"] == pytest.approx(b["sd_comp_pondere"], abs=1e-6)32    # identité P_aj = exp(x_s'β)·exp(e_j) pour les 16 candidats (prop. 17.2)33    point = math.exp(float(b["Xs"] @ b["beta"]))34    assert g["P_aj"] == pytest.approx(point * np.exp(b["adj16"]["residu_log"].values), rel=1e-9)3536def test_residus_candidats(r):37    a = r["base"]["adj16"]38    ret = a["retenu"].values39    assert np.mean(np.exp(a.loc[ret, "residu_log"]) - 1) == pytest.approx(-0.0853, abs=0.0005)40    assert np.mean(np.exp(a.loc[~ret, "residu_log"]) - 1) == pytest.approx(-0.0171, abs=0.0005)41    assert np.mean(np.exp(a["residu_log"]) - 1) == pytest.approx(-0.0384, abs=0.0005)4243# ---------------------------------------------------------------- bootstrap et énumération44def test_bootstrap(r):45    bs = r["bootstrap"]; rs = bs["resume"]46    assert len(bs["VA"]) == c20.B == 200047    assert rs["candidats"]["ecart_type"] == pytest.approx(18160, abs=30)48    assert rs["candidats"]["q05"] == pytest.approx(552872, abs=60) and rs["candidats"]["q95"] == pytest.approx(611283, abs=60)49    assert rs["ajustements"]["ecart_type"] == pytest.approx(2672, abs=10)50    assert rs["conjoint"]["ecart_type"] == pytest.approx(18385, abs=30)51    assert rs["conjoint"]["q05"] == pytest.approx(552095, abs=60) and rs["conjoint"]["q95"] == pytest.approx(611779, abs=60)52    assert rs["parametrique"]["ecart_type"] == pytest.approx(2751, abs=10)53    assert rs["prediction"]["ecart_type"] == pytest.approx(6531, abs=15) and rs["prediction"]["moyenne"] == pytest.approx(637736, abs=30)54    assert bs["rho_conjoint_pred"] == pytest.approx(0.088, abs=0.005) and bs["rho_ajustements_pred"] == pytest.approx(0.425, abs=0.01)55    assert bs["part_C_sous_hed"] > 0.9956    # l'incertitude des coefficients est petite devant celle de la sélection57    assert rs["ajustements"]["ecart_type"] < 0.2 * rs["candidats"]["ecart_type"]58    # la variance conjointe est à peu près la somme des deux (indépendance des deux tirages)59    assert rs["conjoint"]["ecart_type"] ** 2 == pytest.approx(rs["candidats"]["ecart_type"] ** 2 + rs["ajustements"]["ecart_type"] ** 2, rel=0.05)6061def test_exhaustif(r):62    ex = r["exhaustif"]63    assert ex["n"] == math.comb(16, 5) == 436864    assert ex["moyenne"] == pytest.approx(606317, abs=1) and ex["ecart_type"] == pytest.approx(20492, abs=1)65    assert ex["min"] == pytest.approx(550231, abs=1) and ex["max"] == pytest.approx(661670, abs=1)66    assert ex["rang_selection"] == 27967    assert ex["part_sous_hed"] == pytest.approx(0.935, abs=0.002)68    par = ex["par_n_retenus"]69    assert list(par["count"]) == [462, 1650, 1650, 550, 55, 1]70    assert par.loc[5, "mean"] == pytest.approx(575565, abs=1)71    # la moyenne par nombre de retenus décroît : plus on inclut les cinq « vrais » comparables, plus la valeur baisse72    assert all(np.diff(par["mean"].values) < 0)7374def test_elargissement(r):75    el = r["elargissement"].set_index("k")76    assert el.loc[5, "valeur_ponderee"] == pytest.approx(575565, abs=1)77    assert el.loc[16, "valeur_ponderee"] == pytest.approx(604689, abs=1) and el.loc[16, "moyenne"] == pytest.approx(611401, abs=1)78    assert el.loc[16, "residu_moyen_pct"] == pytest.approx(-0.0384, abs=0.0005)79    assert el.loc[10, "valeur_ponderee"] == pytest.approx(592737, abs=1)8081# ---------------------------------------------------------------- coût (chapitre 24)82def test_cout(r):83    co = r["cout"]; CN = co["CN"]84    assert CN == pytest.approx(397567.33, abs=0.01)85    assert co["V_L"] == 277000 and co["V_L_mediane"] == pytest.approx(276954, abs=1) and co["n_terrain"] == 1386    assert co["D_ext"] == pytest.approx(87461.05, abs=0.05) and co["taux_ext"] == pytest.approx(0.2200, abs=0.0001)87    assert co["D_fonc"] == pytest.approx(7605.31, abs=0.01)88    assert co["D_tot"] == pytest.approx(95066.36, abs=0.05) and co["taux_tot"] == pytest.approx(0.2391, abs=0.0001)89    assert co["valeur"] == pytest.approx(co["V_L"] + CN - co["D_tot"], abs=1e-6) and co["valeur"] == pytest.approx(579500.97, abs=0.05)90    assert co["arrondie"] == 580000 and co["ventilee"] == pytest.approx(445872.14, abs=0.05)91    assert co["cout_neuf_plus_terrain"] == pytest.approx(674567, abs=1)92    assert co["extraction_hed"]["depreciation"] == pytest.approx(36614, abs=1) and co["extraction_hed"]["taux"] == pytest.approx(0.0921, abs=0.0001)93    assert co["extraction_comp"]["depreciation"] == pytest.approx(99002, abs=1) and co["extraction_comp"]["taux"] == pytest.approx(0.2490, abs=0.0001)94    # contre-exemple : hypothèses d'usage (allocation 30 %, âge-vie 60 ans)95    u = co["usage"]96    assert u["V_L"] == pytest.approx(172669, abs=1) and u["D"] == pytest.approx(185531, abs=1) and u["valeur"] == pytest.approx(384705, abs=1)97    assert u["extraction_hed"]["depreciation"] == pytest.approx(-67716, abs=1)98    assert co["sd_spec"] == pytest.approx(2377, abs=2)99    # identité de la bibliothèque : valeur_par_cout = V_L + C_N − D100    v = ct.valeur_par_cout(100000, 400000, 150000)101    assert v["valeur"] == 350000 and v["taux_depreciation"] == pytest.approx(0.375)102103def test_bootstrap_cout(r):104    bk = r["bootstrap_cout"]; rk = bk["resume"]105    assert len(bk["VK"]) == 2000 and not np.isnan(bk["VK"]).any()106    assert rk["ecart_type"] == pytest.approx(23575, abs=60)107    assert rk["q05"] == pytest.approx(552142, abs=150) and rk["q95"] == pytest.approx(628078, abs=150)108    assert bk["rho_cout_pred"] == pytest.approx(0.225, abs=0.01) and bk["rho_cout_comp"] == pytest.approx(0.018, abs=0.01)109    assert bk["sd_VL"] == pytest.approx(29803, abs=80) and bk["sd_T28"] == pytest.approx(0.0419, abs=0.0005)110111# ---------------------------------------------------------------- combinaison112def test_poids_optimaux_et_mcg(r):113    cb = r["combinaison"]; S = cb["Sigma"]114    for w in cb["schemas"].values():115        assert w.sum() == pytest.approx(1.0, abs=1e-12)116    w = c20.poids_gls(S)117    var = w @ S @ w118    rng = np.random.default_rng(0)119    for _ in range(200):                      # tout autre vecteur de poids sommant à 1 a une variance ≥ celle des MCG120        d = rng.normal(size=3); d -= d.mean(); w2 = w + 0.1 * d121        assert w2 @ S @ w2 >= var - 1e-6122    # formule à deux indications = MCG sur la sous-matrice 2 × 2123    s1, s2, rho = cb["s"][0], cb["s"][1], cb["rho"]124    S2 = np.array([[s1 ** 2, rho * s1 * s2], [rho * s1 * s2, s2 ** 2]])125    assert c20.poids_optimaux_deux(s1, s2, rho) == pytest.approx(c20.poids_gls(S2)[0], rel=1e-12)126    assert c20.poids_optimaux_deux(s1, s2, 0.0) == pytest.approx((1 / s1 ** 2) / (1 / s1 ** 2 + 1 / s2 ** 2), rel=1e-12)127    assert c20.poids_optimaux_deux(s1, s2, 0.9) < 0            # poids négatif lorsque ρ > σ_h/σ_c128129def test_combinaison_chiffres(r):130    cb = r["combinaison"]; t = cb["tableau"].set_index("schema")131    assert cb["V"] == pytest.approx([575565, 637953, 579501], abs=1)132    assert cb["s"] == pytest.approx([18385, 11462, 23695], abs=60)133    assert cb["sd_spec_hed"] == pytest.approx(9420, abs=1) and cb["sd_pred_boot"] == pytest.approx(6531, abs=15)134    assert cb["R"][0, 1] == pytest.approx(0.088, abs=0.005) and cb["R"][1, 2] == pytest.approx(0.225, abs=0.01) and cb["R"][0, 2] == pytest.approx(0.018, abs=0.01)135    assert t.loc["égale (un tiers chacune)", "valeur"] == pytest.approx(597673, abs=1)136    assert cb["moyenne_simple_usage"] == pytest.approx(532741, abs=1)137    assert t.loc["inverse de la variance (indépendance)", "valeur"] == pytest.approx(614579, abs=200)138    assert t.loc["inverse de la variance (indépendance)", ["w_comp", "w_hed", "w_cout"]].values == pytest.approx([0.240, 0.616, 0.144], abs=0.005)139    assert t.loc["variance minimale avec corrélation (MCG)", "valeur"] == pytest.approx(616823, abs=250)140    assert t.loc["variance minimale avec corrélation (MCG)", ["w_comp", "w_hed", "w_cout"]].values == pytest.approx([0.248, 0.655, 0.097], abs=0.006)141    assert t.loc["raisonnée (retenue)", "valeur"] == pytest.approx(604230, abs=1)142    assert t.loc["raisonnée (retenue)", "ecart_type"] == pytest.approx(10450, abs=40)143    assert cb["conclusion"] == 605000 and cb["fourchette90"] == (590000, 625000) and cb["fourchette_conclue"] == (575000, 640000)144    assert cb["q"]["q05"] == pytest.approx(592310, abs=150) and cb["q"]["q95"] == pytest.approx(622009, abs=150)145    assert cb["ecart_conclusion_comp"] == pytest.approx(0.0511, abs=0.0005) and cb["ecart_conclusion_hed"] == pytest.approx(-0.0517, abs=0.0005)146    assert cb["w_opt_deux"][0.0] == pytest.approx(0.280, abs=0.002) and cb["w_opt_deux"][0.6] == pytest.approx(0.023, abs=0.002)147148def test_verite_synthetique(r):149    vt = r["verite"]; b = r["base"]150    assert vt["V_true"] == pytest.approx(641159, abs=5)151    assert vt["se_c"] == pytest.approx(0.0034, abs=0.0002) and vt["sigma_e"] == pytest.approx(0.0827, abs=0.001)152    assert abs(vt["ecart_hed"]) < 0.01 and vt["ecart_comp"] == pytest.approx(-0.1023, abs=0.001)153    assert r["combinaison"]["conclusion"] / vt["V_true"] - 1 == pytest.approx(-0.0564, abs=0.001)154    # la prédiction hédonique est dans l'intervalle de la vérité ± 2 erreurs-types de la constante155    assert vt["V_true_bas"] <= b["V_hed"] * 1.01 and b["V_hed"] * 0.99 <= vt["V_true_haut"]156157# ---------------------------------------------------------------- solutions des exercices158def test_solutions_exercices(r):159    e = r["exercices"]160    assert e["ex1"]["w1"] == pytest.approx(0.1379, abs=0.0001) and e["ex1"]["V"] == pytest.approx(628552, abs=1) and e["ex1"]["sd"] == pytest.approx(11142, abs=1)161    assert e["ex1"]["V_egal"] == 609000 and e["ex1"]["sd_egal"] == pytest.approx(16155, abs=1)162    assert e["ex2"]["ic_bas"] == pytest.approx(625586, abs=5) and e["ex2"]["ic_haut"] == pytest.approx(650596, abs=5)163    assert e["ex2"]["ip_bas"] == pytest.approx(539682, abs=60) and e["ex2"]["ip_haut"] == pytest.approx(749087, abs=60)164    assert e["ex2"]["rapport_largeurs"] == pytest.approx(8.36, abs=0.02)165    assert e["ex6"]["V_30_55_15"] == pytest.approx(610469, abs=1) and e["ex6"]["arrondi_30_55_15"] == 610000166    assert e["ex6"]["V_k16_raisonnee"] == pytest.approx(615880, abs=1) and e["ex6"]["arrondi_k16"] == 615000167    assert e["ex6"]["V_55_30_15"] == pytest.approx(594872, abs=1) and e["ex6"]["arrondi_55_30_15"] == 595000168    assert e["ex3"][0.3]["w1"] == pytest.approx(0.0435, abs=0.0001) and e["ex3"][0.5]["w1"] == pytest.approx(-0.0526, abs=0.0001)169    assert e["ex3"][0.3]["sd"] == pytest.approx(11935, abs=1)170    assert e["ex4"]["moyenne"] == 586150 and e["ex4"]["ecart_type"] == pytest.approx(14954, abs=1)171    assert e["ex4"]["q05"] == pytest.approx(567650, abs=1) and e["ex4"]["q95"] == pytest.approx(611650, abs=1)172    assert e["ex5"]["a"] == pytest.approx(453240, abs=1) and e["ex5"]["b"] == pytest.approx(450590, abs=1)173    assert e["ex5"]["extraction_hed"]["depreciation"] == pytest.approx(36614, abs=1) and e["ex5"]["extraction_comp"]["depreciation"] == pytest.approx(99002, abs=1)174    assert e["ex5"]["V_L_pour_hed"] == pytest.approx(335452, abs=1)175    assert e["ex7"]["delta"] == 14000 and e["ex7"]["esperance"] == 600800 and e["ex7"]["V_H"] == 605000176
Files · 577f3e95e830087bf4d5101ceb2925ae494ca15c
Menu

Theme