Skip to content

Instantly share code, notes, and snippets.

@MaxGhenis
Created March 24, 2026 20:05
Show Gist options
  • Select an option

  • Save MaxGhenis/bf9e48ef6d935c088abad871fb1f5f2f to your computer and use it in GitHub Desktop.

Select an option

Save MaxGhenis/bf9e48ef6d935c088abad871fb1f5f2f to your computer and use it in GitHub Desktop.
PolicyEngine 2025 top income thresholds (top 1% and 0.1% AGI minimums)
Display the source blob
Display the rendered blob
Raw
{
"cells": [
{
"cell_type": "markdown",
"id": "6a5f4915",
"metadata": {},
"source": [
"# Top income thresholds in PolicyEngine's 2025 microsimulation\n",
"\n",
"AGI minimums for top 1% and top 0.1% of tax units in 2025,\n",
"with IRS SOI TY 2022 for comparison."
]
},
{
"cell_type": "code",
"execution_count": 1,
"id": "b404f6b4",
"metadata": {
"execution": {
"iopub.execute_input": "2026-03-24T20:04:39.052571Z",
"iopub.status.busy": "2026-03-24T20:04:39.052299Z",
"iopub.status.idle": "2026-03-24T20:05:53.500369Z",
"shell.execute_reply": "2026-03-24T20:05:53.499949Z"
}
},
"outputs": [],
"source": [
"from policyengine_us import Microsimulation\n",
"import numpy as np\n",
"import pandas as pd\n",
"\n",
"sim = Microsimulation()\n",
"YEAR = 2025\n",
"\n",
"weight = np.array(sim.calc(\"tax_unit_weight\", period=YEAR))\n",
"agi = np.array(sim.calc(\"adjusted_gross_income\", period=YEAR))\n",
"is_filer = np.array(sim.calc(\"tax_unit_is_filer\", period=YEAR)).astype(bool)"
]
},
{
"cell_type": "markdown",
"id": "063f551c",
"metadata": {},
"source": [
"## All tax units (including non-filers)"
]
},
{
"cell_type": "code",
"execution_count": 2,
"id": "808c2c2b",
"metadata": {
"execution": {
"iopub.execute_input": "2026-03-24T20:05:53.501657Z",
"iopub.status.busy": "2026-03-24T20:05:53.501582Z",
"iopub.status.idle": "2026-03-24T20:05:53.506338Z",
"shell.execute_reply": "2026-03-24T20:05:53.505974Z"
}
},
"outputs": [],
"source": [
"total_all = weight.sum()\n",
"sort_idx = np.argsort(agi)\n",
"agi_sorted = agi[sort_idx]\n",
"wt_sorted = weight[sort_idx]\n",
"cum_pct = np.cumsum(wt_sorted) / total_all\n",
"\n",
"idx_99 = np.searchsorted(cum_pct, 0.99)\n",
"idx_999 = np.searchsorted(cum_pct, 0.999)\n",
"\n",
"all_top1_min = agi_sorted[idx_99]\n",
"all_top1_units = wt_sorted[idx_99:].sum()\n",
"all_top01_min = agi_sorted[idx_999]\n",
"all_top01_units = wt_sorted[idx_999:].sum()"
]
},
{
"cell_type": "markdown",
"id": "5f6e70a8",
"metadata": {},
"source": [
"## Filers only"
]
},
{
"cell_type": "code",
"execution_count": 3,
"id": "5e2c5d8f",
"metadata": {
"execution": {
"iopub.execute_input": "2026-03-24T20:05:53.507330Z",
"iopub.status.busy": "2026-03-24T20:05:53.507266Z",
"iopub.status.idle": "2026-03-24T20:05:53.511485Z",
"shell.execute_reply": "2026-03-24T20:05:53.511123Z"
}
},
"outputs": [],
"source": [
"agi_f = agi[is_filer]\n",
"wt_f = weight[is_filer]\n",
"total_filers = wt_f.sum()\n",
"\n",
"sort_idx_f = np.argsort(agi_f)\n",
"agi_sorted_f = agi_f[sort_idx_f]\n",
"wt_sorted_f = wt_f[sort_idx_f]\n",
"cum_pct_f = np.cumsum(wt_sorted_f) / total_filers\n",
"\n",
"idx_99_f = np.searchsorted(cum_pct_f, 0.99)\n",
"idx_999_f = np.searchsorted(cum_pct_f, 0.999)\n",
"\n",
"filer_top1_min = agi_sorted_f[idx_99_f]\n",
"filer_top1_units = wt_sorted_f[idx_99_f:].sum()\n",
"filer_top01_min = agi_sorted_f[idx_999_f]\n",
"filer_top01_units = wt_sorted_f[idx_999_f:].sum()"
]
},
{
"cell_type": "markdown",
"id": "c3ac8b33",
"metadata": {},
"source": [
"## Comparison table"
]
},
{
"cell_type": "code",
"execution_count": 4,
"id": "6397bd93",
"metadata": {
"execution": {
"iopub.execute_input": "2026-03-24T20:05:53.512256Z",
"iopub.status.busy": "2026-03-24T20:05:53.512205Z",
"iopub.status.idle": "2026-03-24T20:05:53.521889Z",
"shell.execute_reply": "2026-03-24T20:05:53.521598Z"
}
},
"outputs": [
{
"data": {
"text/html": [
"<div>\n",
"<style scoped>\n",
" .dataframe tbody tr th:only-of-type {\n",
" vertical-align: middle;\n",
" }\n",
"\n",
" .dataframe tbody tr th {\n",
" vertical-align: top;\n",
" }\n",
"\n",
" .dataframe thead th {\n",
" text-align: right;\n",
" }\n",
"</style>\n",
"<table border=\"1\" class=\"dataframe\">\n",
" <thead>\n",
" <tr style=\"text-align: right;\">\n",
" <th></th>\n",
" <th>PolicyEngine 2025 (all)</th>\n",
" <th>PolicyEngine 2025 (filers)</th>\n",
" <th>IRS SOI TY 2022</th>\n",
" </tr>\n",
" <tr>\n",
" <th>Metric</th>\n",
" <th></th>\n",
" <th></th>\n",
" <th></th>\n",
" </tr>\n",
" </thead>\n",
" <tbody>\n",
" <tr>\n",
" <th>Total tax units</th>\n",
" <td>194,116,928</td>\n",
" <td>151,335,680</td>\n",
" <td>153,801,397</td>\n",
" </tr>\n",
" <tr>\n",
" <th>Top 1% AGI minimum</th>\n",
" <td>$645,448</td>\n",
" <td>$753,655</td>\n",
" <td>$663,164</td>\n",
" </tr>\n",
" <tr>\n",
" <th>Top 1% tax units</th>\n",
" <td>1,985,947</td>\n",
" <td>1,513,074</td>\n",
" <td>1,538,014</td>\n",
" </tr>\n",
" <tr>\n",
" <th>Top 0.1% AGI minimum</th>\n",
" <td>$3,715,259</td>\n",
" <td>$4,075,306</td>\n",
" <td>$3,271,387</td>\n",
" </tr>\n",
" <tr>\n",
" <th>Top 0.1% tax units</th>\n",
" <td>197,571</td>\n",
" <td>153,729</td>\n",
" <td>153,801</td>\n",
" </tr>\n",
" </tbody>\n",
"</table>\n",
"</div>"
],
"text/plain": [
" PolicyEngine 2025 (all) PolicyEngine 2025 (filers) \\\n",
"Metric \n",
"Total tax units 194,116,928 151,335,680 \n",
"Top 1% AGI minimum $645,448 $753,655 \n",
"Top 1% tax units 1,985,947 1,513,074 \n",
"Top 0.1% AGI minimum $3,715,259 $4,075,306 \n",
"Top 0.1% tax units 197,571 153,729 \n",
"\n",
" IRS SOI TY 2022 \n",
"Metric \n",
"Total tax units 153,801,397 \n",
"Top 1% AGI minimum $663,164 \n",
"Top 1% tax units 1,538,014 \n",
"Top 0.1% AGI minimum $3,271,387 \n",
"Top 0.1% tax units 153,801 "
]
},
"execution_count": 4,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"comparison = pd.DataFrame({\n",
" \"Metric\": [\n",
" \"Total tax units\",\n",
" \"Top 1% AGI minimum\",\n",
" \"Top 1% tax units\",\n",
" \"Top 0.1% AGI minimum\",\n",
" \"Top 0.1% tax units\",\n",
" ],\n",
" \"PolicyEngine 2025 (all)\": [\n",
" f\"{total_all:,.0f}\",\n",
" f\"${all_top1_min:,.0f}\",\n",
" f\"{all_top1_units:,.0f}\",\n",
" f\"${all_top01_min:,.0f}\",\n",
" f\"{all_top01_units:,.0f}\",\n",
" ],\n",
" \"PolicyEngine 2025 (filers)\": [\n",
" f\"{total_filers:,.0f}\",\n",
" f\"${filer_top1_min:,.0f}\",\n",
" f\"{filer_top1_units:,.0f}\",\n",
" f\"${filer_top01_min:,.0f}\",\n",
" f\"{filer_top01_units:,.0f}\",\n",
" ],\n",
" \"IRS SOI TY 2022\": [\n",
" \"153,801,397\",\n",
" \"$663,164\",\n",
" \"1,538,014\",\n",
" \"$3,271,387\",\n",
" \"153,801\",\n",
" ],\n",
"})\n",
"comparison.set_index(\"Metric\")"
]
}
],
"metadata": {
"jupytext": {
"cell_metadata_filter": "-all",
"main_language": "python",
"notebook_metadata_filter": "-all"
},
"language_info": {
"codemirror_mode": {
"name": "ipython",
"version": 3
},
"file_extension": ".py",
"mimetype": "text/x-python",
"name": "python",
"nbconvert_exporter": "python",
"pygments_lexer": "ipython3",
"version": "3.14.0"
}
},
"nbformat": 4,
"nbformat_minor": 5
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment