policyengine 3.1.1__tar.gz → 3.1.3__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (89) hide show
  1. policyengine-3.1.3/.claude/policyengine-guide.md +568 -0
  2. policyengine-3.1.3/.claude/quick-reference.md +367 -0
  3. {policyengine-3.1.1 → policyengine-3.1.3}/CHANGELOG.md +14 -0
  4. {policyengine-3.1.1 → policyengine-3.1.3}/PKG-INFO +1 -1
  5. {policyengine-3.1.1 → policyengine-3.1.3}/changelog.yaml +10 -0
  6. {policyengine-3.1.1 → policyengine-3.1.3}/docs/core-concepts.md +113 -0
  7. {policyengine-3.1.1 → policyengine-3.1.3}/docs/country-models-uk.md +36 -0
  8. {policyengine-3.1.1 → policyengine-3.1.3}/docs/country-models-us.md +32 -0
  9. {policyengine-3.1.1 → policyengine-3.1.3}/examples/employment_income_variation_uk.py +0 -32
  10. {policyengine-3.1.1 → policyengine-3.1.3}/examples/employment_income_variation_us.py +0 -46
  11. {policyengine-3.1.1 → policyengine-3.1.3}/pyproject.toml +1 -1
  12. policyengine-3.1.3/src/policyengine/__pycache__/__init__.cpython-313.pyc +0 -0
  13. {policyengine-3.1.1 → policyengine-3.1.3}/src/policyengine/core/__init__.py +1 -0
  14. {policyengine-3.1.1 → policyengine-3.1.3}/src/policyengine/core/dataset.py +163 -8
  15. {policyengine-3.1.1 → policyengine-3.1.3}/src/policyengine/core/dynamic.py +4 -1
  16. {policyengine-3.1.1 → policyengine-3.1.3}/src/policyengine/core/parameter.py +1 -0
  17. {policyengine-3.1.1 → policyengine-3.1.3}/src/policyengine/core/policy.py +4 -1
  18. {policyengine-3.1.1 → policyengine-3.1.3}/src/policyengine/core/simulation.py +8 -5
  19. policyengine-3.1.3/src/policyengine/core/tax_benefit_model_version.py +82 -0
  20. {policyengine-3.1.1 → policyengine-3.1.3}/src/policyengine/tax_benefit_models/uk/datasets.py +7 -27
  21. {policyengine-3.1.1 → policyengine-3.1.3}/src/policyengine/tax_benefit_models/uk/model.py +86 -73
  22. {policyengine-3.1.1 → policyengine-3.1.3}/src/policyengine/tax_benefit_models/us/datasets.py +7 -27
  23. {policyengine-3.1.1 → policyengine-3.1.3}/src/policyengine/tax_benefit_models/us/model.py +71 -59
  24. {policyengine-3.1.1 → policyengine-3.1.3}/src/policyengine/utils/plotting.py +0 -1
  25. {policyengine-3.1.1 → policyengine-3.1.3}/src/policyengine.egg-info/PKG-INFO +1 -1
  26. {policyengine-3.1.1 → policyengine-3.1.3}/src/policyengine.egg-info/SOURCES.txt +3 -0
  27. policyengine-3.1.3/tests/test_get_parameter_variable.py +141 -0
  28. {policyengine-3.1.1 → policyengine-3.1.3}/tests/test_us_simulation.py +0 -13
  29. policyengine-3.1.1/src/policyengine/__pycache__/__init__.cpython-313.pyc +0 -0
  30. policyengine-3.1.1/src/policyengine/core/tax_benefit_model_version.py +0 -34
  31. {policyengine-3.1.1 → policyengine-3.1.3}/.github/CONTRIBUTING.md +0 -0
  32. {policyengine-3.1.1 → policyengine-3.1.3}/.github/changelog_template.md +0 -0
  33. {policyengine-3.1.1 → policyengine-3.1.3}/.github/fetch_version.py +0 -0
  34. {policyengine-3.1.1 → policyengine-3.1.3}/.github/get-changelog-diff.sh +0 -0
  35. {policyengine-3.1.1 → policyengine-3.1.3}/.github/has-functional-changes.sh +0 -0
  36. {policyengine-3.1.1 → policyengine-3.1.3}/.github/is-version-number-acceptable.sh +0 -0
  37. {policyengine-3.1.1 → policyengine-3.1.3}/.github/publish-git-tag.sh +0 -0
  38. {policyengine-3.1.1 → policyengine-3.1.3}/.github/workflows/code_changes.yaml +0 -0
  39. {policyengine-3.1.1 → policyengine-3.1.3}/.github/workflows/docs.yml +0 -0
  40. {policyengine-3.1.1 → policyengine-3.1.3}/.github/workflows/pr_code_changes.yaml +0 -0
  41. {policyengine-3.1.1 → policyengine-3.1.3}/.github/workflows/pr_docs_changes.yaml +0 -0
  42. {policyengine-3.1.1 → policyengine-3.1.3}/.github/workflows/versioning.yaml +0 -0
  43. {policyengine-3.1.1 → policyengine-3.1.3}/.gitignore +0 -0
  44. {policyengine-3.1.1 → policyengine-3.1.3}/CLAUDE.md +0 -0
  45. {policyengine-3.1.1 → policyengine-3.1.3}/LICENSE +0 -0
  46. {policyengine-3.1.1 → policyengine-3.1.3}/Makefile +0 -0
  47. {policyengine-3.1.1 → policyengine-3.1.3}/README.md +0 -0
  48. {policyengine-3.1.1 → policyengine-3.1.3}/changelog_entry.yaml +0 -0
  49. {policyengine-3.1.1 → policyengine-3.1.3}/docs/.gitignore +0 -0
  50. {policyengine-3.1.1 → policyengine-3.1.3}/docs/dev.md +0 -0
  51. {policyengine-3.1.1 → policyengine-3.1.3}/docs/index.md +0 -0
  52. {policyengine-3.1.1 → policyengine-3.1.3}/docs/myst.yml +0 -0
  53. {policyengine-3.1.1 → policyengine-3.1.3}/docs/visualisation.md +0 -0
  54. {policyengine-3.1.1 → policyengine-3.1.3}/examples/income_bands_uk.py +0 -0
  55. {policyengine-3.1.1 → policyengine-3.1.3}/examples/income_distribution_us.py +0 -0
  56. {policyengine-3.1.1 → policyengine-3.1.3}/examples/policy_change_uk.py +0 -0
  57. {policyengine-3.1.1 → policyengine-3.1.3}/examples/speedtest_us_simulation.py +0 -0
  58. {policyengine-3.1.1 → policyengine-3.1.3}/setup.cfg +0 -0
  59. {policyengine-3.1.1 → policyengine-3.1.3}/src/policyengine/__init__.py +0 -0
  60. {policyengine-3.1.1 → policyengine-3.1.3}/src/policyengine/core/dataset_version.py +0 -0
  61. {policyengine-3.1.1 → policyengine-3.1.3}/src/policyengine/core/output.py +0 -0
  62. {policyengine-3.1.1 → policyengine-3.1.3}/src/policyengine/core/parameter_value.py +0 -0
  63. {policyengine-3.1.1 → policyengine-3.1.3}/src/policyengine/core/tax_benefit_model.py +0 -0
  64. {policyengine-3.1.1 → policyengine-3.1.3}/src/policyengine/core/variable.py +0 -0
  65. {policyengine-3.1.1 → policyengine-3.1.3}/src/policyengine/outputs/__init__.py +0 -0
  66. {policyengine-3.1.1 → policyengine-3.1.3}/src/policyengine/outputs/aggregate.py +0 -0
  67. {policyengine-3.1.1 → policyengine-3.1.3}/src/policyengine/outputs/change_aggregate.py +0 -0
  68. {policyengine-3.1.1 → policyengine-3.1.3}/src/policyengine/outputs/decile_impact.py +0 -0
  69. {policyengine-3.1.1 → policyengine-3.1.3}/src/policyengine/tax_benefit_models/uk/__init__.py +0 -0
  70. {policyengine-3.1.1 → policyengine-3.1.3}/src/policyengine/tax_benefit_models/uk/analysis.py +0 -0
  71. {policyengine-3.1.1 → policyengine-3.1.3}/src/policyengine/tax_benefit_models/uk/outputs.py +0 -0
  72. {policyengine-3.1.1 → policyengine-3.1.3}/src/policyengine/tax_benefit_models/uk.py +0 -0
  73. {policyengine-3.1.1 → policyengine-3.1.3}/src/policyengine/tax_benefit_models/us/__init__.py +0 -0
  74. {policyengine-3.1.1 → policyengine-3.1.3}/src/policyengine/tax_benefit_models/us/analysis.py +0 -0
  75. {policyengine-3.1.1 → policyengine-3.1.3}/src/policyengine/tax_benefit_models/us/outputs.py +0 -0
  76. {policyengine-3.1.1 → policyengine-3.1.3}/src/policyengine/tax_benefit_models/us.py +0 -0
  77. {policyengine-3.1.1 → policyengine-3.1.3}/src/policyengine/utils/__init__.py +0 -0
  78. {policyengine-3.1.1 → policyengine-3.1.3}/src/policyengine/utils/dates.py +0 -0
  79. {policyengine-3.1.1 → policyengine-3.1.3}/src/policyengine/utils/parametric_reforms.py +0 -0
  80. {policyengine-3.1.1 → policyengine-3.1.3}/src/policyengine.egg-info/dependency_links.txt +0 -0
  81. {policyengine-3.1.1 → policyengine-3.1.3}/src/policyengine.egg-info/requires.txt +0 -0
  82. {policyengine-3.1.1 → policyengine-3.1.3}/src/policyengine.egg-info/top_level.txt +0 -0
  83. {policyengine-3.1.1 → policyengine-3.1.3}/tests/test_aggregate.py +0 -0
  84. {policyengine-3.1.1 → policyengine-3.1.3}/tests/test_change_aggregate.py +0 -0
  85. {policyengine-3.1.1 → policyengine-3.1.3}/tests/test_entity_mapping.py +0 -0
  86. {policyengine-3.1.1 → policyengine-3.1.3}/tests/test_uk_dataset.py +0 -0
  87. {policyengine-3.1.1 → policyengine-3.1.3}/tests/test_us_datasets.py +0 -0
  88. {policyengine-3.1.1 → policyengine-3.1.3}/tests/test_us_entity_mapping.py +0 -0
  89. {policyengine-3.1.1 → policyengine-3.1.3}/uv.lock +0 -0
