@@ -131,6 +131,55 @@ def weighted_median(values: np.ndarray, weights: np.ndarray) -> float:
131131 cutoff = weights .sum () / 2.0
132132 return float (values [np .searchsorted (np .cumsum (weights ), cutoff , side = "left" )])
133133
134+ def refresh_household_hbai_columns (hh : "pd.DataFrame" ) -> "pd.DataFrame" :
135+ """Recompute derived HBAI household columns from primitive inputs.
136+
137+ Post-hooks often mutate household net income directly. Re-deriving the
138+ AHC and equivalised fields here keeps poverty metrics consistent with
139+ those changes, rather than trusting stale columns emitted before the
140+ hook ran.
141+ """
142+ hh = hh .copy ()
143+
144+ if "housing_costs_ahc_annual" in hh .columns :
145+ housing_costs = hh ["housing_costs_ahc_annual" ].fillna (0.0 )
146+ elif {"baseline_net_income" , "baseline_net_income_ahc" }.issubset (hh .columns ):
147+ housing_costs = (
148+ hh ["baseline_net_income" ].fillna (0.0 )
149+ - hh ["baseline_net_income_ahc" ].fillna (0.0 )
150+ )
151+ else :
152+ housing_costs = 0.0
153+
154+ for prefix in ("baseline" , "reform" ):
155+ net_col = f"{ prefix } _net_income"
156+ net_ahc_col = f"{ prefix } _net_income_ahc"
157+ eq_factor_col = f"{ prefix } _equivalisation_factor"
158+ eq_col = f"{ prefix } _equivalised_net_income"
159+ eq_factor_ahc_col = f"{ prefix } _equivalisation_factor_ahc"
160+ eq_ahc_col = f"{ prefix } _equivalised_net_income_ahc"
161+
162+ if net_col not in hh .columns :
163+ continue
164+
165+ hh [net_ahc_col ] = hh [net_col ].fillna (0.0 ) - housing_costs
166+
167+ if eq_factor_col in hh .columns :
168+ eq_factor = hh [eq_factor_col ].fillna (1.0 ).clip (lower = 1e-9 )
169+ hh [eq_col ] = hh [net_col ].fillna (0.0 ) / eq_factor
170+ elif eq_col not in hh .columns :
171+ hh [eq_col ] = hh [net_col ].fillna (0.0 )
172+
173+ if eq_factor_ahc_col in hh .columns :
174+ eq_factor_ahc = hh [eq_factor_ahc_col ].fillna (1.0 ).clip (lower = 1e-9 )
175+ hh [eq_ahc_col ] = hh [net_ahc_col ].fillna (0.0 ) / eq_factor_ahc
176+ elif eq_ahc_col not in hh .columns :
177+ hh [eq_ahc_col ] = hh [net_ahc_col ].fillna (0.0 )
178+
179+ return hh
180+
181+ households = refresh_household_hbai_columns (households )
182+
134183 person_counts = persons .groupby ("household_id" ).size () if "household_id" in persons .columns else pd .Series (dtype = float )
135184
136185 def hbai_for (prefix : str ) -> HbaiIncomes :
0 commit comments