|
| 1 | +import numpy as np |
| 2 | +import pandas as pd |
| 3 | + |
| 4 | +from policyengine_uk_data.datasets.local_areas.constituencies.devolved_housing import ( |
| 5 | + _PRIVATE_RENT_TARGETS, |
| 6 | + add_private_rent_targets, |
| 7 | +) |
| 8 | +from policyengine_uk_data.datasets.local_areas.constituencies import ( |
| 9 | + loss as constituency_loss, |
| 10 | +) |
| 11 | + |
| 12 | + |
| 13 | +def _age_targets(): |
| 14 | + return pd.DataFrame( |
| 15 | + { |
| 16 | + "code": ["W07000041", "W07000042", "S14000001", "S14000002"], |
| 17 | + "name": ["W1", "W2", "S1", "S2"], |
| 18 | + "age/0_10": [100, 300, 200, 200], |
| 19 | + "age/10_20": [100, 300, 200, 200], |
| 20 | + } |
| 21 | + ) |
| 22 | + |
| 23 | + |
| 24 | +def test_add_private_rent_targets_filters_matrix_to_country_private_renters(): |
| 25 | + matrix = pd.DataFrame() |
| 26 | + y = pd.DataFrame() |
| 27 | + |
| 28 | + add_private_rent_targets( |
| 29 | + matrix, |
| 30 | + y, |
| 31 | + _age_targets(), |
| 32 | + country=np.array(["WALES", "WALES", "SCOTLAND", "ENGLAND"]), |
| 33 | + tenure_type=np.array( |
| 34 | + ["RENT_PRIVATELY", "OWNED_OUTRIGHT", "RENT_PRIVATELY", "RENT_PRIVATELY"] |
| 35 | + ), |
| 36 | + rent=np.array([9_600.0, 0.0, 12_000.0, 15_000.0]), |
| 37 | + ) |
| 38 | + |
| 39 | + np.testing.assert_array_equal( |
| 40 | + matrix["housing/wales_private_renter_households"].values, |
| 41 | + np.array([1.0, 0.0, 0.0, 0.0]), |
| 42 | + ) |
| 43 | + np.testing.assert_array_equal( |
| 44 | + matrix["housing/scotland_private_renter_households"].values, |
| 45 | + np.array([0.0, 0.0, 1.0, 0.0]), |
| 46 | + ) |
| 47 | + np.testing.assert_array_equal( |
| 48 | + matrix["housing/wales_private_rent_amount"].values, |
| 49 | + np.array([9_600.0, 0.0, 0.0, 0.0]), |
| 50 | + ) |
| 51 | + np.testing.assert_array_equal( |
| 52 | + matrix["housing/scotland_private_rent_amount"].values, |
| 53 | + np.array([0.0, 0.0, 12_000.0, 0.0]), |
| 54 | + ) |
| 55 | + |
| 56 | + |
| 57 | +def test_add_private_rent_targets_allocate_country_totals_by_population_share(): |
| 58 | + matrix = pd.DataFrame() |
| 59 | + y = pd.DataFrame() |
| 60 | + |
| 61 | + add_private_rent_targets( |
| 62 | + matrix, |
| 63 | + y, |
| 64 | + _age_targets(), |
| 65 | + country=np.array(["WALES", "SCOTLAND"]), |
| 66 | + tenure_type=np.array(["RENT_PRIVATELY", "RENT_PRIVATELY"]), |
| 67 | + rent=np.array([9_600.0, 12_000.0]), |
| 68 | + ) |
| 69 | + |
| 70 | + wales_shares = np.array([0.25, 0.75, 0.0, 0.0]) |
| 71 | + scotland_shares = np.array([0.0, 0.0, 0.5, 0.5]) |
| 72 | + |
| 73 | + np.testing.assert_allclose( |
| 74 | + y["housing/wales_private_renter_households"].values, |
| 75 | + wales_shares * _PRIVATE_RENT_TARGETS["WALES"]["private_renter_households"], |
| 76 | + ) |
| 77 | + np.testing.assert_allclose( |
| 78 | + y["housing/scotland_private_renter_households"].values, |
| 79 | + scotland_shares |
| 80 | + * _PRIVATE_RENT_TARGETS["SCOTLAND"]["private_renter_households"], |
| 81 | + ) |
| 82 | + np.testing.assert_allclose( |
| 83 | + y["housing/wales_private_rent_amount"].values.sum(), |
| 84 | + _PRIVATE_RENT_TARGETS["WALES"]["private_renter_households"] |
| 85 | + * _PRIVATE_RENT_TARGETS["WALES"]["annual_private_rent"], |
| 86 | + ) |
| 87 | + np.testing.assert_allclose( |
| 88 | + y["housing/scotland_private_rent_amount"].values.sum(), |
| 89 | + _PRIVATE_RENT_TARGETS["SCOTLAND"]["private_renter_households"] |
| 90 | + * _PRIVATE_RENT_TARGETS["SCOTLAND"]["annual_private_rent"], |
| 91 | + ) |
| 92 | + |
| 93 | + |
| 94 | +class _FakeDataset: |
| 95 | + time_period = 2025 |
| 96 | + |
| 97 | + |
| 98 | +class _FakeSim: |
| 99 | + def __init__(self, *args, **kwargs): |
| 100 | + self.default_calculation_period = 2025 |
| 101 | + |
| 102 | + def calculate(self, variable): |
| 103 | + mapping = { |
| 104 | + "self_employment_income": np.array([0.0, 0.0]), |
| 105 | + "employment_income": np.array([0.0, 0.0]), |
| 106 | + "income_tax": np.array([1.0, 1.0]), |
| 107 | + "age": np.array([35, 35]), |
| 108 | + "universal_credit": np.array([1.0, 1.0]), |
| 109 | + "is_child": np.array([0.0, 0.0]), |
| 110 | + "country": np.array(["WALES", "SCOTLAND"]), |
| 111 | + "tenure_type": np.array(["RENT_PRIVATELY", "RENT_PRIVATELY"]), |
| 112 | + "rent": np.array([9_600.0, 12_000.0]), |
| 113 | + } |
| 114 | + return type("Result", (), {"values": mapping[variable]})() |
| 115 | + |
| 116 | + def map_result(self, values, source_entity, target_entity): |
| 117 | + return np.asarray(values) |
| 118 | + |
| 119 | + |
| 120 | +def test_constituency_target_matrix_includes_devolved_housing_targets(monkeypatch): |
| 121 | + age_targets = _age_targets().iloc[[0, 2]].reset_index(drop=True) |
| 122 | + income_targets = pd.DataFrame( |
| 123 | + { |
| 124 | + "self_employment_income_amount": [1.0, 1.0], |
| 125 | + "self_employment_income_count": [1.0, 1.0], |
| 126 | + "employment_income_amount": [1.0, 1.0], |
| 127 | + "employment_income_count": [1.0, 1.0], |
| 128 | + } |
| 129 | + ) |
| 130 | + national_income = pd.DataFrame( |
| 131 | + { |
| 132 | + "total_income_lower_bound": [12_570], |
| 133 | + "total_income_upper_bound": [np.inf], |
| 134 | + "self_employment_income_amount": [1.0], |
| 135 | + "employment_income_amount": [1.0], |
| 136 | + } |
| 137 | + ) |
| 138 | + uc_by_children = pd.DataFrame( |
| 139 | + { |
| 140 | + "uc_hh_0_children": [1.0, 1.0], |
| 141 | + "uc_hh_1_child": [0.0, 0.0], |
| 142 | + "uc_hh_2_children": [0.0, 0.0], |
| 143 | + "uc_hh_3plus_children": [0.0, 0.0], |
| 144 | + } |
| 145 | + ) |
| 146 | + |
| 147 | + monkeypatch.setattr(constituency_loss, "Microsimulation", _FakeSim) |
| 148 | + monkeypatch.setattr( |
| 149 | + constituency_loss, "get_constituency_income_targets", lambda: income_targets |
| 150 | + ) |
| 151 | + monkeypatch.setattr( |
| 152 | + constituency_loss, |
| 153 | + "get_national_income_projections", |
| 154 | + lambda year: national_income, |
| 155 | + ) |
| 156 | + monkeypatch.setattr( |
| 157 | + constituency_loss, "get_constituency_age_targets", lambda: age_targets |
| 158 | + ) |
| 159 | + monkeypatch.setattr(constituency_loss, "get_uk_total_population", lambda year: 2.0) |
| 160 | + monkeypatch.setattr( |
| 161 | + constituency_loss, |
| 162 | + "get_constituency_uc_targets", |
| 163 | + lambda: pd.Series([1.0, 1.0]), |
| 164 | + ) |
| 165 | + monkeypatch.setattr( |
| 166 | + constituency_loss, |
| 167 | + "get_constituency_uc_by_children_targets", |
| 168 | + lambda: uc_by_children, |
| 169 | + ) |
| 170 | + monkeypatch.setattr(constituency_loss, "mapping_matrix", np.eye(2)) |
| 171 | + monkeypatch.setattr( |
| 172 | + constituency_loss.pd, |
| 173 | + "read_csv", |
| 174 | + lambda path: pd.DataFrame({"code": ["W07000041", "S14000001"]}), |
| 175 | + ) |
| 176 | + |
| 177 | + matrix, y, country_mask = constituency_loss.create_constituency_target_matrix( |
| 178 | + _FakeDataset() |
| 179 | + ) |
| 180 | + |
| 181 | + assert "housing/wales_private_renter_households" in matrix.columns |
| 182 | + assert "housing/scotland_private_rent_amount" in matrix.columns |
| 183 | + np.testing.assert_allclose( |
| 184 | + y["housing/wales_private_renter_households"].values, |
| 185 | + np.array([_PRIVATE_RENT_TARGETS["WALES"]["private_renter_households"], 0.0]), |
| 186 | + ) |
| 187 | + np.testing.assert_allclose( |
| 188 | + y["housing/scotland_private_renter_households"].values, |
| 189 | + np.array([0.0, _PRIVATE_RENT_TARGETS["SCOTLAND"]["private_renter_households"]]), |
| 190 | + ) |
| 191 | + np.testing.assert_array_equal( |
| 192 | + country_mask, |
| 193 | + np.array([[1.0, 0.0], [0.0, 1.0]]), |
| 194 | + ) |
0 commit comments