SPB Git
11.3 KB · 170 lines python
1"""Chapitre 1 — vérification des identités et des chiffres cités dans le texte et les solutions (données synthétiques, cas 1)."""2import sys, pathlib, math3import numpy as np, pytest4from scipy.stats import norm5from scipy.optimize import brentq6sys.path.insert(0, str(pathlib.Path(__file__).resolve().parents[1] / "cases"))7import ch01_valeur_prix as c189@pytest.fixture(scope="module")10def t():11    return c1.tout()1213# ---------------------------------------------------------------------------- modèle et dispersion1415def test_modele_et_dispersion(t):16    m, d = t["modele"], t["dispersion"]17    assert m["n"] == 600 and m["k"] == 1618    assert m["sigma"] == pytest.approx(0.0830, abs=0.0005)19    assert d["ecart_type_residus"] == pytest.approx(0.0820, abs=0.0005)20    assert m["r2"] == pytest.approx(0.853, abs=0.001) and m["r2_ajuste"] == pytest.approx(0.849, abs=0.001)21    assert d["part_dans_pm10pct"] == pytest.approx(0.745, abs=0.002) and d["part_dans_pm5pct"] == pytest.approx(0.448, abs=0.002)22    assert abs(d["asymetrie"]) < 0.15 and abs(d["aplatissement_exces"]) < 0.523    assert abs(float(np.mean(m["residus"]))) < 1e-8          # MCO avec constante : résidus centrés2425def test_esperance_conditionnelle_sujet(t):26    p = t["pred"]; sigma = p["sigma"]27    assert p["mediane"] == pytest.approx(635821, abs=1)28    assert p["mediane"] == pytest.approx(math.exp(p["ln_pred"]), rel=1e-12)29    assert p["moyenne_lognormale"] == pytest.approx(p["mediane"] * math.exp(sigma ** 2 / 2), rel=1e-12)30    assert p["mode_lognormal"] == pytest.approx(p["mediane"] * math.exp(-sigma ** 2), rel=1e-12)31    assert p["facteur_duan"] == pytest.approx(1.00335, abs=0.00005)32    assert p["facteur_duan"] == pytest.approx(math.exp(sigma ** 2 / 2), abs=0.001)   # Duan ≈ correction log-normale33    assert p["moyenne_duan"] == pytest.approx(637953, abs=1) and p["mode_lognormal"] == pytest.approx(631455, abs=1)34    assert p["ip_bas"] == pytest.approx(539682, abs=1) and p["ip_haut"] == pytest.approx(749087, abs=1)35    assert p["ip_bas"] < p["mode_lognormal"] < p["mediane"] < p["moyenne_lognormale"] < p["ip_haut"]36    # l'intervalle est symétrique en log autour de la prédiction37    assert math.log(p["ip_haut"]) - p["ln_pred"] == pytest.approx(p["ln_pred"] - math.log(p["ip_bas"]), rel=1e-9)3839def test_probabilites_ecart(t):40    sigma = t["modele"]["sigma"]41    assert c1.proba_ecart(sigma, 0.10) == pytest.approx(0.251, abs=0.002)42    assert c1.proba_ecart(sigma, 0.05) == pytest.approx(0.557, abs=0.002)43    assert c1.proba_ecart(sigma, 0.20) == pytest.approx(0.0281, abs=0.0005)44    assert c1.proba_ecart(sigma, 0.0) == pytest.approx(1.0)45    assert c1.proba_ecart(0.04, 0.10) < c1.proba_ecart(0.083, 0.10) < c1.proba_ecart(0.16, 0.10)4647# ---------------------------------------------------------------------------- principes4849def test_contributions(t):50    c = t["contrib"]; p = t["pred"]51    assert c.loc["garage", "contribution_dollars"] == pytest.approx(20291, abs=1)52    assert c.loc["sous_sol", "contribution_dollars"] == pytest.approx(13829, abs=1)53    assert c.loc["etages", "contribution_dollars"] == pytest.approx(18860, abs=1)54    assert c.loc["ln_S", "contribution_dollars"] == pytest.approx(53893, abs=1)55    assert c.loc["age", "contribution_dollars"] + c.loc["age2_100", "contribution_dollars"] == pytest.approx(-84497, abs=2)56    assert c.loc["mois", "contribution_dollars"] == pytest.approx(57385, abs=1)57    # identité : constante + somme des β_k x_k = ln V̂58    const = float(t["modele"]["beta"][0])59    assert const + c["beta_x"].sum() == pytest.approx(p["ln_pred"], abs=1e-9)60    # contribution par retrait : P̂ [1 − exp(−βx)]61    assert c.loc["garage", "contribution_dollars"] == pytest.approx(p["mediane"] * (1 - math.exp(-c.loc["garage", "beta_x"])), rel=1e-12)62    assert c.loc["garage", "effet_pct_unitaire"] == pytest.approx(0.0330, abs=0.0002)63    assert c.loc["piscine", "effet_pct_unitaire"] == pytest.approx(0.0267, abs=0.0002)64    assert c.loc["qualite", "effet_pct_unitaire"] == pytest.approx(0.0630, abs=0.0002)6566def test_rendements_decroissants(t):67    r = t["rendements"]; p = t["pred"]68    assert r["elasticite_S"] == pytest.approx(0.5391, abs=0.0005)69    assert r["prix_moyen_m2"] == pytest.approx(3853, abs=1) and r["prix_marginal_m2"] == pytest.approx(2077, abs=1)70    assert r["prix_marginal_m2"] == pytest.approx(r["elasticite_S"] * r["prix_moyen_m2"], rel=1e-12)71    assert r["gain_10m2"] == pytest.approx(20491, abs=1) and r["gain_10m2_suivants"] == pytest.approx(19958, abs=1)72    assert r["gain_10m2_suivants"] < r["gain_10m2"]73    assert r["gain_10m2"] + r["gain_10m2_suivants"] == pytest.approx(p["mediane"] * ((185 / 165) ** r["elasticite_S"] - 1), rel=1e-9)7475def test_anticipation(t):76    a = t["anticipation"]77    assert a["croissance_mensuelle"] == pytest.approx(0.003642, abs=0.00001)78    assert a["croissance_annuelle"] == pytest.approx(0.0446, abs=0.0002)79    assert a["croissance_annuelle"] == pytest.approx((1 + a["croissance_mensuelle"]) ** 12 - 1, rel=1e-12)80    assert a["mois_depuis_reference"] == pytest.approx(26.0, abs=0.1)81    assert a["facteur_cumule"] == pytest.approx(1.0992, abs=0.0002)82    assert a["valeur_a_la_reference"] == pytest.approx(578436, abs=1)83    assert a["valeur_role"] == 4680008485def test_conformite(t):86    c = t["conformite"]87    assert c.loc["Aylmer", "effet_pct"] == pytest.approx(0.0892, abs=0.0002) and c.loc["Aylmer", "valeur"] == pytest.approx(692518, abs=1)88    assert c.loc["Masson-Angers", "effet_pct"] == pytest.approx(-0.2314, abs=0.0002) and c.loc["Masson-Angers", "valeur"] == pytest.approx(488712, abs=1)89    assert c.loc["Buckingham", "valeur"] == pytest.approx(494999, abs=1) and c.loc["Gatineau", "valeur"] == pytest.approx(621798, abs=1)90    assert c.loc["Hull", "valeur"] == pytest.approx(t["pred"]["mediane"])9192def test_comparables_ch16(t):93    c = t["comparables"].set_index("id_vente")94    assert list(c.index) == ["G0574", "G0404", "G0468", "G0477", "G0575"]95    assert c.loc["G0574", "ecart_pct"] == pytest.approx(0.007, abs=0.001)96    assert c.loc["G0404", "ecart_pct"] == pytest.approx(-0.149, abs=0.001)97    assert c.loc["G0468", "ecart_pct"] == pytest.approx(-0.099, abs=0.001)98    assert c.loc["G0477", "ecart_pct"] == pytest.approx(-0.105, abs=0.001)99    assert c.loc["G0575", "ecart_pct"] == pytest.approx(-0.080, abs=0.001)100    assert c.loc["G0404", "valeur_estimee"] == pytest.approx(595030, abs=1)101    assert (c["ecart_pct"] == pytest.approx(np.exp(c["residu_log"].values) - 1, rel=1e-9)) if "residu_log" in c else True102103# ---------------------------------------------------------------------------- recherche104105def test_recherche(t):106    s = t["recherche"]; m = t["pred"]["mediane"]; P = c1.PARAMS_RECHERCHE107    assert s["prix_reserve"] / m == pytest.approx(1.0267, abs=0.0005)108    assert s["prix_moyen"] / m == pytest.approx(1.068, abs=0.003)109    assert s["cv_prix"] == pytest.approx(0.0317, abs=0.001)110    assert s["delai_moyen"] == pytest.approx(5.08, abs=0.1) and s["delai_theorique"] == pytest.approx(5.05, abs=0.02)111    assert s["delai_moyen"] == pytest.approx(s["delai_theorique"], rel=0.03)112    assert s["prix"].min() >= s["prix_reserve"]                      # aucun prix accepté sous le prix de réserve113    # point fixe : W = −c + δ [p E max(O, W) + (1 − p) W]114    r = s["prix_reserve"]; delta = 1 / (1 + P["taux_annuel"] / 12); c = P["c_mois"] * m115    z = np.linspace(-6, 6, 20001); w = norm.pdf(z); w /= w.sum(); O = m * np.exp(P["sigma_o"] * z)116    EmaxO = float(np.sum(w * np.maximum(O, r)))117    assert r == pytest.approx(-c + delta * (P["p_offre"] * EmaxO + (1 - P["p_offre"]) * r), rel=1e-4)118    tab = t["recherche_tableau"]119    assert tab.loc[0.002, "prix_reserve_sur_V"] == pytest.approx(1.0377, abs=0.0005) and tab.loc[0.002, "delai_moyen_mois"] == pytest.approx(6.22, abs=0.15)120    assert tab.loc[0.015, "prix_reserve_sur_V"] == pytest.approx(0.9869, abs=0.0005) and tab.loc[0.015, "delai_moyen_mois"] == pytest.approx(2.88, abs=0.1)121    assert tab["prix_reserve_sur_V"].is_monotonic_decreasing and tab["delai_moyen_mois"].is_monotonic_decreasing and tab["prix_moyen_sur_V"].is_monotonic_decreasing122123def test_phmu(t):124    p = t["phmu"]125    assert list(p["valeur_residuelle_terrain"]) == [170_000, 160_000, 140_000]126    assert p["valeur_residuelle_terrain"].idxmax() == "Unifamiliale (usage actuel)"127128def test_sensibilite_sigma(t):129    s = t["sensibilite"]130    assert s.loc[0.04, "ip95_bas_pct"] == pytest.approx(-0.0754, abs=0.0003) and s.loc[0.04, "ip95_haut_pct"] == pytest.approx(0.0816, abs=0.0003)131    assert s.loc[0.083, "ip95_bas_pct"] == pytest.approx(-0.150, abs=0.001) and s.loc[0.083, "ip95_haut_pct"] == pytest.approx(0.177, abs=0.001)132    assert s.loc[0.16, "ip95_haut_pct"] == pytest.approx(0.368, abs=0.001) and s.loc[0.16, "proba_ecart_10pct"] == pytest.approx(0.551, abs=0.002)133    assert s.loc[0.04, "proba_ecart_10pct"] == pytest.approx(0.0172, abs=0.0005)134    assert s.loc[0.083, "moyenne_sur_mediane"] == pytest.approx(0.0035, abs=0.0001) and s.loc[0.16, "moyenne_sur_mediane"] == pytest.approx(0.0129, abs=0.0001)135    assert list(s["n_pour_se_2pct"]) == [16, 36, 68, 142, 251]136137# ---------------------------------------------------------------------------- solutions des exercices138139def test_solutions_exercices():140    P = 635821.26141    # ex. 1.2 : contribution d'un ajout = P [exp(β) − 1]142    assert P * (math.exp(0.02633) - 1) == pytest.approx(16964, abs=2)143    assert P * (math.exp(0.03243) - 1) == pytest.approx(20958, abs=2)144    # ex. 1.3 : prix moyen, marginal, agrandissement de 25 m²145    b = 0.53907146    assert P / 165 == pytest.approx(3853, abs=1) and b * P / 165 == pytest.approx(2077, abs=1)147    assert P * ((190 / 165) ** b - 1) == pytest.approx(50241, abs=2)148    assert P * ((190 / 165) ** b - 1) / 25 == pytest.approx(2010, abs=1)149    # ex. 1.4 : log-normale σ = 0,12, médiane 500 000150    s, med = 0.12, 500_000151    assert med * math.exp(s ** 2 / 2) == pytest.approx(503613, abs=1) and med * math.exp(-s ** 2) == pytest.approx(492852, abs=1)152    assert med * math.exp(-1.96 * s) == pytest.approx(395206, abs=1) and med * math.exp(1.96 * s) == pytest.approx(632581, abs=1)153    assert c1.proba_ecart(s, 0.10) == pytest.approx(0.427, abs=0.001)154    # ex. 1.5 : Gordon et coût d'usage155    assert 24_000 / (0.06 - 0.02) == pytest.approx(600_000)156    assert 600_000 * (0.045 + 0.01 + 0.015 - 0.02) == pytest.approx(30_000) and 600_000 * (0.06 + 0.01 + 0.015 - 0.02) == pytest.approx(39_000)157    # ex. 1.6 : prix de réserve avec offres uniformes sur [0,9 ; 1,1], c = 0,005, δ = 1158    a, bb = 0.9, 1.1159    def fixe(c):160        return brentq(lambda r: -c + r * (r - a) / (bb - a) + (bb ** 2 - r ** 2) / (2 * (bb - a)) - r, a, bb)161    r1 = fixe(0.005); assert r1 == pytest.approx(1.0553, abs=0.0001)162    assert (bb - a) / (bb - r1) == pytest.approx(4.47, abs=0.01) and (r1 + bb) / 2 == pytest.approx(1.0776, abs=0.0001)163    r2 = fixe(0.02); assert r2 == pytest.approx(1.0106, abs=0.0001) and (bb - a) / (bb - r2) == pytest.approx(2.24, abs=0.01)164    assert r1 == pytest.approx(1.1 - math.sqrt(0.4 * 0.005), abs=1e-9)            # forme fermée : r = b − sqrt(2c(b−a))165    # ex. 1.7 : ancrage, λ par MCO sans constante166    x = np.array([40, -30, 25, 60, -20, 15.0]); y = np.array([18, -12, 9, 27, -10, 6.0])167    lam = float(x @ y / (x @ x)); assert lam == pytest.approx(0.437, abs=0.001)168    assert lam * 50 == pytest.approx(21.9, abs=0.05)169    assert c1.estimation_ancree(600_000, 650_000, lam) == pytest.approx(600_000 + lam * 50_000)170
Files · 8cb0de6f5c8171b14f35a0fe50fcc68c0097664d
Menu

Theme