Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
36 changes: 36 additions & 0 deletions analysis/asec-2026-anchored-thresholds/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
# 2025 SPM poverty with thresholds anchored to 2024

Computed September 15, 2026, the day Census released *Poverty in the United States: 2025* (P60-290), from the 2026 CPS ASEC public-use person file (`asecpub26csv.zip`, `pppub26.csv`, posted at www2.census.gov/programs-surveys/cps/datasets/2026/march/).

## Replication

Person weight `MARSUPWT`. SPM poverty (`SPM_POOR`): 13.11 percent of all people, 13.39 percent of children, 12.28 percent of people 18 to 64, 15.38 percent of people 65 and over; 44,390 thousand people and 9,653 thousand children below the threshold. Published (Table 8): 13.1 / 13.4 / 12.3 / 15.4; 44,390 and 9,657 thousand. Official poverty (`PERLIS` = 1 within `POV_UNIV` = 1): 10.20 / 13.41 / 9.20 / 9.77 against 10.2 / 13.4 / 9.2 / 9.8.

## Anchoring

Each unit's `SPM_POVTHRESHOLD` is scaled by tenure so that the national two-adult, two-child base equals its 2024 value (BLS corrected series: owners with a mortgage 39,231; owners without 32,879; renters 39,220) times the CPI-U ratio Census used for the 2025 official thresholds (32,649 / 31,812 = 1.02631). Tenure codes were verified from the implied 2025 bases (`SPM_POVTHRESHOLD / (SPM_EQUIVSCALE × SPM_GEOADJ)`): 1 = 41,323, 2 = 34,326, 3 = 41,701. The equivalence scale and geographic adjustment stay at their published 2025 values. A person is poor when `SPM_RESOURCES` is below the anchored threshold.

| SPM poverty rate, percent | 2024 (re-based, P60-290) | 2025 published | 2025 at 2024 thresholds × CPI-U | 2025 at 2024 thresholds × C-CPI-U |
| --- | ---: | ---: | ---: | ---: |
| All people | 13.0 | 13.11 | 12.31 | 12.29 |
| Under 18 | 13.5 | 13.39 | 12.31 | 12.30 |
| 18 to 64 | 12.2 | 12.28 | 11.55 | 11.53 |
| 65 and over | 15.1 | 15.38 | 14.66 | 14.62 |

Threshold growth beyond CPI-U adds 0.80 points for all people, 1.08 for children, 0.73 for people 18 to 64 and 0.72 for people 65 and over. By tenure: renters 23.96 published against 22.13 anchored, owners without a mortgage 11.89 against 11.49, owners with a mortgage 6.00 against 5.67.

The chained variant uses C-CPI-U (`SUUR0000SA0`) annual means, 174.372 for 2024 and 178.697 for 2025 (eleven months; October 2025 was not published), a ratio of 1.02480; recent C-CPI-U values are interim.

## Files

- `replicate_and_anchor.py`: run in a directory containing `pppub26.csv`; optional argument is the price factor (default 32649/31812).
- `anchored_results.json`, `anchored_results_chained.json`: outputs.
- `bls_ccpi.json`: BLS API response for `SUUR0000SA0` and `CUUR0000SA0`, 2024 to 2025.

## By state and by detailed age

`state_age_anchored.py` extends the recomputation to the 51 states (from `hhpub26.csv` `GESTFIPS`, joined to the person file on `H_SEQ`) and to detailed age groups, with standard errors from the 160 ASEC replicate weights (`asec_csv_repwgt_2026.csv`; variance = 4/160 times the sum of squared replicate deviations; `PWWGT0` equals `MARSUPWT`/100). Outputs: `state_age_results.json`, `by_state.csv`. Run it with the scratch directory holding the three CSV files as the first argument and the output directory as the second.

## State tile maps

