SPB Git
11.0 KB · 158 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ût82def test_cout(r):83    co = r["cout"]; CN = co["CN"]84    assert co["V_L"] == pytest.approx(0.30 * r["base"]["V_comp"], abs=1e-6) and co["V_L"] == pytest.approx(172669, abs=1)85    assert co["D"] == pytest.approx(CN * 28 / 60, abs=1e-6) and co["D"] == pytest.approx(185531, abs=1)86    assert co["valeur"] == pytest.approx(co["V_L"] + CN - co["D"], abs=1e-6) and co["valeur"] == pytest.approx(384705, abs=1)87    assert co["cout_neuf_plus_terrain"] == pytest.approx(570237, abs=1)88    assert co["extraction_hed"]["depreciation"] == pytest.approx(-67716, abs=1) and co["extraction_comp"]["depreciation"] == pytest.approx(-5328, abs=1)89    assert co["min_grille"] == pytest.approx(290043, abs=1) and co["max_grille"] == pytest.approx(479368, abs=1)90    assert co["sd_grille"] == pytest.approx(50737, abs=1)91    assert co["age_effectif_variantes"][20] == pytest.approx(437714, abs=1)92    assert co["V_L_pour_hed"] == pytest.approx(425917, abs=1)93    # identité de la bibliothèque : valeur_par_cout = V_L + C_N − D94    v = ct.valeur_par_cout(100000, 400000, 150000)95    assert v["valeur"] == 350000 and v["taux_depreciation"] == pytest.approx(0.375)9697# ---------------------------------------------------------------- combinaison98def test_poids_optimaux_et_mcg(r):99    cb = r["combinaison"]; S = cb["Sigma"]100    for w in cb["schemas"].values():101        assert w.sum() == pytest.approx(1.0, abs=1e-12)102    w = c20.poids_gls(S)103    var = w @ S @ w104    rng = np.random.default_rng(0)105    for _ in range(200):                      # tout autre vecteur de poids sommant à 1 a une variance ≥ celle des MCG106        d = rng.normal(size=3); d -= d.mean(); w2 = w + 0.1 * d107        assert w2 @ S @ w2 >= var - 1e-6108    # formule à deux indications = MCG sur la sous-matrice 2 × 2109    s1, s2, rho = cb["s"][0], cb["s"][1], cb["rho"]110    S2 = np.array([[s1 ** 2, rho * s1 * s2], [rho * s1 * s2, s2 ** 2]])111    assert c20.poids_optimaux_deux(s1, s2, rho) == pytest.approx(c20.poids_gls(S2)[0], rel=1e-12)112    assert c20.poids_optimaux_deux(s1, s2, 0.0) == pytest.approx((1 / s1 ** 2) / (1 / s1 ** 2 + 1 / s2 ** 2), rel=1e-12)113    assert c20.poids_optimaux_deux(s1, s2, 0.9) < 0            # poids négatif lorsque ρ > σ_h/σ_c114115def test_combinaison_chiffres(r):116    cb = r["combinaison"]; t = cb["tableau"].set_index("schema")117    assert cb["V"] == pytest.approx([575565, 637953, 384705], abs=1)118    assert cb["s"] == pytest.approx([18385, 11462, 50737], abs=30)119    assert cb["sd_spec_hed"] == pytest.approx(9420, abs=1) and cb["sd_pred_boot"] == pytest.approx(6531, abs=15)120    assert t.loc["égale (un tiers chacune)", "valeur"] == pytest.approx(532741, abs=1)121    assert t.loc["inverse de la variance (indépendance)", "valeur"] == pytest.approx(612132, abs=150)122    assert t.loc["inverse de la variance (indépendance)", ["w_comp", "w_hed", "w_cout"]].values == pytest.approx([0.270, 0.695, 0.035], abs=0.005)123    assert t.loc["variance minimale avec corrélation (MCG)", "valeur"] == pytest.approx(612640, abs=200)124    assert t.loc["raisonnée (retenue)", "valeur"] == pytest.approx(600335, abs=1)125    assert t.loc["raisonnée (retenue)", "ecart_type"] == pytest.approx(10411, abs=30)126    assert cb["conclusion"] == 600000 and cb["fourchette90"] == (585000, 620000) and cb["fourchette_conclue"] == (575000, 640000)127    assert cb["q"]["q05"] == pytest.approx(587949, abs=100) and cb["q"]["q95"] == pytest.approx(616418, abs=100)128    assert cb["ecart_conclusion_comp"] == pytest.approx(0.0425, abs=0.0005) and cb["ecart_conclusion_hed"] == pytest.approx(-0.0595, abs=0.0005)129    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)130131def test_verite_synthetique(r):132    vt = r["verite"]; b = r["base"]133    assert vt["V_true"] == pytest.approx(641159, abs=5)134    assert vt["se_c"] == pytest.approx(0.0034, abs=0.0002) and vt["sigma_e"] == pytest.approx(0.0827, abs=0.001)135    assert abs(vt["ecart_hed"]) < 0.01 and vt["ecart_comp"] == pytest.approx(-0.1023, abs=0.001)136    assert r["combinaison"]["conclusion"] / vt["V_true"] - 1 == pytest.approx(-0.0642, abs=0.001)137    # la prédiction hédonique est dans l'intervalle de la vérité ± 2 erreurs-types de la constante138    assert vt["V_true_bas"] <= b["V_hed"] * 1.01 and b["V_hed"] * 0.99 <= vt["V_true_haut"]139140# ---------------------------------------------------------------- solutions des exercices141def test_solutions_exercices(r):142    e = r["exercices"]143    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)144    assert e["ex1"]["V_egal"] == 609000 and e["ex1"]["sd_egal"] == pytest.approx(16155, abs=1)145    assert e["ex2"]["ic_bas"] == pytest.approx(625586, abs=5) and e["ex2"]["ic_haut"] == pytest.approx(650596, abs=5)146    assert e["ex2"]["ip_bas"] == pytest.approx(539682, abs=60) and e["ex2"]["ip_haut"] == pytest.approx(749087, abs=60)147    assert e["ex2"]["rapport_largeurs"] == pytest.approx(8.36, abs=0.02)148    assert e["ex6"]["V_30_65_05"] == pytest.approx(606574, abs=1) and e["ex6"]["arrondi_30_65_05"] == 605000149    assert e["ex6"]["V_k16_raisonnee"] == pytest.approx(611985, abs=1) and e["ex6"]["arrondi_k16"] == 610000150    assert e["ex6"]["V_60_35_05"] == pytest.approx(587858, abs=1)151    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)152    assert e["ex3"][0.3]["sd"] == pytest.approx(11935, abs=1)153    assert e["ex4"]["moyenne"] == 586150 and e["ex4"]["ecart_type"] == pytest.approx(14954, abs=1)154    assert e["ex4"]["q05"] == pytest.approx(567650, abs=1) and e["ex4"]["q95"] == pytest.approx(611650, abs=1)155    assert e["ex5"]["a"] == pytest.approx(453240, abs=1) and e["ex5"]["b"] == pytest.approx(450590, abs=1)156    assert e["ex5"]["extraction"]["depreciation"] == pytest.approx(-38938, abs=1)157    assert e["ex7"]["delta"] == 14000 and e["ex7"]["esperance"] == 595800158
Files · b39d09a4fc988c2cb0fb8ba9c86d745a81de3752
Menu

Theme