diff --git a/us/states/la/data_exploration.ipynb b/us/states/la/data_exploration.ipynb new file mode 100644 index 0000000..c19bbee --- /dev/null +++ b/us/states/la/data_exploration.ipynb @@ -0,0 +1,411 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# LA Dataset Exploration\n", + "\n", + "This notebook explores the Louisiana (LA) dataset to understand household counts, income distribution, and demographic characteristics." + ] + }, + { + "cell_type": "code", + "execution_count": 1, + "metadata": {}, + "outputs": [], + "source": [ + "from policyengine_us import Microsimulation\n", + "import pandas as pd\n", + "import numpy as np\n", + "\n", + "LA_DATASET = \"hf://policyengine/policyengine-us-data/states/LA.h5\"" + ] + }, + { + "cell_type": "code", + "execution_count": 2, + "metadata": {}, + "outputs": [ + { + "name": "stderr", + "output_type": "stream", + "text": [ + "Xet Storage is enabled for this repo, but the 'hf_xet' package is not installed. Falling back to regular HTTP download. For better performance, install the package with: `pip install huggingface_hub[hf_xet]` or `pip install hf_xet`\n" + ] + }, + { + "data": { + "application/vnd.jupyter.widget-view+json": { + "model_id": "7cead5eac53b42aaa314abee9421696e", + "version_major": 2, + "version_minor": 0 + }, + "text/plain": [ + "LA.h5: 0%| | 0.00/37.2M [00:00 0]['household_weight'].sum()\n", + "households_with_1_child = children_per_household[children_per_household['is_child'] == 1]['household_weight'].sum()\n", + "households_with_2_children = children_per_household[children_per_household['is_child'] == 2]['household_weight'].sum()\n", + "households_with_3plus_children = children_per_household[children_per_household['is_child'] >= 3]['household_weight'].sum()\n", + "\n", + "print(f\"\\nHouseholds with children (weighted):\")\n", + "print(f\" Total households with children: {total_households_with_children:,.0f}\")\n", + "print(f\" Households with 1 child: {households_with_1_child:,.0f}\")\n", + "print(f\" Households with 2 children: {households_with_2_children:,.0f}\")\n", + "print(f\" Households with 3+ children: {households_with_3plus_children:,.0f}\")" + ] + }, + { + "cell_type": "code", + "execution_count": 6, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "\n", + "Children by age:\n", + " Total children under 18: 1,102,922\n", + " Children under 6: 324,808\n", + " Children under 3: 158,424\n" + ] + } + ], + "source": [ + "# Check children by age groups\n", + "df = pd.DataFrame({\n", + " \"household_id\": sim.calculate(\"household_id\", map_to=\"person\"),\n", + " \"tax_unit_id\": sim.calculate(\"tax_unit_id\", map_to=\"person\"),\n", + " \"person_id\": sim.calculate(\"person_id\", map_to=\"person\"),\n", + " \"age\": sim.calculate(\"age\", map_to=\"person\"),\n", + " \"person_weight\": sim.calculate(\"person_weight\", map_to=\"person\")\n", + "})\n", + "\n", + "# Filter for children and apply weights\n", + "children_under_18_df = df[df['age'] < 18]\n", + "children_under_6_df = df[df['age'] < 6]\n", + "children_under_3_df = df[df['age'] < 3]\n", + "\n", + "# Calculate weighted totals\n", + "total_children = children_under_18_df['person_weight'].sum()\n", + "children_under_6 = children_under_6_df['person_weight'].sum()\n", + "children_under_3 = children_under_3_df['person_weight'].sum()\n", + "\n", + "print(f\"\\nChildren by age:\")\n", + "print(f\" Total children under 18: {total_children:,.0f}\")\n", + "print(f\" Children under 6: {children_under_6:,.0f}\")\n", + "print(f\" Children under 3: {children_under_3:,.0f}\")" + ] + }, + { + "cell_type": "code", + "execution_count": 7, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "\n", + "============================================================\n", + "LA DATASET SUMMARY - WEIGHTED (Population Estimates)\n", + "============================================================\n", + " Metric Value\n", + " Household count (weighted) 1,609,614\n", + " Person count (weighted) 4,548,557\n", + " Median AGI $29,715\n", + " 75th percentile AGI $79,047\n", + " 90th percentile AGI $151,127\n", + " 95th percentile AGI $224,678\n", + " Max AGI $6,430,892\n", + "Total households with children 540,663\n", + " Households with 1 child 211,686\n", + " Households with 2 children 175,881\n", + " Households with 3+ children 153,096\n", + " Total children under 18 1,102,922\n", + " Children under 6 324,808\n", + " Children under 3 158,424\n", + "============================================================\n", + "\n", + "Summary saved to: la_dataset_summary_weighted.csv\n" + ] + } + ], + "source": [ + "# Create weighted summary table\n", + "weighted_summary_data = {\n", + " 'Metric': [\n", + " 'Household count (weighted)',\n", + " 'Person count (weighted)',\n", + " 'Median AGI',\n", + " '75th percentile AGI',\n", + " '90th percentile AGI',\n", + " '95th percentile AGI',\n", + " 'Max AGI',\n", + " 'Total households with children',\n", + " 'Households with 1 child',\n", + " 'Households with 2 children',\n", + " 'Households with 3+ children',\n", + " 'Total children under 18',\n", + " 'Children under 6',\n", + " 'Children under 3'\n", + " ],\n", + " 'Value': [\n", + " f\"{household_count.sum():,.0f}\",\n", + " f\"{person_count.sum():,.0f}\",\n", + " f\"${agi.median():,.0f}\",\n", + " f\"${agi.quantile(0.75):,.0f}\",\n", + " f\"${agi.quantile(0.90):,.0f}\",\n", + " f\"${agi.quantile(0.95):,.0f}\",\n", + " f\"${agi.max():,.0f}\",\n", + " f\"{total_households_with_children:,.0f}\",\n", + " f\"{households_with_1_child:,.0f}\",\n", + " f\"{households_with_2_children:,.0f}\",\n", + " f\"{households_with_3plus_children:,.0f}\",\n", + " f\"{total_children:,.0f}\",\n", + " f\"{children_under_6:,.0f}\",\n", + " f\"{children_under_3:,.0f}\"\n", + " ]\n", + "}\n", + "\n", + "weighted_df = pd.DataFrame(weighted_summary_data)\n", + "\n", + "print(\"\\n\" + \"=\"*60)\n", + "print(\"LA DATASET SUMMARY - WEIGHTED (Population Estimates)\")\n", + "print(\"=\"*60)\n", + "print(weighted_df.to_string(index=False))\n", + "print(\"=\"*60)\n", + "\n", + "# Save table\n", + "weighted_df.to_csv('la_dataset_summary_weighted.csv', index=False)\n", + "print(\"\\nSummary saved to: la_dataset_summary_weighted.csv\")" + ] + }, + { + "cell_type": "code", + "execution_count": 8, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "\n", + "======================================================================\n", + "HOUSEHOLDS WITH $0 INCOME\n", + "======================================================================\n", + "Household count: 204,254\n", + "Percentage of all households: 12.69%\n", + "======================================================================\n" + ] + } + ], + "source": [ + "# Households with $0 income\n", + "agi_hh = np.array(sim.calculate(\"adjusted_gross_income\", period=2025, map_to=\"household\"))\n", + "weights = np.array(sim.calculate(\"household_weight\", period=2025))\n", + "\n", + "zero_income_mask = agi_hh == 0\n", + "zero_income_count = weights[zero_income_mask].sum()\n", + "total_households = weights.sum()\n", + "\n", + "print(\"\\n\" + \"=\"*70)\n", + "print(\"HOUSEHOLDS WITH $0 INCOME\")\n", + "print(\"=\"*70)\n", + "print(f\"Household count: {zero_income_count:,.0f}\")\n", + "print(f\"Percentage of all households: {zero_income_count / total_households * 100:.2f}%\")\n", + "print(\"=\"*70)" + ] + }, + { + "cell_type": "code", + "execution_count": 9, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "\n", + "======================================================================\n", + "HOUSEHOLD COUNTS BY INCOME BRACKET\n", + "======================================================================\n", + "Income Bracket Households % of All Households\n", + " $0-$10k 463,600 28.80%\n", + " $10k-$20k 158,107 9.82%\n", + " $20k-$30k 143,144 8.89%\n", + " $30k-$40k 96,403 5.99%\n", + " $40k-$50k 102,480 6.37%\n", + " $50k-$60k 80,002 4.97%\n", + "======================================================================\n", + "\n", + "Total households in $0-$60k range: 1,043,735\n", + "Percentage of all households in $0-$60k range: 64.84%\n" + ] + } + ], + "source": [ + "# Household counts by income brackets\n", + "income_brackets = [\n", + " (0, 10000, \"$0-$10k\"),\n", + " (10000, 20000, \"$10k-$20k\"),\n", + " (20000, 30000, \"$20k-$30k\"),\n", + " (30000, 40000, \"$30k-$40k\"),\n", + " (40000, 50000, \"$40k-$50k\"),\n", + " (50000, 60000, \"$50k-$60k\")\n", + "]\n", + "\n", + "bracket_data = []\n", + "for lower, upper, label in income_brackets:\n", + " mask = (agi_hh >= lower) & (agi_hh < upper)\n", + " count = weights[mask].sum()\n", + " pct_of_total = (count / total_households) * 100\n", + " \n", + " bracket_data.append({\n", + " \"Income Bracket\": label,\n", + " \"Households\": f\"{count:,.0f}\",\n", + " \"% of All Households\": f\"{pct_of_total:.2f}%\"\n", + " })\n", + "\n", + "income_df = pd.DataFrame(bracket_data)\n", + "\n", + "print(\"\\n\" + \"=\"*70)\n", + "print(\"HOUSEHOLD COUNTS BY INCOME BRACKET\")\n", + "print(\"=\"*70)\n", + "print(income_df.to_string(index=False))\n", + "print(\"=\"*70)\n", + "\n", + "# Total in $0-$60k range\n", + "total_in_range = sum([weights[(agi_hh >= lower) & (agi_hh < upper)].sum() for lower, upper, _ in income_brackets])\n", + "print(f\"\\nTotal households in $0-$60k range: {total_in_range:,.0f}\")\n", + "print(f\"Percentage of all households in $0-$60k range: {total_in_range / total_households * 100:.2f}%\")" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3", + "language": "python", + "name": "python3" + }, + "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.11.5" + } + }, + "nbformat": 4, + "nbformat_minor": 4 +} diff --git a/us/states/la/la_dataset_summary_weighted.csv b/us/states/la/la_dataset_summary_weighted.csv new file mode 100644 index 0000000..66b79b8 --- /dev/null +++ b/us/states/la/la_dataset_summary_weighted.csv @@ -0,0 +1,15 @@ +Metric,Value +Household count (weighted),"1,609,614" +Person count (weighted),"4,548,557" +Median AGI,"$29,715" +75th percentile AGI,"$79,047" +90th percentile AGI,"$151,127" +95th percentile AGI,"$224,678" +Max AGI,"$6,430,892" +Total households with children,"540,663" +Households with 1 child,"211,686" +Households with 2 children,"175,881" +Households with 3+ children,"153,096" +Total children under 18,"1,102,922" +Children under 6,"324,808" +Children under 3,"158,424" diff --git a/us/states/la/la_tax_reform_analysis.ipynb b/us/states/la/la_tax_reform_analysis.ipynb new file mode 100644 index 0000000..a8c5862 --- /dev/null +++ b/us/states/la/la_tax_reform_analysis.ipynb @@ -0,0 +1,601 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# Louisiana Tax Reform Analysis (2025)\n", + "\n", + "This notebook analyzes the impact of Louisiana's tax reform package.\n", + "\n", + "## Baseline (Prior Law)\n", + "- Louisiana standard deduction: **disabled** (false)\n", + "- Retirement income exemption bracket 2: **$6,000**\n", + "- Flat income tax rate: **disabled** (false)\n", + "\n", + "## Reform (Current Law)\n", + "- Louisiana standard deduction: **enabled** (true)\n", + "- Retirement income exemption bracket 2: **$12,000**\n", + "- Flat income tax rate: **enabled** (true)\n", + "\n", + "## Metrics\n", + "We calculate:\n", + "- Budgetary impact (revenue change)\n", + "- Winners and losers (percentage of population affected)\n", + "- Overall poverty impact\n", + "- Child poverty impact" + ] + }, + { + "cell_type": "code", + "execution_count": 10, + "metadata": {}, + "outputs": [], + "source": [ + "from policyengine_us import Microsimulation\n", + "from policyengine_core.reforms import Reform\n", + "import pandas as pd\n", + "import numpy as np\n", + "\n", + "LA_DATASET = \"hf://policyengine/policyengine-us-data/states/LA.h5\"\n", + "YEAR = 2025" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Helper Functions" + ] + }, + { + "cell_type": "code", + "execution_count": 11, + "metadata": {}, + "outputs": [], + "source": [ + "def calculate_poverty(sim, period=2025, child_only=False):\n", + " \"\"\"\n", + " Calculate poverty rate and count.\n", + " \n", + " Args:\n", + " sim: Microsimulation object\n", + " period: Year to analyze\n", + " child_only: If True, only count children under 18\n", + " \n", + " Returns:\n", + " poverty_rate: Weighted poverty rate\n", + " people_in_poverty: Weighted count\n", + " \"\"\"\n", + " age = np.array(sim.calculate(\"age\", period=period))\n", + " is_in_poverty = np.array(sim.calculate(\"person_in_poverty\", period=period))\n", + " person_weight = np.array(sim.calculate(\"person_weight\", period=period))\n", + " \n", + " if child_only:\n", + " mask = age < 18\n", + " else:\n", + " mask = np.ones_like(age, dtype=bool)\n", + " \n", + " # Weighted poverty rate\n", + " weighted_in_poverty = (is_in_poverty[mask] * person_weight[mask]).sum()\n", + " weighted_total = person_weight[mask].sum()\n", + " poverty_rate = weighted_in_poverty / weighted_total if weighted_total > 0 else 0\n", + " \n", + " # Weighted count of people in poverty\n", + " people_in_poverty = weighted_in_poverty\n", + " \n", + " return {\n", + " \"poverty_rate\": poverty_rate,\n", + " \"people_in_poverty\": people_in_poverty,\n", + " \"total_people\": weighted_total\n", + " }\n", + "\n", + "def calculate_winners_losers(baseline_sim, reform_sim, period=2025):\n", + " \"\"\"\n", + " Calculate winners and losers from a reform at the person level (weighted).\n", + " Winners: People in households with higher net income under reform.\n", + " Losers: People in households with lower net income under reform.\n", + " Returns weighted counts and percentages of total population.\n", + " \"\"\"\n", + " # Get household-level income change\n", + " baseline_income = np.array(baseline_sim.calculate(\"household_net_income\", period=period, map_to=\"household\"))\n", + " reform_income = np.array(reform_sim.calculate(\"household_net_income\", period=period, map_to=\"household\"))\n", + " household_weight = np.array(baseline_sim.calculate(\"household_weight\", period=period))\n", + " income_change = reform_income - baseline_income\n", + " \n", + " # Get person-level data\n", + " household_id_person = np.array(baseline_sim.calculate(\"household_id\", period=period, map_to=\"person\"))\n", + " household_id_household = np.array(baseline_sim.calculate(\"household_id\", period=period, map_to=\"household\"))\n", + " person_weight = np.array(baseline_sim.calculate(\"person_weight\", period=period))\n", + " \n", + " # Create mapping of household_id to income_change\n", + " income_change_dict = dict(zip(household_id_household, income_change))\n", + " \n", + " # Map income change to each person\n", + " person_income_change = np.array([income_change_dict.get(hh_id, 0) for hh_id in household_id_person])\n", + " \n", + " # Weighted count of people who are winners/losers (threshold of $1)\n", + " winners_mask = person_income_change > 1\n", + " losers_mask = person_income_change < -1\n", + " \n", + " people_winning = person_weight[winners_mask].sum()\n", + " people_losing = person_weight[losers_mask].sum()\n", + " total_people = person_weight.sum()\n", + " \n", + " # Calculate percentages\n", + " pct_winners = (people_winning / total_people * 100) if total_people > 0 else 0\n", + " pct_losers = (people_losing / total_people * 100) if total_people > 0 else 0\n", + " \n", + " # Average gain/loss for winning/losing households (weighted)\n", + " winning_hh_mask = income_change > 1\n", + " losing_hh_mask = income_change < -1\n", + " \n", + " if winning_hh_mask.sum() > 0:\n", + " avg_gain = np.average(income_change[winning_hh_mask], weights=household_weight[winning_hh_mask])\n", + " else:\n", + " avg_gain = 0\n", + " \n", + " if losing_hh_mask.sum() > 0:\n", + " avg_loss = np.average(income_change[losing_hh_mask], weights=household_weight[losing_hh_mask])\n", + " else:\n", + " avg_loss = 0\n", + " \n", + " return {\n", + " \"people_winning\": people_winning,\n", + " \"people_losing\": people_losing,\n", + " \"total_people\": total_people,\n", + " \"pct_winners\": pct_winners,\n", + " \"pct_losers\": pct_losers,\n", + " \"avg_gain\": avg_gain,\n", + " \"avg_loss\": avg_loss\n", + " }\n", + "\n", + "def format_currency(value):\n", + " \"\"\"Format value as currency in millions or billions.\"\"\"\n", + " if abs(value) >= 1e9:\n", + " return f\"${value/1e9:.2f}B\"\n", + " return f\"${value/1e6:.2f}M\"\n", + "\n", + "def format_percent(value):\n", + " \"\"\"Format value as percentage.\"\"\"\n", + " return f\"{value*100:.2f}%\"" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Define Baseline (Prior Law)" + ] + }, + { + "cell_type": "code", + "execution_count": 12, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Baseline reform function defined!\n" + ] + } + ], + "source": [ + "def create_baseline_reform():\n", + " \"\"\"\n", + " Baseline: Prior law settings\n", + " - Standard deduction: disabled (false)\n", + " - Retirement exemption bracket 2: $6,000\n", + " - Flat tax rate: disabled (false)\n", + " \"\"\"\n", + " reform = Reform.from_dict(\n", + " {\n", + " \"gov.states.la.tax.income.deductions.standard.applies\": {\n", + " \"2025-01-01.2100-12-31\": False\n", + " },\n", + " \"gov.states.la.tax.income.exempt_income.retirement.cap[1].amount\": {\n", + " \"2025-01-01.2100-12-31\": 6000\n", + " },\n", + " \"gov.states.la.tax.income.main.flat.applies\": {\n", + " \"2025-01-01.2100-12-31\": False\n", + " }\n", + " },\n", + " country_id=\"us\",\n", + " )\n", + " return reform\n", + "\n", + "print(\"Baseline reform function defined!\")" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Load Simulations" + ] + }, + { + "cell_type": "code", + "execution_count": 13, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Loading baseline (prior law - old LA tax settings)...\n", + "Baseline loaded\n", + "\n", + "Loading reform (current law - new LA tax settings)...\n", + "Reform loaded\n", + "\n", + "============================================================\n", + "All simulations ready!\n", + "============================================================\n" + ] + } + ], + "source": [ + "print(\"Loading baseline (prior law - old LA tax settings)...\")\n", + "baseline_reform = create_baseline_reform()\n", + "baseline = Microsimulation(dataset=LA_DATASET, reform=baseline_reform)\n", + "print(\"Baseline loaded\")\n", + "\n", + "print(\"\\nLoading reform (current law - new LA tax settings)...\")\n", + "reform_sim = Microsimulation(dataset=LA_DATASET)\n", + "print(\"Reform loaded\")\n", + "\n", + "print(\"\\n\" + \"=\"*60)\n", + "print(\"All simulations ready!\")\n", + "print(\"=\"*60)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Calculate Impacts" + ] + }, + { + "cell_type": "code", + "execution_count": 14, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "All impacts calculated\n" + ] + } + ], + "source": [ + "# Baseline metrics\n", + "baseline_overall_pov = calculate_poverty(baseline, child_only=False)\n", + "baseline_child_pov = calculate_poverty(baseline, child_only=True)\n", + "\n", + "# Reform metrics\n", + "reform_overall_pov = calculate_poverty(reform_sim, child_only=False)\n", + "reform_child_pov = calculate_poverty(reform_sim, child_only=True)\n", + "\n", + "# Revenue impact - calculated as change in state income tax\n", + "# Note: .sum() on MicroSeries already applies weights internally\n", + "baseline_tax = baseline.calculate(\"la_income_tax\", period=YEAR, map_to=\"household\").sum()\n", + "reform_tax = reform_sim.calculate(\"la_income_tax\", period=YEAR, map_to=\"household\").sum()\n", + "revenue_change = reform_tax - baseline_tax\n", + "\n", + "# Net income impact (cost to state = gain to households)\n", + "baseline_hh_income = baseline.calculate(\"household_net_income\", period=YEAR, map_to=\"household\").sum()\n", + "reform_hh_income = reform_sim.calculate(\"household_net_income\", period=YEAR, map_to=\"household\").sum()\n", + "net_income_change = reform_hh_income - baseline_hh_income\n", + "\n", + "# Winners and losers (at person level)\n", + "winners_losers = calculate_winners_losers(baseline, reform_sim)\n", + "\n", + "print(\"All impacts calculated\")" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Results Summary" + ] + }, + { + "cell_type": "code", + "execution_count": 15, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "\n", + "================================================================================\n", + "LOUISIANA TAX REFORM IMPACTS (2025)\n", + "Baseline: Prior Law | Reform: Current Law\n", + "================================================================================\n", + "\n", + "Reform components:\n", + " 1. Enable Louisiana standard deduction (false -> true)\n", + " 2. Double retirement income exemption bracket 2 ($6k -> $12k)\n", + " 3. Enable flat income tax rate (false -> true)\n", + "\n", + "================================BUDGETARY IMPACT================================\n", + "Baseline LA income tax revenue: $4.93B\n", + "Reform LA income tax revenue: $3.45B\n", + "Revenue change: $-1.48B\n", + "Net income gain to households: $1.46B\n", + "\n", + "========================WINNERS AND LOSERS (POPULATION)=========================\n", + "People gaining income: 3,620,437 (79.60% of population)\n", + "Average gain per household: $1,312.58\n", + "People losing income: 135 (0.00% of population)\n", + "Average loss per household: $-112.39\n", + "\n", + "============================POVERTY IMPACT - OVERALL============================\n", + "Baseline poverty rate: 25.08%\n", + "Reform poverty rate: 24.78%\n", + "Absolute reduction: 0.30%\n", + "Relative reduction: 1.21%\n", + "People lifted from poverty: 13,766\n", + "\n", + "===========================POVERTY IMPACT - CHILDREN============================\n", + "Baseline child poverty rate: 21.59%\n", + "Reform child poverty rate: 21.25%\n", + "Absolute reduction: 0.35%\n", + "Relative reduction: 1.61%\n", + "Children lifted from poverty: 3,880\n", + "================================================================================\n" + ] + } + ], + "source": [ + "print(\"\\n\" + \"=\"*80)\n", + "print(\"LOUISIANA TAX REFORM IMPACTS (2025)\")\n", + "print(\"Baseline: Prior Law | Reform: Current Law\")\n", + "print(\"=\"*80)\n", + "print(\"\\nReform components:\")\n", + "print(\" 1. Enable Louisiana standard deduction (false -> true)\")\n", + "print(\" 2. Double retirement income exemption bracket 2 ($6k -> $12k)\")\n", + "print(\" 3. Enable flat income tax rate (false -> true)\")\n", + "\n", + "print(f\"\\n{'BUDGETARY IMPACT':=^80}\")\n", + "print(f\"Baseline LA income tax revenue: {format_currency(baseline_tax)}\")\n", + "print(f\"Reform LA income tax revenue: {format_currency(reform_tax)}\")\n", + "print(f\"Revenue change: {format_currency(revenue_change)}\")\n", + "print(f\"Net income gain to households: {format_currency(net_income_change)}\")\n", + "\n", + "print(f\"\\n{'WINNERS AND LOSERS (POPULATION)':=^80}\")\n", + "print(f\"People gaining income: {winners_losers['people_winning']:,.0f} ({winners_losers['pct_winners']:.2f}% of population)\")\n", + "print(f\"Average gain per household: ${winners_losers['avg_gain']:,.2f}\")\n", + "print(f\"People losing income: {winners_losers['people_losing']:,.0f} ({winners_losers['pct_losers']:.2f}% of population)\")\n", + "print(f\"Average loss per household: ${winners_losers['avg_loss']:,.2f}\")\n", + "\n", + "print(f\"\\n{'POVERTY IMPACT - OVERALL':=^80}\")\n", + "print(f\"Baseline poverty rate: {format_percent(baseline_overall_pov['poverty_rate'])}\")\n", + "print(f\"Reform poverty rate: {format_percent(reform_overall_pov['poverty_rate'])}\")\n", + "overall_pov_reduction = baseline_overall_pov['poverty_rate'] - reform_overall_pov['poverty_rate']\n", + "overall_pov_pct_reduction = (overall_pov_reduction / baseline_overall_pov['poverty_rate'] * 100) if baseline_overall_pov['poverty_rate'] > 0 else 0\n", + "print(f\"Absolute reduction: {format_percent(overall_pov_reduction)}\")\n", + "print(f\"Relative reduction: {overall_pov_pct_reduction:.2f}%\")\n", + "people_lifted = baseline_overall_pov['people_in_poverty'] - reform_overall_pov['people_in_poverty']\n", + "print(f\"People lifted from poverty: {people_lifted:,.0f}\")\n", + "\n", + "print(f\"\\n{'POVERTY IMPACT - CHILDREN':=^80}\")\n", + "print(f\"Baseline child poverty rate: {format_percent(baseline_child_pov['poverty_rate'])}\")\n", + "print(f\"Reform child poverty rate: {format_percent(reform_child_pov['poverty_rate'])}\")\n", + "child_pov_reduction = baseline_child_pov['poverty_rate'] - reform_child_pov['poverty_rate']\n", + "child_pov_pct_reduction = (child_pov_reduction / baseline_child_pov['poverty_rate'] * 100) if baseline_child_pov['poverty_rate'] > 0 else 0\n", + "print(f\"Absolute reduction: {format_percent(child_pov_reduction)}\")\n", + "print(f\"Relative reduction: {child_pov_pct_reduction:.2f}%\")\n", + "children_lifted = baseline_child_pov['people_in_poverty'] - reform_child_pov['people_in_poverty']\n", + "print(f\"Children lifted from poverty: {children_lifted:,.0f}\")\n", + "print(\"=\"*80)" + ] + }, + { + "cell_type": "code", + "execution_count": 16, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "======================================================================\n", + "HOUSEHOLD-LEVEL SUMMARY\n", + "======================================================================\n", + "Households benefitting: 1,112,533 (69.12%)\n", + "Households losing: 135 (0.01%)\n", + "Total households: 1,609,614\n", + "======================================================================\n" + ] + } + ], + "source": [ + "# Calculate households benefitting (weighted)\n", + "baseline_hh_income_arr = np.array(baseline.calculate(\"household_net_income\", period=YEAR, map_to=\"household\"))\n", + "reform_hh_income_arr = np.array(reform_sim.calculate(\"household_net_income\", period=YEAR, map_to=\"household\"))\n", + "household_weight = np.array(baseline.calculate(\"household_weight\", period=YEAR))\n", + "\n", + "hh_income_change = reform_hh_income_arr - baseline_hh_income_arr\n", + "hh_benefitting_mask = hh_income_change > 1 # Gained more than $1\n", + "hh_losing_mask = hh_income_change < -1 # Lost more than $1\n", + "\n", + "households_benefitting = household_weight[hh_benefitting_mask].sum()\n", + "households_losing = household_weight[hh_losing_mask].sum()\n", + "total_households = household_weight.sum()\n", + "pct_households_benefitting = (households_benefitting / total_households) * 100\n", + "pct_households_losing = (households_losing / total_households) * 100\n", + "\n", + "print(\"=\"*70)\n", + "print(\"HOUSEHOLD-LEVEL SUMMARY\")\n", + "print(\"=\"*70)\n", + "print(f\"Households benefitting: {households_benefitting:,.0f} ({pct_households_benefitting:.2f}%)\")\n", + "print(f\"Households losing: {households_losing:,.0f} ({pct_households_losing:.2f}%)\")\n", + "print(f\"Total households: {total_households:,.0f}\")\n", + "print(\"=\"*70)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Distributional Analysis by Income" + ] + }, + { + "cell_type": "code", + "execution_count": 17, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "\n", + "====================================================================================================\n", + "DISTRIBUTIONAL ANALYSIS BY INCOME\n", + "====================================================================================================\n", + "Income Bracket Households Avg Income Change Total Change ($M) Winners Losers\n", + " $0-$25k 698,970 $49 $34.2 246,524 134\n", + " $25k-$50k 264,763 $323 $85.4 263,859 0\n", + " $50k-$75k 172,219 $463 $79.7 172,219 0\n", + " $75k-$100k 137,709 $637 $87.8 137,709 0\n", + " $100k-$150k 128,017 $898 $114.9 128,017 0\n", + " $150k-$250k 88,479 $1,608 $142.3 88,479 0\n", + " $250k+ 74,540 $12,289 $916.0 74,540 0\n", + "====================================================================================================\n" + ] + } + ], + "source": [ + "# Get household income for distributional analysis\n", + "agi = np.array(baseline.calculate(\"adjusted_gross_income\", period=YEAR, map_to=\"household\"))\n", + "weights = np.array(baseline.calculate(\"household_weight\", period=YEAR))\n", + "tax_change_arr = np.array(reform_sim.calculate(\"household_net_income\", period=YEAR, map_to=\"household\")) - \\\n", + " np.array(baseline.calculate(\"household_net_income\", period=YEAR, map_to=\"household\"))\n", + "\n", + "# Income bracket analysis\n", + "income_brackets = [\n", + " (0, 25000, \"$0-$25k\"),\n", + " (25000, 50000, \"$25k-$50k\"),\n", + " (50000, 75000, \"$50k-$75k\"),\n", + " (75000, 100000, \"$75k-$100k\"),\n", + " (100000, 150000, \"$100k-$150k\"),\n", + " (150000, 250000, \"$150k-$250k\"),\n", + " (250000, float('inf'), \"$250k+\")\n", + "]\n", + "\n", + "distributional_data = []\n", + "for lower, upper, label in income_brackets:\n", + " mask = (agi >= lower) & (agi < upper)\n", + " if weights[mask].sum() > 0:\n", + " hh_count = weights[mask].sum()\n", + " # Weighted total change for this bracket\n", + " total_change = (tax_change_arr[mask] * weights[mask]).sum()\n", + " avg_change = total_change / hh_count\n", + " winners_in_bracket = weights[mask & (tax_change_arr > 1)].sum()\n", + " losers_in_bracket = weights[mask & (tax_change_arr < -1)].sum()\n", + " \n", + " distributional_data.append({\n", + " \"Income Bracket\": label,\n", + " \"Households\": f\"{hh_count:,.0f}\",\n", + " \"Avg Income Change\": f\"${avg_change:,.0f}\",\n", + " \"Total Change ($M)\": f\"${total_change/1e6:,.1f}\",\n", + " \"Winners\": f\"{winners_in_bracket:,.0f}\",\n", + " \"Losers\": f\"{losers_in_bracket:,.0f}\"\n", + " })\n", + "\n", + "dist_df = pd.DataFrame(distributional_data)\n", + "\n", + "print(\"\\n\" + \"=\"*100)\n", + "print(\"DISTRIBUTIONAL ANALYSIS BY INCOME\")\n", + "print(\"=\"*100)\n", + "print(dist_df.to_string(index=False))\n", + "print(\"=\"*100)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Export Results" + ] + }, + { + "cell_type": "code", + "execution_count": 18, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "\n", + "========================================================================================================================\n", + "LA TAX REFORM SUMMARY\n", + "========================================================================================================================\n", + " Scenario Description Revenue Change Net Income Change Overall Poverty Change (%) Child Poverty Change (%) % Population Winning % Population Losing\n", + "LA Tax Reform Standard deduction + retirement exemption + flat tax $-1.48B $1.46B 1.21% 1.61% 79.60% 0.00%\n", + "========================================================================================================================\n", + "\n", + "Exported to: la_tax_reform_results.csv\n" + ] + } + ], + "source": [ + "# Create results DataFrame\n", + "results = [\n", + " {\n", + " \"Scenario\": \"LA Tax Reform\",\n", + " \"Description\": \"Standard deduction + retirement exemption + flat tax\",\n", + " \"Revenue Change\": format_currency(revenue_change),\n", + " \"Net Income Change\": format_currency(net_income_change),\n", + " \"Overall Poverty Change (%)\": f\"{overall_pov_pct_reduction:.2f}%\",\n", + " \"Child Poverty Change (%)\": f\"{child_pov_pct_reduction:.2f}%\",\n", + " \"% Population Winning\": f\"{winners_losers['pct_winners']:.2f}%\",\n", + " \"% Population Losing\": f\"{winners_losers['pct_losers']:.2f}%\"\n", + " }\n", + "]\n", + "\n", + "df_results = pd.DataFrame(results)\n", + "\n", + "print(\"\\n\" + \"=\"*120)\n", + "print(\"LA TAX REFORM SUMMARY\")\n", + "print(\"=\"*120)\n", + "print(df_results.to_string(index=False))\n", + "print(\"=\"*120)\n", + "\n", + "# Export to CSV\n", + "df_results.to_csv(\"la_tax_reform_results.csv\", index=False)\n", + "print(\"\\nExported to: la_tax_reform_results.csv\")" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3", + "language": "python", + "name": "python3" + }, + "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.11.5" + } + }, + "nbformat": 4, + "nbformat_minor": 4 +} diff --git a/us/states/la/la_tax_reform_results.csv b/us/states/la/la_tax_reform_results.csv new file mode 100644 index 0000000..8b1dc69 --- /dev/null +++ b/us/states/la/la_tax_reform_results.csv @@ -0,0 +1,2 @@ +Scenario,Description,Revenue Change,Net Income Change,Overall Poverty Change (%),Child Poverty Change (%),% Population Winning,% Population Losing +LA Tax Reform,Standard deduction + retirement exemption + flat tax,$-1.48B,$1.46B,1.21%,1.61%,79.60%,0.00%