SPB Git
12.0 KB · 171 lines python
1"""Chapitre 17 — vérification des identités et des chiffres cités dans le texte et 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 ch17_ajustements as c176from traite import comparables as cp78@pytest.fixture(scope="module")9def r():10    return c17.tout()1112def test_memes_comparables_que_ch16(r):13    a = r["base"]["adj"]14    assert list(a["id_vente"]) == ["G0574", "G0404", "G0468", "G0477", "G0575"]15    assert list(a["prix_vente"]) == [598000, 506500, 624500, 538000, 513500]1617def test_prix_ajustes_et_statistiques(r):18    a = r["base"]["adj"]19    assert a["prix_ajuste"].values == pytest.approx([640247, 541222, 572793, 568853, 584862], abs=1)20    assert a["prix_ajuste"].mean() == pytest.approx(581595, abs=1) and a["prix_ajuste"].median() == pytest.approx(572793, abs=1)21    assert a["prix_ajuste"].std() < a["prix_vente"].std()22    # ajustement de localisation nul : même secteur, tous à moins de 3 km du centre (variable tronquée)23    assert (a["localisation"].abs() < 1e-6).all()24    # brut et net recalculés avec la bibliothèque25    for _, row in a.iterrows():26        bn = cp.ajustement_brut_net({g: row[g] for g in c17.GROUPES}, row["prix_vente"])27        assert bn["brut_pct"] == pytest.approx(row["ajust_brut_pct"], abs=1e-9) and bn["net_pct"] == pytest.approx(row["ajust_net_pct"], abs=1e-9)28    assert a["ajust_brut_pct"].values == pytest.approx([0.198, 0.109, 0.176, 0.224, 0.268], abs=0.001)2930def test_identite_somme_des_ajustements(r):31    a = r["base"]["adj"]32    for _, row in a.iterrows():33        assert row["prix_vente"] + sum(row[g] for g in c17.GROUPES) == pytest.approx(row["prix_ajuste"], abs=1e-6)3435def test_identite_prix_ajuste_egal_prediction_fois_residu(r):36    """P_aj_j = exp(x_s'β) × exp(e_j) : les prix ajustés par la grille hédonique sont la prédiction ponctuelle multipliée par le facteur résiduel du comparable."""37    i = r["identite_residus"]38    assert i["prix_ajuste"].values == pytest.approx(i["point_x_exp_res"].values, rel=1e-9)39    b = r["base"]40    assert b["point_sans_duan"] == pytest.approx(635821, abs=1) and b["prediction_hedonique"] == pytest.approx(637953, abs=1)41    assert np.mean(np.exp(b["adj"]["residu_log"])) - 1 == pytest.approx(-0.0853, abs=0.0005)42    assert b["adj"]["residu_log"].mean() == pytest.approx(-0.0907, abs=0.0005)43    assert b["adj"]["prix_ajuste"].mean() / b["point_sans_duan"] - 1 == pytest.approx(-0.0853, abs=0.0005)4445def test_cascade_g0404(r):46    c = r["cascade_G0404"]47    etapes = dict(zip(c["etape"], c["ajustement"]))48    assert etapes["Conditions du marché"] == pytest.approx(28733, abs=1)49    assert etapes["Superficie habitable"] == pytest.approx(8953, abs=1)50    assert etapes["Terrain"] == pytest.approx(7279, abs=1)51    assert etapes["Âge"] == pytest.approx(-10242, abs=1)52    assert c["base_apres"].iloc[-1] == pytest.approx(541222, abs=1)53    # commutativité du total, non de la répartition54    ci = r["cascade_G0404_ordre_inverse"]55    assert ci["base_apres"].iloc[-1] == pytest.approx(541222, abs=1)56    ei = dict(zip(ci["etape"], ci["ajustement"]))57    assert ei["Conditions du marché"] == pytest.approx(29054, abs=1) and ei["Âge"] == pytest.approx(-9407, abs=1)58    # Δln total = somme des Δln (exactitude de la forme multiplicative)59    assert c["dlog"].iloc[1:-1].sum() == pytest.approx(c["dlog"].iloc[-1], abs=1e-12)6061def test_formes_additive_multiplicative_mixte(r):62    f = r["formes"]63    assert (f["ecart_additif_pct"].abs() < 0.004).all() and (f["ecart_mixte_pct"].abs() < 0.005).all()64    g0574 = f[f["id_vente"] == "G0574"].iloc[0]65    assert g0574["additif"] == pytest.approx(641341, abs=1) and g0574["multiplicatif"] == pytest.approx(640247, abs=1)66    for _, row in f.iterrows():67        assert row["multiplicatif"] == pytest.approx(row["prix_vente"] * math.exp(row["somme_dlog"]), rel=1e-12)6869def test_ajustement_de_temps(r):70    t = r["temps"]; b = r["base"]71    assert t["mois"] == pytest.approx(15.2, abs=0.05)72    assert 100 * b["g_mensuel"] == pytest.approx(0.364, abs=0.001) and 100 * b["g_annuel"] == pytest.approx(4.46, abs=0.01)73    assert 100 * c17.TAUX_GENERATION == pytest.approx(4.43, abs=0.01)74    assert t["coefficient"] == pytest.approx(28733, abs=1) and t["taux_compose_estime"] == pytest.approx(t["coefficient"], abs=1)75    assert t["indices"] == pytest.approx(t["coefficient"], abs=1)76    assert t["taux_compose_generation"] == pytest.approx(28558, abs=1) and t["lineaire_simple"] == pytest.approx(27998, abs=1)7778def test_financement(r):79    fi = r["financement"]; fa = r["financement_amorti"]80    assert fi["paiement_mensuel"] == pytest.approx(248.96, abs=0.01) and fi["valeur_actualisee"] == pytest.approx(130389, abs=1)81    assert fi["avantage"] == pytest.approx(19611, abs=1) and 100 * fi["ajustement_pct"] == pytest.approx(-3.16, abs=0.01) and fi["prix_comptant"] == pytest.approx(600389, abs=1)82    assert fa["avantage"] == pytest.approx(18138, abs=1)83    assert 100 * fi["i_m_contrat"] == pytest.approx(0.1660, abs=0.0001) and 100 * fi["i_m_marche"] == pytest.approx(0.4124, abs=0.0001)8485def test_paires(r):86    g = r["resume_garage"]; s = r["resume_sous_sol"]87    assert g["n"] == 15 and s["n"] == 1488    assert g["moyenne"] == pytest.approx(7991, abs=1) and g["mediane"] == pytest.approx(6034, abs=1) and g["erreur_type"] == pytest.approx(16594, abs=1)89    assert s["moyenne"] == pytest.approx(23623, abs=1) and s["mediane"] == pytest.approx(30789, abs=1) and s["erreur_type"] == pytest.approx(6255, abs=1)90    ig = r["implicite_garage"]; iss = r["implicite_sous_sol"]91    assert 100 * ig["effet_pct"] == pytest.approx(3.30, abs=0.01) and ig["au_prix_median_paires"] == pytest.approx(17155, abs=1)92    assert 100 * iss["effet_pct"] == pytest.approx(2.22, abs=0.01) and iss["au_prix_median_paires"] == pytest.approx(10394, abs=1)93    # le prix implicite hédonique est à l'intérieur de ± 1,96 erreur-type de la moyenne des paires (garage) ; à l'extérieur pour le sous-sol94    assert abs(ig["au_prix_median_paires"] - g["moyenne"]) < 1.96 * g["erreur_type"]95    assert abs(iss["au_prix_median_paires"] - s["moyenne"]) > 1.96 * s["erreur_type"]96    # critères déclarés : mêmes secteur/type, superficie ±5 %, âge ±5 ans, dates à moins de 12 mois97    p = r["paires_garage"]98    assert (abs(p["superficie_avec"] / p["superficie_sans"] - 1) <= 0.05 + 1e-12).all() and (p["delta_mois"].abs() <= 12).all()99    vg = r["variantes_garage"]; assert vg["n"].min() >= 9 and vg["n"].max() == 45100101def test_halvorsen_palmquist_kennedy(r):102    h = r["hp_kennedy"].set_index("variable")103    assert 100 * h.loc["garage", "hp_pct"] == pytest.approx(3.297, abs=0.002) and 100 * h.loc["garage", "kennedy_pct"] == pytest.approx(3.295, abs=0.002)104    assert 100 * h.loc["sect_Buckingham", "naif_pct"] == pytest.approx(-25.04, abs=0.01)105    assert 100 * h.loc["sect_Buckingham", "hp_pct"] == pytest.approx(-22.15, abs=0.01) and 100 * h.loc["sect_Buckingham", "kennedy_pct"] == pytest.approx(-22.35, abs=0.01)106    assert cp.ajustement_depuis_coefficient_log(0.03243) == pytest.approx(math.exp(0.03243) - 1)107    assert cp.ajustement_depuis_coefficient_log_sans_biais(0.03243, 0.00501 ** 2) < cp.ajustement_depuis_coefficient_log(0.03243)108109def test_ponderation_et_reconciliation(r):110    rec = r["reconciliation"]111    assert rec["poids_inverse_brut"] == pytest.approx([0.179, 0.327, 0.202, 0.159, 0.133], abs=0.001)112    assert sum(rec["poids_inverse_brut"]) == pytest.approx(1.0)113    assert rec["egal"]["valeur_ponderee"] == pytest.approx(581595, abs=1)114    assert rec["inverse_brut"]["valeur_ponderee"] == pytest.approx(575565, abs=1)115    assert rec["inverse_brut"]["min"] == pytest.approx(541222, abs=1) and rec["inverse_brut"]["max"] == pytest.approx(640247, abs=1)116    assert rec["inverse_brut"]["etendue_pct"] == pytest.approx(0.172, abs=0.001)117118def test_analyse_qualitative(r):119    q = r["qualitatif"].set_index("id_vente"); e = r["encadrement"]120    assert q.loc["G0468", "position"] == "supérieur" and (q.drop("G0468")["position"] == "inférieur").all()121    assert e == {"borne_basse": 598000.0, "borne_haute": 624500.0, "n_inferieurs": 4, "n_superieurs": 1}122123def test_sensibilite(r):124    s = r["sensibilite"].set_index("scenario")125    assert s.loc["Omission de l'ajustement « conditions du marché »", "valeur"] == pytest.approx(545208, abs=1)126    assert s.loc["Omission de l'ajustement « qualité »", "valeur"] == pytest.approx(597252, abs=1)127    assert s.loc["Conditions du marché : taux de 3 % à 6 %/an", "etendue"] == pytest.approx(16734, abs=2)128    assert s.loc["Superficie habitable : coefficient ±1,96 é.-t.", "etendue"] < 1000129    assert s["etendue"].max() == pytest.approx(30357, abs=2)130131def test_solutions_exercices():132    # ex. 17.1 : additif, signe du point de vue du sujet133    aj = {"temps": 18000, "superficie": -12500, "garage": 15000, "sous_sol": -9000, "qualite": 0}134    assert cp.prix_ajuste_additif(540000, aj) == pytest.approx(551500)135    bn = cp.ajustement_brut_net(aj, 540000)136    assert bn["brut_pct"] == pytest.approx(54500 / 540000) and bn["net_pct"] == pytest.approx(11500 / 540000)137    # ex. 17.2 : temps par taux composé et par indices138    assert cp.ajustement_temps_taux(506500, 0.0443, 15.2) == pytest.approx(28587, abs=1)139    assert 506500 * 0.003642 * 15.2 == pytest.approx(28040, abs=5)140    assert cp.ajustement_temps(506500, 131.2, 138.7) == pytest.approx(28954, abs=1)141    # ex. 17.3 : paires142    p = cp.ventes_appariees([612000, 548000, 589500, 571000, 603000, 557500], [590000, 541000, 562000, 570500, 578000, 549000])143    assert p["moyenne"] == pytest.approx(15083, abs=1) and p["mediane"] == pytest.approx(15250) and p["ecart_type"] == pytest.approx(11151, abs=1)144    assert p["ecart_type"] / math.sqrt(6) == pytest.approx(4552, abs=1)145    # ex. 17.4 : Halvorsen–Palmquist et Kennedy146    assert 100 * cp.ajustement_depuis_coefficient_log(0.045) == pytest.approx(4.603, abs=0.001)147    assert 100 * cp.ajustement_depuis_coefficient_log_sans_biais(0.045, 0.020 ** 2) == pytest.approx(4.582, abs=0.001)148    assert 100 * cp.ajustement_depuis_coefficient_log(-0.25) == pytest.approx(-22.12, abs=0.01)149    assert 100 * cp.ajustement_depuis_coefficient_log_sans_biais(-0.25, 0.08 ** 2) == pytest.approx(-22.37, abs=0.01)150    # ex. 17.5 : multiplicatif vs additif151    assert cp.prix_ajuste_multiplicatif(500000, [0.06, 0.04, -0.05]) == pytest.approx(523640)152    assert cp.prix_ajuste_additif(500000, {"a": 30000, "b": 20000, "c": -25000}) == pytest.approx(525000)153    assert cp.prix_ajuste_mixte(500000, [0.06], {"b": 530000 * 0.04, "c": -530000 * 0.05})["prix_ajuste"] == pytest.approx(524700)154    # ex. 17.6 : pondération inverse du brut155    cs = [cp.ComparableAjuste("A", 600000, {"x": 30000, "y": -60000}), cp.ComparableAjuste("B", 520000, {"x": 26000, "y": 13000}),156          cp.ComparableAjuste("C", 575000, {"x": -23000, "y": 46000, "z": 23000})]157    w = cp.poids_inverse_brut(cs)158    assert [c.prix_ajuste for c in cs] == [570000, 559000, 621000] and [c.brut_pct for c in cs] == pytest.approx([0.15, 0.075, 0.16])159    assert w == pytest.approx([0.254, 0.508, 0.238], abs=0.001)160    rec = cp.reconciliation(cs, w)161    assert rec["valeur_ponderee"] == pytest.approx(576556, abs=1) and cp.reconciliation(cs)["valeur_ponderee"] == pytest.approx(583333, abs=1)162    # ex. 17.7 : grille avec les ajustements tirés des paires (garage 7 991 $, sous-sol 23 623 $) au lieu des prix implicites hédoniques163    r7 = c17.tout(); a = r7["base"]["adj"].copy()164    g, ss = r7["resume_garage"]["moyenne"], r7["resume_sous_sol"]["moyenne"]165    a.loc[a["id_vente"] == "G0574", "garage"] = g; a.loc[a["id_vente"] == "G0575", "garage"] = g; a.loc[a["id_vente"] == "G0574", "sous_sol"] = ss166    a["prix_ajuste"] = a["prix_vente"] + a[c17.GROUPES].sum(axis=1)167    rec7 = c17.reconcilier(r7["base"], a)168    assert a.loc[a["id_vente"] == "G0574", "prix_ajuste"].iloc[0] == pytest.approx(637948, abs=2)169    assert a.loc[a["id_vente"] == "G0575", "prix_ajuste"].iloc[0] == pytest.approx(574188, abs=2)170    assert a["prix_ajuste"].mean() == pytest.approx(579001, abs=2) and rec7["inverse_brut"]["valeur_ponderee"] == pytest.approx(573963, abs=3)171
Files · da8a4ee0304be0aa9c54ed0aad08d985fc4539fc
Menu

Theme