@@ -0,0 +1,568 @@
1
+ # PolicyEngine.py - Claude Guide
2
+
3
+ This guide helps you use the policyengine.py package to perform tax-benefit microsimulation analysis.
4
+
5
+ ## Core workflow
6
+
7
+ 1. **Create or load a dataset** with microdata (person, household, etc.)
8
+ 2. **Run a simulation** applying tax-benefit rules to the dataset
9
+ 3. **Extract results** using output classes (Aggregate, ChangeAggregate)
10
+ 4. **Visualise** using built-in plotting utilities
11
+
12
+ ## Package structure
13
+
14
+ ```
15
+ policyengine
16
+ ├── core/
17
+ │ ├── Dataset, YearData # Data containers
18
+ │ ├── Simulation # Runs tax-benefit calculations
19
+ │ ├── Policy, Parameter # Define reforms
20
+ │ └── map_to_entity() # Entity mapping utility
21
+ ├── outputs/
22
+ │ ├── Aggregate # Calculate statistics
23
+ │ └── ChangeAggregate # Analyse reforms
24
+ ├── tax_benefit_models/
25
+ │ ├── uk/ # UK-specific models
26
+ │ └── us/ # US-specific models
27
+ └── utils/
28
+ └── plotting # Visualisation tools
29
+ ```
30
+
31
+ ## Quick start patterns
32
+
33
+ ### Pattern 1: Synthetic scenario analysis
34
+
35
+ Use when: User wants to analyse specific household scenarios
36
+
37
+ ```python
38
+ import pandas as pd
39
+ from microdf import MicroDataFrame
40
+ from policyengine.tax_benefit_models.uk import (
41
+ PolicyEngineUKDataset,
42
+ UKYearData,
43
+ uk_latest
44
+ )
45
+ from policyengine.core import Simulation
46
+
47
+ # Create synthetic person data
48
+ person_df = MicroDataFrame(
49
+ pd.DataFrame({
50
+ "person_id": [0, 1, 2],
51
+ "person_household_id": [0, 0, 1],
52
+ "person_benunit_id": [0, 0, 1],
53
+ "age": [35, 8, 40],
54
+ "employment_income": [30000, 0, 50000],
55
+ "person_weight": [1.0, 1.0, 1.0],
56
+ }),
57
+ weights="person_weight"
58
+ )
59
+
60
+ # Create household data
61
+ household_df = MicroDataFrame(
62
+ pd.DataFrame({
63
+ "household_id": [0, 1],
64
+ "region": ["LONDON", "SOUTH_EAST"],
65
+ "rent": [15000, 12000],
66
+ "household_weight": [1.0, 1.0],
67
+ }),
68
+ weights="household_weight"
69
+ )
70
+
71
+ # Create benunit data (UK only)
72
+ benunit_df = MicroDataFrame(
73
+ pd.DataFrame({
74
+ "benunit_id": [0, 1],
75
+ "would_claim_uc": [True, True],
76
+ "benunit_weight": [1.0, 1.0],
77
+ }),
78
+ weights="benunit_weight"
79
+ )
80
+
81
+ # Package into dataset
82
+ dataset = PolicyEngineUKDataset(
83
+ name="Custom scenario",
84
+ description="Analysis scenario",
85
+ filepath="./custom.h5",
86
+ year=2026,
87
+ data=UKYearData(
88
+ person=person_df,
89
+ household=household_df,
90
+ benunit=benunit_df,
91
+ )
92
+ )
93
+
94
+ # Run simulation
95
+ simulation = Simulation(
96
+ dataset=dataset,
97
+ tax_benefit_model_version=uk_latest,
98
+ )
99
+ simulation.run()
100
+
101
+ # Access results
102
+ output = simulation.output_dataset.data
103
+ print(output.household[["household_id", "household_net_income"]])
104
+ ```
105
+
106
+ ### Pattern 2: US synthetic scenario
107
+
108
+ ```python
109
+ from policyengine.tax_benefit_models.us import (
110
+ PolicyEngineUSDataset,
111
+ USYearData,
112
+ us_latest
113
+ )
114
+
115
+ # Create person data (note: US has more entity types)
116
+ person_df = MicroDataFrame(
117
+ pd.DataFrame({
118
+ "person_id": [0, 1, 2, 3],
119
+ "person_household_id": [0, 0, 0, 0],
120
+ "person_tax_unit_id": [0, 0, 0, 0],
121
+ "person_spm_unit_id": [0, 0, 0, 0],
122
+ "person_family_id": [0, 0, 0, 0],
123
+ "person_marital_unit_id": [0, 0, 1, 2],
124
+ "age": [35, 33, 8, 5],
125
+ "employment_income": [60000, 40000, 0, 0],
126
+ "person_weight": [1.0, 1.0, 1.0, 1.0],
127
+ }),
128
+ weights="person_weight"
129
+ )
130
+
131
+ # Create entity dataframes (tax_unit, spm_unit, family, marital_unit, household)
132
+ # ... (see examples/employment_income_variation_us.py for full pattern)
133
+
134
+ dataset = PolicyEngineUSDataset(
135
+ name="US scenario",
136
+ year=2024,
137
+ filepath="./us_scenario.h5",
138
+ data=USYearData(
139
+ person=person_df,
140
+ tax_unit=tax_unit_df,
141
+ spm_unit=spm_unit_df,
142
+ family=family_df,
143
+ marital_unit=marital_unit_df,
144
+ household=household_df,
145
+ )
146
+ )
147
+ ```
148
+
149
+ ### Pattern 3: Parameter sweep analysis
150
+
151
+ Use when: User wants to vary one parameter across many values
152
+
153
+ ```python
154
+ import numpy as np
155
+
156
+ # Create N scenarios with varying parameter
157
+ n_scenarios = 43
158
+ income_values = np.linspace(0, 100000, n_scenarios)
159
+
160
+ # Create person data with all scenarios
161
+ person_df = MicroDataFrame(
162
+ pd.DataFrame({
163
+ "person_id": range(n_scenarios),
164
+ "person_household_id": range(n_scenarios),
165
+ "person_benunit_id": range(n_scenarios),
166
+ "age": [35] * n_scenarios,
167
+ "employment_income": income_values,
168
+ "person_weight": [1.0] * n_scenarios,
169
+ }),
170
+ weights="person_weight"
171
+ )
172
+
173
+ # Create matching household/benunit data
174
+ household_df = MicroDataFrame(
175
+ pd.DataFrame({
176
+ "household_id": range(n_scenarios),
177
+ "region": ["LONDON"] * n_scenarios,
178
+ "rent": [15000] * n_scenarios,
179
+ "household_weight": [1.0] * n_scenarios,
180
+ }),
181
+ weights="household_weight"
182
+ )
183
+
184
+ # ... create dataset and run simulation once for all scenarios
185
+ ```
186
+
187
+ ### Pattern 4: Policy reform analysis
188
+
189
+ Use when: User wants to compare baseline vs reform
190
+
191
+ ```python
192
+ from policyengine.core import Policy, Parameter, ParameterValue
193
+ import datetime
194
+
195
+ # Define reform
196
+ parameter = Parameter(
197
+ name="gov.hmrc.income_tax.allowances.personal_allowance.amount",
198
+ tax_benefit_model_version=uk_latest,
199
+ description="Personal allowance",
200
+ data_type=float,
201
+ )
202
+
203
+ policy = Policy(
204
+ name="Increase personal allowance",
205
+ description="Raises PA to £15,000",
206
+ parameter_values=[
207
+ ParameterValue(
208
+ parameter=parameter,
209
+ start_date=datetime.date(2026, 1, 1),
210
+ end_date=datetime.date(2026, 12, 31),
211
+ value=15000,
212
+ )
213
+ ],
214
+ )
215
+
216
+ # Run baseline
217
+ baseline_sim = Simulation(
218
+ dataset=dataset,
219
+ tax_benefit_model_version=uk_latest,
220
+ )
221
+ baseline_sim.run()
222
+
223
+ # Run reform
224
+ reform_sim = Simulation(
225
+ dataset=dataset,
226
+ tax_benefit_model_version=uk_latest,
227
+ policy=policy,
228
+ )
229
+ reform_sim.run()
230
+
231
+ # Analyse impact
232
+ from policyengine.outputs.change_aggregate import (
233
+ ChangeAggregate,
234
+ ChangeAggregateType
235
+ )
236
+
237
+ winners = ChangeAggregate(
238
+ baseline_simulation=baseline_sim,
239
+ reform_simulation=reform_sim,
240
+ variable="household_net_income",
241
+ aggregate_type=ChangeAggregateType.COUNT,
242
+ change_geq=1, # Gain at least £1
243
+ )
244
+ winners.run()
245
+ print(f"Winners: {winners.result:,.0f}")
246
+ ```
247
+
248
+ ### Pattern 5: Extract aggregates
249
+
250
+ Use when: User wants statistics from simulation results
251
+
252
+ ```python
253
+ from policyengine.outputs.aggregate import Aggregate, AggregateType
254
+
255
+ # Total spending on a benefit
256
+ total_uc = Aggregate(
257
+ simulation=simulation,
258
+ variable="universal_credit",
259
+ entity="benunit",
260
+ aggregate_type=AggregateType.SUM,
261
+ )
262
+ total_uc.run()
263
+ print(f"Total UC: £{total_uc.result / 1e9:.1f}bn")
264
+
265
+ # Mean income in top decile
266
+ top_decile_income = Aggregate(
267
+ simulation=simulation,
268
+ variable="household_net_income",
269
+ entity="household",
270
+ aggregate_type=AggregateType.MEAN,
271
+ filter_variable="household_net_income",
272
+ quantile=10,
273
+ quantile_eq=10, # 10th decile only
274
+ )
275
+ top_decile_income.run()
276
+ print(f"Top decile mean income: £{top_decile_income.result:,.0f}")
277
+
278
+ # Count households below poverty line
279
+ poverty_count = Aggregate(
280
+ simulation=simulation,
281
+ variable="household_id",
282
+ entity="household",
283
+ aggregate_type=AggregateType.COUNT,
284
+ filter_variable="in_absolute_poverty_bhc",
285
+ filter_eq=True,
286
+ )
287
+ poverty_count.run()
288
+ print(f"Households in poverty: {poverty_count.result:,.0f}")
289
+ ```
290
+
291
+ ### Pattern 6: Entity mapping
292
+
293
+ Use when: User needs to map data between entity levels
294
+
295
+ ```python
296
+ # Map person income to household level (sum)
297
+ household_income = dataset.data.map_to_entity(
298
+ source_entity="person",
299
+ target_entity="household",
300
+ columns=["employment_income"],
301
+ how="sum"
302
+ )
303
+
304
+ # Map household rent to person level (broadcast)
305
+ person_rent = dataset.data.map_to_entity(
306
+ source_entity="household",
307
+ target_entity="person",
308
+ columns=["rent"],
309
+ how="project"
310
+ )
311
+
312
+ # Split household savings equally per person
313
+ person_savings_share = dataset.data.map_to_entity(
314
+ source_entity="household",
315
+ target_entity="person",
316
+ columns=["total_savings"],
317
+ how="divide"
318
+ )
319
+
320
+ # Map custom values
321
+ import numpy as np
322
+ custom_values = np.array([100, 200, 150])
323
+ household_totals = dataset.data.map_to_entity(
324
+ source_entity="person",
325
+ target_entity="household",
326
+ values=custom_values,
327
+ how="sum"
328
+ )
329
+ ```
330
+
331
+ ### Pattern 7: Visualisation
332
+
333
+ ```python
334
+ from policyengine.utils.plotting import format_fig, COLORS
335
+ import plotly.graph_objects as go
336
+
337
+ fig = go.Figure()
338
+ fig.add_trace(go.Scatter(
339
+ x=income_values,
340
+ y=net_income_values,
341
+ mode='lines',
342
+ name='Net income',
343
+ line=dict(color=COLORS["primary"], width=3)
344
+ ))
345
+
346
+ format_fig(
347
+ fig,
348
+ title="Net income by employment income",
349
+ xaxis_title="Employment income (£)",
350
+ yaxis_title="Net income (£)",
351
+ height=600,
352
+ width=1000,
353
+ )
354
+ fig.show()
355
+ ```
356
+
357
+ ## Entity structures
358
+
359
+ ### UK entities
360
+ ```
361
+ household
362
+ └── benunit (benefit unit - family claiming benefits together)
363
+ └── person
364
+ ```
365
+
366
+ ### US entities
367
+ ```
368
+ household
369
+ ├── tax_unit (federal tax filing unit)
370
+ ├── spm_unit (Supplemental Poverty Measure unit)
371
+ ├── family (Census definition)
372
+ └── marital_unit (married couple or single)
373
+ └── person
374
+ ```
375
+
376
+ ## Key concepts
377
+
378
+ ### 1. MicroDataFrame
379
+ All entity data uses `MicroDataFrame` which automatically handles survey weights:
380
+ ```python
381
+ df = MicroDataFrame(pd_dataframe, weights="weight_column_name")
382
+ df.sum() # Automatically weighted
383
+ ```
384
+
385
+ ### 2. Entity mapping
386
+ When variables are at different entity levels, automatic mapping occurs:
387
+ - **Person → Group**: Sum values within each group
388
+ - **Group → Person**: Replicate group value to all members
389
+
390
+ ### 3. Required fields
391
+
392
+ **UK person:**
393
+ - `person_id`, `person_household_id`, `person_benunit_id`
394
+ - `age`, `employment_income`
395
+ - `person_weight`
396
+
397
+ **UK household:**
398
+ - `household_id`
399
+ - `region` (e.g., "LONDON", "SOUTH_EAST")
400
+ - `rent` (annual)
401
+ - `household_weight`
402
+
403
+ **UK benunit:**
404
+ - `benunit_id`
405
+ - `would_claim_uc` (boolean - CRITICAL for UC calculations)
406
+ - `benunit_weight`
407
+
408
+ **US person:**
409
+ - `person_id`, `person_household_id`, `person_tax_unit_id`, `person_spm_unit_id`, `person_family_id`, `person_marital_unit_id`
410
+ - `age`, `employment_income`
411
+ - `person_weight`
412
+
413
+ **US household:**
414
+ - `household_id`
415
+ - `state_code` (e.g., "CA", "NY")
416
+ - `household_weight`
417
+
418
+ ### 4. Common pitfalls
419
+
420
+ **Always set would_claim variables:**
421
+ ```python
422
+ "would_claim_uc": [True] * n_benunits # UK
423
+ ```
424
+
425
+ **Set disability variables to avoid spikes:**
426
+ ```python
427
+ "is_disabled_for_benefits": [False] * n_people
428
+ "uc_limited_capability_for_WRA": [False] * n_people
429
+ ```
430
+
431
+ **Use consistent ID linkages:**
432
+ ```python
433
+ # Person 0 must link to valid household_id and benunit_id
434
+ person_df["person_household_id"] = [0, 0, 1] # Persons 0,1 in household 0
435
+ ```
436
+
437
+ ## Finding parameters
438
+
439
+ ### UK common parameters
440
+ ```
441
+ gov.hmrc.income_tax.allowances.personal_allowance.amount
442
+ gov.hmrc.national_insurance.class_1.rates.main
443
+ gov.dwp.universal_credit.means_test.reduction_rate
444
+ gov.dwp.universal_credit.elements.child.first_child
445
+ gov.dwp.child_benefit.amount.first_child
446
+ ```
447
+
448
+ ### US common parameters
449
+ ```
450
+ gov.irs.income.standard_deduction.single
451
+ gov.irs.income.standard_deduction.joint
452
+ gov.irs.credits.ctc.amount.base
453
+ gov.irs.credits.ctc.refundable.amount.max
454
+ gov.irs.credits.eitc.max[0] # 0 children
455
+ gov.usda.snap.normal_allotment.max[1] # 1 person
456
+ ```
457
+
458
+ ## Aggregation methods for entity mapping
459
+
460
+ - `how='sum'`: Aggregate by summing (person → group default)
461
+ - `how='first'`: Take first value in group
462
+ - `how='project'`: Broadcast group value to members (group → person default)
463
+ - `how='divide'`: Split equally among members
464
+
465
+ ## Response patterns
466
+
467
+ When user asks to:
468
+
469
+ 1. **"Analyse a family with £X income"** → Use Pattern 1 (synthetic scenario)
470
+ 2. **"How does income vary from £0 to £100k"** → Use Pattern 3 (parameter sweep)
471
+ 3. **"What if we increased personal allowance?"** → Use Pattern 4 (policy reform)
472
+ 4. **"How many people benefit?"** → Use Pattern 5 (extract aggregates)
473
+ 5. **"Compare US vs UK"** → Create both datasets, run separately
474
+ 6. **"Show me the phase-out"** → Use Pattern 3 + Pattern 7 (sweep + visualise)
475
+
476
+ ## Debugging tips
477
+
478
+ 1. **Check dataset shape**: `len(dataset.data.person)` should match expectations
479
+ 2. **Verify linkages**: All person IDs should map to valid household IDs
480
+ 3. **Check weights**: `dataset.data.household["household_weight"].sum()`
481
+ 4. **Inspect output columns**: `list(simulation.output_dataset.data.person.columns)`
482
+ 5. **Test small first**: Use 3-5 scenarios before scaling to 100+
483
+
484
+ ## Example responses
485
+
486
+ **User: "What's the net income of a single person earning £30k in London?"**
487
+
488
+ ```python
489
+ # I'll create a synthetic dataset with one person earning £30k in London
490
+ # and run a UK simulation to calculate their net income.
491
+
492
+ import pandas as pd
493
+ from microdf import MicroDataFrame
494
+ from policyengine.tax_benefit_models.uk import (
495
+ PolicyEngineUKDataset, UKYearData, uk_latest
496
+ )
497
+ from policyengine.core import Simulation
498
+
499
+ # Create person data
500
+ person_df = MicroDataFrame(
501
+ pd.DataFrame({
502
+ "person_id": [0],
503
+ "person_household_id": [0],
504
+ "person_benunit_id": [0],
505
+ "age": [30],
506
+ "employment_income": [30000],
507
+ "person_weight": [1.0],
508
+ }),
509
+ weights="person_weight"
510
+ )
511
+
512
+ # Create household data
513
+ household_df = MicroDataFrame(
514
+ pd.DataFrame({
515
+ "household_id": [0],
516
+ "region": ["LONDON"],
517
+ "rent": [12000], # Typical London rent
518
+ "household_weight": [1.0],
519
+ }),
520
+ weights="household_weight"
521
+ )
522
+
523
+ # Create benunit data
524
+ benunit_df = MicroDataFrame(
525
+ pd.DataFrame({
526
+ "benunit_id": [0],
527
+ "would_claim_uc": [True],
528
+ "benunit_weight": [1.0],
529
+ }),
530
+ weights="benunit_weight"
531
+ )
532
+
533
+ # Create and run simulation
534
+ dataset = PolicyEngineUKDataset(
535
+ name="Single person £30k",
536
+ filepath="./temp_scenario.h5",
537
+ year=2026,
538
+ data=UKYearData(
539
+ person=person_df,
540
+ household=household_df,
541
+ benunit=benunit_df,
542
+ )
543
+ )
544
+
545
+ simulation = Simulation(
546
+ dataset=dataset,
547
+ tax_benefit_model_version=uk_latest,
548
+ )
549
+ simulation.run()
550
+
551
+ # Extract results
552
+ output = simulation.output_dataset.data
553
+ net_income = output.household["household_net_income"].iloc[0]
554
+ income_tax = output.person["income_tax"].iloc[0]
555
+ ni = output.person["national_insurance"].iloc[0]
556
+
557
+ print(f"Employment income: £30,000")
558
+ print(f"Income tax: £{income_tax:,.0f}")
559
+ print(f"National Insurance: £{ni:,.0f}")
560
+ print(f"Net income: £{net_income:,.0f}")
561
+ ```
562
+
563
+ ## Additional resources
564
+
565
+ - Full examples in `examples/` directory
566
+ - Core concepts: `docs/core-concepts.md`
567
+ - UK model: `docs/country-models-uk.md`
568
+ - US model: `docs/country-models-us.md`