44loaded from pre-downloaded Stat-Xplore exports and scaled to match
55national UC payment distribution totals.
66
7+ Also provides UC household counts split by number of children, using
8+ country-level proportions from Stat-Xplore (November 2023) applied to
9+ each constituency's total. This ensures the reweighting algorithm
10+ places adequate weight on larger families in every constituency.
11+
712Source: DWP Stat-Xplore
813https://stat-xplore.dwp.gov.uk
914"""
1015
1116import logging
1217
18+ import numpy as np
1319import pandas as pd
1420
1521logger = logging .getLogger (__name__ )
1622
1723_REF = "https://stat-xplore.dwp.gov.uk"
1824
25+ # Country-level UC households by number of children (Nov 2023, Stat-Xplore).
26+ # Used to split each constituency's UC total into children-count buckets.
27+ # Keys: (0 children, 1 child, 2 children, 3+ children)
28+ _UC_CHILDREN_BY_COUNTRY = {
29+ "E" : np .array ([2_411_993 , 948_304 , 802_992 , 495_279 ], dtype = float ),
30+ "W" : np .array ([141_054 , 52_953 , 44_348 , 26_372 ], dtype = float ),
31+ "S" : np .array ([253_609 , 86_321 , 66_829 , 35_036 ], dtype = float ),
32+ # Northern Ireland: use GB-wide proportions as fallback
33+ "N" : np .array (
34+ [
35+ 2_411_993 + 141_054 + 253_609 ,
36+ 948_304 + 52_953 + 86_321 ,
37+ 802_992 + 44_348 + 66_829 ,
38+ 495_279 + 26_372 + 35_036 ,
39+ ],
40+ dtype = float ,
41+ ),
42+ }
43+
1944
2045def get_constituency_uc_targets () -> pd .Series :
2146 """UC household counts for 650 constituencies (positional order).
@@ -28,6 +53,44 @@ def get_constituency_uc_targets() -> pd.Series:
2853 return uc_pc_households .household_count
2954
3055
56+ def get_constituency_uc_by_children_targets () -> pd .DataFrame :
57+ """UC households split by 0, 1, 2, 3+ children for 650 constituencies.
58+
59+ Applies country-level proportions from Stat-Xplore to each
60+ constituency's total UC count. Returns a DataFrame with columns
61+ ``uc_hh_0_children``, ``uc_hh_1_child``, ``uc_hh_2_children``,
62+ ``uc_hh_3plus_children``, in the same positional order as
63+ :func:`get_constituency_uc_targets`.
64+ """
65+ from policyengine_uk_data .utils .uc_data import uc_pc_households
66+ from policyengine_uk_data .storage import STORAGE_FOLDER
67+
68+ codes = pd .read_csv (STORAGE_FOLDER / "constituencies_2024.csv" )["code" ]
69+ totals = uc_pc_households .household_count .values .astype (float )
70+
71+ result = pd .DataFrame (index = range (len (totals )))
72+ cols = [
73+ "uc_hh_0_children" ,
74+ "uc_hh_1_child" ,
75+ "uc_hh_2_children" ,
76+ "uc_hh_3plus_children" ,
77+ ]
78+ for col in cols :
79+ result [col ] = 0.0
80+
81+ for i , (total , code ) in enumerate (zip (totals , codes )):
82+ country_prefix = code [0 ]
83+ proportions = _UC_CHILDREN_BY_COUNTRY .get (
84+ country_prefix ,
85+ _UC_CHILDREN_BY_COUNTRY ["N" ], # fallback
86+ )
87+ shares = proportions / proportions .sum ()
88+ for j , col in enumerate (cols ):
89+ result .loc [i , col ] = round (total * shares [j ])
90+
91+ return result
92+
93+
3194def get_la_uc_targets () -> pd .Series :
3295 """UC household counts for 360 local authorities (positional order).
3396
0 commit comments