SPB Git
12.6 KB · 141 lines python
1"""Tests du chapitre 33 (immeubles de six logements et plus) : identités, cohérence avec traite.cas3/cas4 et le ch. 47,2lecture des tableaux SCHL (données réelles), valeurs citées dans le texte. Tolérances déclarées : montants ±1 $,3taux ±1e-5 (±0,1 pb), pourcentages ±1e-4."""4import sys, pathlib5import numpy as np, pytest6sys.path.insert(0, str(pathlib.Path(__file__).resolve().parents[1] / "cases"))7from traite import cas3, cas4, finance as fin8import ch33_six_logements as ch910@pytest.fixture(scope="module")11def R():12    return ch.calculer()1314# ----------------------------------------------------------------------------- SCHL (réel)1516def test_schl_lecture_coherente_avec_notes(R):17    t = R["schl"]["taille"].set_index("categorie"); a = R["schl"]["annee"].set_index("categorie")18    # valeurs consignées dans sources/notes-E.md (ch. 27 et 47) : 6-19 unités île : 2,2 % / 1 177 $ (2 ch.) / 1 162 $ (tous)19    assert t.loc["6 à 19 unités", "inoccupation_tous"] == pytest.approx(2.2) and t.loc["6 à 19 unités", "loyer_2ch"] == 1177 and t.loc["6 à 19 unités", "loyer_tous"] == 116220    assert t.loc["20 à 49 unités", "inoccupation_tous"] == pytest.approx(3.9) and t.loc["50 à 99 unités", "inoccupation_tous"] == pytest.approx(3.8)21    assert t.loc["100 unités et +", "inoccupation_tous"] == pytest.approx(5.1) and t.loc["100 unités et +", "loyer_2ch"] == 196622    assert t.loc["3 à 5 unités", "loyer_2ch"] == 1350 and t.loc["Tous les immeubles", "inoccupation_tous"] == pytest.approx(3.1)23    assert a.loc["Avant 1940", "loyer_tous"] == 1238 and a.loc["1960 à 1974", "loyer_tous"] == 1186 and a.loc["2015 ou après", "loyer_tous"] == 188224    assert a.loc["2015 ou après", "inoccupation_tous"] == pytest.approx(5.6)25    assert a.loc["Avant 1940", "rotation_tous"] == pytest.approx(9.3) and a.loc["2015 ou après", "rotation_tous"] == pytest.approx(13.7) and np.isnan(a.loc["1990 à 2004", "rotation_tous"])26    assert t.loc["6 à 19 unités", "rotation_tous"] == pytest.approx(8.5) and t.loc["100 unités et +", "rotation_tous"] == pytest.approx(12.4)27    assert list(t.index) == ["3 à 5 unités", "6 à 19 unités", "20 à 49 unités", "50 à 99 unités", "100 unités et +", "Tous les immeubles"]2829# ----------------------------------------------------------------------------- cas 4 : sommaire et identités3031def test_sommaire_cas4_identites(R):32    g = R["sommaire"]["par_rmr"]; t = R["sommaire"]["total"]; pf = R["sommaire"]["portefeuille"]33    assert t["n"] == 48 and t["logements"] == 888 and g["n"].sum() == 48 and g["logements"].sum() == 88834    assert g["valeur"].sum() == pytest.approx(t["valeur"]) and t["valeur"] == pytest.approx(143_363_000)35    for r in g.index:36        assert g.loc[r, "taux_pondere"] == pytest.approx(g.loc[r, "rne"] / g.loc[r, "valeur"])37        assert g.loc[r, "valeur_par_logement"] == pytest.approx(g.loc[r, "valeur"] / g.loc[r, "logements"])38    assert t["taux_pondere"] == pytest.approx(0.051945, abs=1e-5) and t["valeur"] / t["logements"] == pytest.approx(161_445, abs=1)39    assert g.loc["Montréal", "valeur_par_logement"] == pytest.approx(206_237, abs=1) and g.loc["Gatineau", "valeur_par_logement"] == pytest.approx(184_173, abs=1)40    assert g.loc["Québec", "valeur_par_logement"] == pytest.approx(125_455, abs=1) and g.loc["Saguenay", "valeur_par_logement"] == pytest.approx(101_969, abs=1)41    assert g.loc["Montréal", "taux_pondere"] == pytest.approx(0.044948, abs=1e-5) and g.loc["Gatineau", "taux_pondere"] == pytest.approx(0.054359, abs=1e-5)42    assert g.loc["Trois-Rivières", "taux_pondere"] == pytest.approx(0.062883, abs=1e-5) and g.loc["Saguenay", "taux_median"] == pytest.approx(0.0646, abs=1e-5)43    # cohérence avec le module partagé44    s = cas4.sommaire(pf)45    assert s["total"]["taux_pondere"] == pytest.approx(t["taux_pondere"]) and (s["par_rmr"]["valeur_par_logement"].sort_index() == g["valeur_par_logement"].sort_index()).all()4647# ----------------------------------------------------------------------------- effet de taille4849def test_effet_taille(R):50    et = R["effet_taille"]51    assert et["cas3_n"] == 40 and et["cas3_mediane"] == pytest.approx(0.04415, abs=1e-6)52    assert et["cas3_pente_pb_par_logement"] == pytest.approx(-2.27, abs=0.01) and et["d_pb_12_36_cas3"] == pytest.approx(-54.6, abs=0.1)53    assert et["cas4_coef_ln"] == pytest.approx(-0.03153, abs=1e-4) and et["cas4_se_ln"] == pytest.approx(0.01222, abs=1e-4)54    assert et["cas4_effet_doublement_pct"] == pytest.approx(np.exp(et["cas4_coef_ln"] * np.log(2)) - 1) and et["cas4_effet_doublement_pct"] == pytest.approx(-0.0216, abs=1e-4)55    assert et["d_pb_12_36_cas4"] == pytest.approx(-15.7, abs=0.1)56    # mêmes coefficients que traite.cas4.effets_regionaux57    e = cas4.effets_regionaux(R["sommaire"]["portefeuille"])58    assert e.loc["ln_logements", "coef"] == pytest.approx(et["cas4_coef_ln"]) and e.loc["rmr_Gatineau", "effet_pct"] == pytest.approx(0.1962, abs=1e-4)59    assert e.loc["rmr_Trois-Rivières", "effet_pct"] == pytest.approx(0.4017, abs=1e-4) and e.loc["etat", "effet_pct"] == pytest.approx(-0.0396, abs=1e-4)6061# ----------------------------------------------------------------------------- unités (cas 3)6263def test_unites_cas3(R):64    u = R["unites"]; b = cas3.tout()["base"]65    assert u["n"] == 12 and u["m2"] == 825 and u["chambres"] == 2266    assert u["par_logement"] == pytest.approx(2_060_000 / 12) and u["par_m2"] == pytest.approx(2_060_000 / 825) and u["par_chambre"] == pytest.approx(2_060_000 / 22)67    assert u["par_logement"] == pytest.approx(171_667, abs=1) and u["par_m2"] == pytest.approx(2_497, abs=1) and u["par_chambre"] == pytest.approx(93_636, abs=1)68    assert u["mrb"] == pytest.approx(2_060_000 / b["rbp_contrat"]) and u["mrb"] == pytest.approx(12.44, abs=0.01) and u["mrb_marche"] == pytest.approx(11.07, abs=0.01)69    assert u["taux_implicite"] == pytest.approx(b["rne"] / 2_060_000) and u["taux_implicite"] == pytest.approx(0.04367, abs=1e-5)70    assert u["valeur_cap"] == pytest.approx(2_037_502.89, abs=0.01) and u["rne_par_logement"] == pytest.approx(7_496.31, abs=0.01)71    assert u["loyer_moyen_m2"] == pytest.approx(16.72, abs=0.01)7273def test_valeur_finale_ch47_coherente():74    """La valeur finale citée (2 060 000 $) est celle de la réconciliation du chapitre 47."""75    import ch47_dossier as c4776    D = c47.dossier(n_mc=200, graine=c47.GRAINE)77    assert D["reconciliation"]["valeur_finale"] == ch.V_FINALE_CH477879# ----------------------------------------------------------------------------- P458081def test_p45_identites_et_valeurs(R):82    p = R["p45"]83    assert p["id"] == "P45" and p["n"] == 36 and p["rmr"] == "Gatineau" and p["etat"] == 2 and p["annee"] == 195484    assert p["rne"] == pytest.approx(366_800) and p["rne_par_logement"] == pytest.approx(10_188.89, abs=0.01)85    for k, R_ in p["taux"].items():86        assert p["valeurs"][k] == pytest.approx(fin.capitalisation_directe(p["rne"], R_)) and p["par_logement"][k] == pytest.approx(p["valeurs"][k] / 36)87    assert p["taux"]["montreal_cas3_mediane"] == pytest.approx(0.04415, abs=1e-6) and p["valeurs"]["montreal_cas3_mediane"] == pytest.approx(8_308_041, abs=1)88    assert p["taux"]["gatineau_pondere"] == pytest.approx(0.054359, abs=1e-5) and p["valeurs"]["gatineau_pondere"] == pytest.approx(6_747_739, abs=1)89    assert p["taux"]["gatineau_median"] == pytest.approx(0.0571, abs=1e-6) and p["valeurs"]["gatineau_median"] == pytest.approx(6_423_818, abs=1)90    assert p["taux"]["regression_sujet"] == pytest.approx(0.054611, abs=1e-5) and p["valeurs"]["regression_sujet"] == pytest.approx(6_716_534, abs=1)91    assert p["taux"]["regression_si_montreal"] == pytest.approx(0.045654, abs=1e-5) and p["valeurs"]["regression_si_montreal"] == pytest.approx(8_034_343, abs=1)92    assert p["taux"]["regression_si_12_logements"] == pytest.approx(0.056536, abs=1e-5) and p["valeurs"]["regression_si_12_logements"] == pytest.approx(6_487_875, abs=1)93    assert p["taux"]["regression_si_etat_3"] == pytest.approx(0.052452, abs=1e-5) and p["valeurs"]["regression_si_etat_3"] == pytest.approx(6_993_820, abs=1)94    assert p["valeur_fichier"] == 7_186_000 and p["valeur_fichier_par_logement"] == pytest.approx(199_611, abs=1)95    assert p["ecart_montreal_vs_sujet"] == pytest.approx(0.237, abs=1e-3)96    d = p["decomposition"]97    assert d["effet_taille_pb"] == pytest.approx(-16.1, abs=0.1) and d["effet_rmr_pb"] == pytest.approx(89.6, abs=0.1)98    # décomposition multiplicative exacte : R_sujet = R_mtl12 × (1 + effet taille) × (1 + effet RMR)99    r12 = d["regression_montreal_12_log_etat2_age72"]100    assert r12 + d["effet_taille_pb"] / 1e4 + d["effet_rmr_pb"] / 1e4 == pytest.approx(p["taux"]["regression_sujet"], abs=1e-9)101    reg = p["regression"]; assert reg["n"] == 48 and reg["r2"] == pytest.approx(0.883, abs=1e-3) and reg["duan"] == pytest.approx(1.0011, abs=1e-4)102    f = p["financement"]103    assert f["service_dette"] == pytest.approx(fin.Hypotheque(5_416_000, 0.0517, 480).service_dette_annuel) and f["rcd"] == pytest.approx(366_800 / f["service_dette"])104    assert f["rcd"] == pytest.approx(1.15, abs=0.01) and f["rpv_fichier"] == pytest.approx(0.7537, abs=1e-4)105106# ----------------------------------------------------------------------------- sensibilité107108def test_sensibilite(R):109    s = R["sensibilite"]; p = R["p45"]110    G, T = s["grille_valeur_par_logement"], s["grille_taux"]111    assert G.shape == (5, 6) and (G.values > 0).all()112    assert G.loc["état 2", "Gatineau"] == pytest.approx(p["par_logement"]["regression_sujet"]) and T.loc["état 2", "Gatineau"] == pytest.approx(p["taux"]["regression_sujet"])113    assert (np.diff(T.values, axis=0) < 0).all()            # un meilleur état abaisse le taux114    assert (T["Montréal"] < T[["Québec", "Gatineau", "Sherbrooke", "Trois-Rivières", "Saguenay"]].min(axis=1)).all()   # Montréal toujours le plus bas115    c = s["courbe_taille"]; assert (np.diff(c["taux_gatineau"]) < 0).all() and c.loc[c["n"] == 36, "taux_gatineau"].iloc[0] == pytest.approx(p["taux"]["regression_sujet"])116    pb = s["taux_pb"].set_index("delta_pb")117    assert pb.loc[0, "valeur"] == pytest.approx(p["valeurs"]["regression_sujet"]) and pb.loc[25, "variation_pct"] == pytest.approx(s["variation_25pb_pct"])118    assert s["variation_25pb_pct"] == pytest.approx(-0.0438, abs=1e-4) and pb.loc[-25, "variation_pct"] == pytest.approx(0.0480, abs=1e-4)119    assert s["valeur_un_pb"] == pytest.approx(12_299, abs=1) and s["pb_par_million_p45"] == pytest.approx(1_831, abs=1) and s["pb_par_million_cas3"] == pytest.approx(2_265, abs=1)120    # élasticité dV/V = -dR/R en petite variation121    R0 = p["taux"]["regression_sujet"]; assert (pb.loc[25, "valeur"] - pb.loc[-25, "valeur"]) / (50e-4) / pb.loc[0, "valeur"] == pytest.approx(-1 / R0, rel=0.01)122123# ----------------------------------------------------------------------------- exercices124125def test_exercices(R):126    ex = R["exercices"]127    assert ex["ex1"]["par_logement"] == pytest.approx(2_746_667, abs=1) and ex["ex1"]["par_m2"] == pytest.approx(2_596_848, abs=1) and ex["ex1"]["par_chambre"] == pytest.approx(2_434_545, abs=1)128    assert ex["ex1"]["ecart_max_pct"] == pytest.approx(0.1282, abs=1e-4)129    assert ex["ex2"]["taxe_5"] == pytest.approx(16_340.20, abs=0.01) and ex["ex2"]["taxe_6"] == pytest.approx(17_340.60, abs=0.01)130    assert ex["ex2"]["ecart_taux_seul"] == pytest.approx(419.40, abs=0.01) and ex["ex2"]["ecart_total"] == pytest.approx(1_000.40, abs=0.01) and ex["ex2"]["effet_valeur_a_5pct"] == pytest.approx(7_625.45, abs=0.01)131    assert ex["ex3"]["R36"] == pytest.approx(ex["ex3"]["R12"] - 54.592e-4, abs=1e-7) and ex["ex3"]["prime_pct"] == pytest.approx(0.1358, abs=1e-4)132    assert ex["ex3"]["valeur_par_logement_12"] == pytest.approx(164_141, abs=1) and ex["ex3"]["valeur_par_logement_36"] == pytest.approx(186_426, abs=1)133    assert ex["ex4"]["ecart"] == pytest.approx(1_591_507, abs=1) and ex["ex4"]["ecart_pct"] == pytest.approx(0.237, abs=1e-3)134    assert ex["ex5"]["delta_rbp"] == 5_760 and ex["ex5"]["delta_rne"] == pytest.approx(5_319.48, abs=0.01) and ex["ex5"]["delta_valeur"] == pytest.approx(97_406, abs=1) and ex["ex5"]["delta_valeur_pct"] == pytest.approx(0.0145, abs=1e-4)135    e6 = ex["ex6"]["par_rmr"]136    assert e6["Montréal"]["taux"] == pytest.approx(0.0452, abs=1e-4) and e6["Trois-Rivières"]["taux"] == pytest.approx(0.0634, abs=1e-4)137    assert e6["Gatineau"]["ic_lo"] < e6["Gatineau"]["effet_pct"] < e6["Gatineau"]["ic_hi"] and e6["Gatineau"]["effet_pct"] == pytest.approx(0.1962, abs=1e-4)138    e7 = ex["ex7"]139    assert e7["cout_2026"] == pytest.approx(6_988_800) and e7["cout_total"] == pytest.approx(8_088_800) and e7["rne"] == pytest.approx(365_124.10, abs=0.01)140    assert e7["V_450"] == pytest.approx(8_113_869, abs=1) and e7["V_475"] == pytest.approx(7_686_823, abs=1) and e7["taux_implicite_cout"] == pytest.approx(0.0451, abs=1e-4)141
Files · 774056809e670c3d870d087d88dfeb8a0dd87f8e
Menu

Theme