`state_tile_map.py` renders a state tile map (pass `--logo` with the PolicyEngine teal wordmark PNG from policyengine-app-v2, `app/public/assets/logos/policyengine/teal.png`, to watermark it) (11 by 8 grid, teal shading, the layout of the PolicyEngine slides' state map) from any CSV with a `state` column. The post uses it twice: `by_state.csv` for the 2025 threshold effect (default arguments) and `census_table17_state_3yr.csv` (P60-290 Table 17, SPM percent and margin of error, three-year average 2023 to 2025) with `--value spm_pct --se spm_moe` and the 9/12/15/18 percent bins. It needs Playwright with its Chromium (`uv run --with playwright --with pandas python state_tile_map.py ...` after `playwright install chromium`).
46 changes: 46 additions & 0 deletions analysis/asec-2026-anchored-thresholds/anchored_results.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
{
"cpi_factor": 1.02631,
"anchor_factors": {
"1": 0.9744,
"2": 0.983,
"3": 0.9653
},
"spm_published_replication": {
"all": 13.11,
"children": 13.39,
"18-64": 12.28,
"65+": 15.38
},
"official_replication": {
"all": 10.2,
"children": 13.41,
"18-64": 9.2,
"65+": 9.77
},
"spm_anchored_2024_thresholds": {
"all": 12.31,
"children": 12.31,
"18-64": 11.55,
"65+": 14.66
},
"by_tenure": {
"owner_with_mortgage": {
"published": 6.0,
"anchored": 5.67
},
"owner_without_mortgage": {
"published": 11.89,
"anchored": 11.49
},
"renter": {
"published": 23.96,
"anchored": 22.13
}
},
"threshold_effect_pp": {
"all": 0.8,
"children": 1.08,
"18-64": 0.73,
"65+": 0.72
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
{
"cpi_factor": 1.0248,
"anchor_factors": {
"1": 0.9729,
"2": 0.9816,
"3": 0.9638
},
"spm_published_replication": {
"all": 13.11,
"children": 13.39,
"18-64": 12.28,
"65+": 15.38
},
"official_replication": {
"all": 10.2,
"children": 13.41,
"18-64": 9.2,
"65+": 9.77
},
"spm_anchored_2024_thresholds": {
"all": 12.29,
"children": 12.3,
"18-64": 11.53,
"65+": 14.62
},
"by_tenure": {
"owner_with_mortgage": {
"published": 6.0,
"anchored": 5.66
},
"owner_without_mortgage": {
"published": 11.89,
"anchored": 11.48
},
"renter": {
"published": 23.96,
"anchored": 22.09
}
},
"threshold_effect_pp": {
"all": 0.82,
"children": 1.09,
"18-64": 0.75,
"65+": 0.76
}
}
5 changes: 5 additions & 0 deletions analysis/asec-2026-anchored-thresholds/bls_ccpi.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
{"status":"REQUEST_SUCCEEDED","responseTime":94,"message":[],"Results":{
"series":
[{"seriesID":"SUUR0000SA0","data":[{"year":"2025","period":"M12","periodName":"December","value":"179.775","footnotes":[{"code":"U","text":"Interim"}]},{"year":"2025","period":"M11","periodName":"November","value":"179.889","footnotes":[{"code":"U","text":"Interim"}]},{"year":"2025","period":"M09","periodName":"September","value":"180.373","footnotes":[{}]},{"year":"2025","period":"M08","periodName":"August","value":"179.839","footnotes":[{}]},{"year":"2025","period":"M07","periodName":"July","value":"179.272","footnotes":[{}]},{"year":"2025","period":"M06","periodName":"June","value":"178.991","footnotes":[{}]},{"year":"2025","period":"M05","periodName":"May","value":"178.427","footnotes":[{}]},{"year":"2025","period":"M04","periodName":"April","value":"178.081","footnotes":[{}]},{"year":"2025","period":"M03","periodName":"March","value":"177.520","footnotes":[{}]},{"year":"2025","period":"M02","periodName":"February","value":"177.134","footnotes":[{}]},{"year":"2025","period":"M01","periodName":"January","value":"176.362","footnotes":[{}]},{"year":"2024","period":"M12","periodName":"December","value":"175.219","footnotes":[{}]},{"year":"2024","period":"M11","periodName":"November","value":"175.180","footnotes":[{}]},{"year":"2024","period":"M10","periodName":"October","value":"175.346","footnotes":[{}]},{"year":"2024","period":"M09","periodName":"September","value":"175.099","footnotes":[{}]},{"year":"2024","period":"M08","periodName":"August","value":"174.848","footnotes":[{}]},{"year":"2024","period":"M07","periodName":"July","value":"174.792","footnotes":[{}]},{"year":"2024","period":"M06","periodName":"June","value":"174.721","footnotes":[{}]},{"year":"2024","period":"M05","periodName":"May","value":"174.685","footnotes":[{}]},{"year":"2024","period":"M04","periodName":"April","value":"174.424","footnotes":[{}]},{"year":"2024","period":"M03","periodName":"March","value":"173.796","footnotes":[{}]},{"year":"2024","period":"M02","periodName":"February","value":"172.700","footnotes":[{}]},{"year":"2024","period":"M01","periodName":"January","value":"171.649","footnotes":[{}]}]},
{"seriesID":"CUUR0000SA0","data":[{"year":"2025","period":"M13","periodName":"Annual","value":"321.943","footnotes":[{}]},{"year":"2025","period":"M12","periodName":"December","value":"324.054","footnotes":[{}]},{"year":"2025","period":"M11","periodName":"November","value":"324.122","footnotes":[{}]},{"year":"2025","period":"M10","periodName":"October","value":"-","footnotes":[{"code":"X","text":"Data unavailable due to the 2025 lapse in appropriations"}]},{"year":"2025","period":"M09","periodName":"September","value":"324.800","footnotes":[{}]},{"year":"2025","period":"M08","periodName":"August","value":"323.976","footnotes":[{}]},{"year":"2025","period":"M07","periodName":"July","value":"323.048","footnotes":[{}]},{"year":"2025","period":"M06","periodName":"June","value":"322.561","footnotes":[{}]},{"year":"2025","period":"M05","periodName":"May","value":"321.465","footnotes":[{}]},{"year":"2025","period":"M04","periodName":"April","value":"320.795","footnotes":[{}]},{"year":"2025","period":"M03","periodName":"March","value":"319.799","footnotes":[{}]},{"year":"2025","period":"M02","periodName":"February","value":"319.082","footnotes":[{}]},{"year":"2025","period":"M01","periodName":"January","value":"317.671","footnotes":[{}]},{"year":"2024","period":"M13","periodName":"Annual","value":"313.689","footnotes":[{}]},{"year":"2024","period":"M12","periodName":"December","value":"315.605","footnotes":[{}]},{"year":"2024","period":"M11","periodName":"November","value":"315.493","footnotes":[{}]},{"year":"2024","period":"M10","periodName":"October","value":"315.664","footnotes":[{}]},{"year":"2024","period":"M09","periodName":"September","value":"315.301","footnotes":[{}]},{"year":"2024","period":"M08","periodName":"August","value":"314.796","footnotes":[{}]},{"year":"2024","period":"M07","periodName":"July","value":"314.540","footnotes":[{}]},{"year":"2024","period":"M06","periodName":"June","value":"314.175","footnotes":[{}]},{"year":"2024","period":"M05","periodName":"May","value":"314.069","footnotes":[{}]},{"year":"2024","period":"M04","periodName":"April","value":"313.548","footnotes":[{}]},{"year":"2024","period":"M03","periodName":"March","value":"312.332","footnotes":[{}]},{"year":"2024","period":"M02","periodName":"February","value":"310.326","footnotes":[{}]},{"year":"2024","period":"M01","periodName":"January","value":"308.417","footnotes":[{}]}]}]
}}
52 changes: 52 additions & 0 deletions analysis/asec-2026-anchored-thresholds/by_state.csv
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
state,published,published_se,anchored,anchored_se,effect_pp,effect_se,n_persons,weighted_persons_thousands
AL,15.75,1.14,14.04,1.19,1.71,0.52,2334,5079.0
AK,10.37,1.22,10.02,1.18,0.35,0.34,1648,708.0
AZ,14.78,1.62,13.41,1.47,1.36,0.51,2279,7666.0
AR,12.67,1.42,11.87,1.4,0.8,0.33,2196,3073.0
CA,17.29,0.66,16.22,0.63,1.08,0.2,13025,38913.0
CO,11.36,2.17,10.9,2.26,0.46,0.29,1665,5950.0
CT,10.56,1.32,9.75,1.27,0.81,0.31,1376,3651.0
DE,7.51,1.15,6.14,0.98,1.37,0.72,1471,1066.0
DC,16.76,1.4,15.65,1.43,1.12,0.46,1919,691.0
FL,17.56,0.91,16.55,0.89,1.01,0.17,6342,23261.0
GA,13.85,1.11,13.11,1.04,0.74,0.27,2881,11164.0
HI,12.5,1.29,11.36,1.2,1.14,0.43,2427,1388.0
ID,9.47,0.9,8.89,0.85,0.58,0.3,2366,1995.0
IL,11.38,1.03,10.78,1.01,0.61,0.25,3297,12607.0
IN,11.78,1.29,10.4,1.28,1.38,0.67,2024,6893.0
IA,9.55,1.11,8.48,1.09,1.07,0.47,1634,3240.0
KS,7.53,1.12,7.45,1.11,0.08,0.08,1678,2909.0
KY,10.63,1.29,10.0,1.26,0.63,0.32,1461,4550.0
LA,17.85,1.01,16.72,0.92,1.13,0.37,2869,4545.0
ME,5.76,1.12,5.69,1.13,0.07,0.07,1061,1408.0
MD,10.51,1.46,9.96,1.38,0.55,0.32,1724,6188.0
MA,13.26,1.08,12.43,1.04,0.83,0.27,2732,7032.0
MI,10.86,1.04,10.42,1.03,0.44,0.22,2517,10006.0
MN,7.78,1.4,7.63,1.42,0.15,0.1,1694,5772.0
MS,18.26,1.77,17.56,1.73,0.7,0.27,2446,2914.0
MO,9.08,1.12,8.11,1.08,0.97,0.3,2010,6308.0
MT,9.68,1.69,8.21,1.3,1.47,0.59,2006,1123.0
NE,5.47,0.96,5.47,0.96,0.0,0.0,1885,2011.0
NV,15.7,1.52,14.49,1.51,1.21,0.43,2116,3335.0
NH,7.09,1.17,6.09,0.99,1.0,0.53,1608,1407.0
NJ,11.53,1.07,10.95,1.03,0.58,0.23,2868,9488.0
NM,14.95,1.36,14.25,1.22,0.7,0.46,3251,2103.0
NY,16.42,1.06,14.92,1.03,1.5,0.37,5036,19683.0
NC,11.69,1.12,11.23,1.05,0.46,0.22,3035,11050.0
ND,8.86,1.12,8.57,1.17,0.29,0.17,1980,780.0
OH,10.05,1.24,9.56,1.24,0.48,0.33,2952,11974.0
OK,11.96,1.35,11.36,1.32,0.6,0.33,2090,4070.0
OR,9.93,1.02,9.52,0.98,0.41,0.19,2241,4234.0
PA,9.83,0.9,9.53,0.89,0.31,0.15,3367,12893.0
RI,11.34,1.45,10.47,1.38,0.87,0.47,1196,1110.0
SC,11.07,1.29,10.27,1.22,0.8,0.34,2302,5592.0
SD,8.78,1.25,8.39,1.15,0.39,0.33,1455,913.0
TN,12.41,1.13,11.66,1.09,0.75,0.27,2368,7196.0
TX,15.27,0.74,14.25,0.71,1.02,0.22,8575,31542.0
UT,10.35,1.04,10.03,1.04,0.31,0.13,2469,3510.0
VT,9.08,1.14,8.76,1.11,0.32,0.2,1544,633.0
VA,10.85,1.19,10.6,1.17,0.24,0.12,2380,8726.0
WA,9.85,0.96,9.71,0.94,0.14,0.13,2752,7975.0
WV,12.23,1.35,11.15,1.19,1.08,0.4,2547,1737.0
WI,7.81,1.07,7.6,1.12,0.21,0.17,1754,5923.0
WY,9.5,1.08,9.07,1.07,0.43,0.27,1876,568.0
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
state,name,official_plus_pct,official_plus_moe,spm_pct,spm_moe
AL,Alabama,15.2,1.6,14.4,1.3
AK,Alaska,10,1.1,10.4,1.3
AZ,Arizona,10.7,1.1,13.7,1.2
AR,Arkansas,12.7,1.5,12.8,1.4
CA,California,10.7,0.5,17.8,0.6
CO,Colorado,8.6,1.5,10.8,1.5
CT,Connecticut,8.6,1.4,10.4,1.4
DE,Delaware,7.3,1.2,8.9,1.3
DC,District of Columbia,12.1,1.4,15.7,1.5
FL,Florida,11.3,0.7,16.4,0.9
GA,Georgia,12.3,1.1,14,1.2
HI,Hawaii,8.5,1.3,11.7,1.3
ID,Idaho,8.4,0.9,9.4,1
IL,Illinois,10.4,0.8,11.6,0.9
IN,Indiana,9.3,1.1,10.3,1.2
IA,Iowa,8.1,0.9,8.3,1.1
KS,Kansas,8.1,1.2,8.1,1
KY,Kentucky,13.7,1.6,13.2,1.6
LA,Louisiana,19.6,1.5,19,1.4
ME,Maine,7.5,1.1,6.4,1.1
MD,Maryland,7.2,1.1,10.9,1.4
MA,Massachusetts,9.1,1,12.5,1
MI,Michigan,11.5,0.9,10.6,1.2
MN,Minnesota,7.4,1,7.9,1
MS,Mississippi,17.6,1.2,16.8,1.4
MO,Missouri,9.3,1.4,9.4,1.3
MT,Montana,8.6,1.3,9.5,1.6
NE,Nebraska,7.3,1.2,7.5,1
NV,Nevada,11.8,1.2,15.3,1.5
NH,New Hampshire,5.4,0.9,8,1.3
NJ,New Jersey,8.9,1,12.1,1
NM,New Mexico,16,2.6,13.2,1.5
NY,New York,10.9,0.8,15.1,0.9
NC,North Carolina,13.1,1.3,13.5,1.2
ND,North Dakota,8.6,1.1,8.9,1.1
OH,Ohio,10.8,1,9.9,1
OK,Oklahoma,12.6,1.3,11.1,1.3
OR,Oregon,9.2,0.9,11,1
PA,Pennsylvania,9.3,0.9,10.7,1
RI,Rhode Island,9.4,1.3,10.6,1.4
SC,South Carolina,11,1.1,11.1,1.2
SD,South Dakota,8.6,1.3,8,1.1
TN,Tennessee,10.7,1.2,11.1,1.1
TX,Texas,11.7,0.6,14.4,0.6
UT,Utah,6.9,1,9.9,1.1
VT,Vermont,7.3,1,8.9,1.2
VA,Virginia,8.7,1.2,10.6,1.2
WA,Washington,8.8,1.2,10.5,1.3
WV,West Virginia,13.5,1.5,12.1,1.6
WI,Wisconsin,8.8,1.5,7.9,1.2
WY,Wyoming,8.6,1.1,8.7,1.1
28 changes: 28 additions & 0 deletions analysis/asec-2026-anchored-thresholds/replicate_and_anchor.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
"""Replicate P60-290 SPM/official toplines from the 2026 CPS ASEC public-use person file
and recompute 2025 SPM poverty with national thresholds anchored to 2024 (x CPI-U).
Weights: SPM_WEIGHT/100 for SPM, MARSUPWT/100 for official. Tenure codes verified from
the implied base threshold: 1 owner w/ mortgage 41,323; 2 owner w/o 34,326; 3 renter 41,701.
Anchoring scales only the national base by tenure (equivalence scale and geo adjustment
stay at 2025 values). 2024 bases = BLS corrected series (39,231 / 32,879 / 39,220).
CPI-U factor = Census's own 2025/2024 two-adult-two-child OPM threshold ratio.
"""
import sys, json, pandas as pd, numpy as np
cpi = float(sys.argv[1]) if len(sys.argv) > 1 else 32649/31812
d = pd.read_csv('pppub26.csv', usecols=['A_AGE','MARSUPWT','SPM_WEIGHT','SPM_POOR','SPM_RESOURCES','SPM_POVTHRESHOLD','SPM_TENMORTSTATUS','PERLIS','POV_UNIV'])
mw = d.MARSUPWT/100; w = mw # person weight for person-level poverty rates (SPM_WEIGHT variant checked: within 0.05pp)
grp = np.select([d.A_AGE<18, d.A_AGE>=65], ['children','65+'], '18-64')
t25 = {1:41322.707, 2:34325.998, 3:41700.556}; t24 = {1:39231.0, 2:32879.0, 3:39220.0}
fac = d.SPM_TENMORTSTATUS.map({k:(t24[k]*cpi)/t25[k] for k in t25})
poor_anch = d.SPM_RESOURCES < d.SPM_POVTHRESHOLD*fac
def rates(flag, wt):
return {g: round(100*(wt[m]*flag[m]).sum()/wt[m].sum(), 2) for g,m in
[('all', np.ones(len(d),bool)), ('children', grp=='children'), ('18-64', grp=='18-64'), ('65+', grp=='65+')]}
out = {"cpi_factor": round(cpi,5), "anchor_factors": {k: round((t24[k]*cpi)/t25[k],4) for k in t25},
"spm_published_replication": rates(d.SPM_POOR==1, w),
"official_replication": {g: round(100*(mw[m]*(d.PERLIS[m]==1)).sum()/mw[m].sum(),2) for g,m in
[('all', d.POV_UNIV==1), ('children', (grp=='children')&(d.POV_UNIV==1)), ('18-64', (grp=='18-64')&(d.POV_UNIV==1)), ('65+', (grp=='65+')&(d.POV_UNIV==1))]},
"spm_anchored_2024_thresholds": rates(poor_anch, w),
"by_tenure": {name: {"published": round(100*(w[m]*(d.SPM_POOR[m]==1)).sum()/w[m].sum(),2), "anchored": round(100*(w[m]*poor_anch[m]).sum()/w[m].sum(),2)}
for name,m in [('owner_with_mortgage', d.SPM_TENMORTSTATUS==1), ('owner_without_mortgage', d.SPM_TENMORTSTATUS==2), ('renter', d.SPM_TENMORTSTATUS==3)]}}
out["threshold_effect_pp"] = {g: round(out["spm_published_replication"][g]-out["spm_anchored_2024_thresholds"][g],2) for g in out["spm_published_replication"]}
print(json.dumps(out, indent=1))
Loading
Loading