From 616baeadaea2695cc121986415cadc1707bae344 Mon Sep 17 00:00:00 2001 From: David Trimmer Date: Tue, 3 Feb 2026 13:59:23 -0500 Subject: [PATCH] SC H.3492 Partially Refundable EITC Analysis Notebooks Fixes #111 --- us/states/sc/sc_h3492_household_example.ipynb | 897 +++++++++ us/states/sc/sc_h3492_household_summary.csv | 8 + us/states/sc/sc_h3492_income_brackets.csv | 6 + us/states/sc/sc_h3492_microsimulation.ipynb | 1764 +++++++++++++++++ us/states/sc/sc_h3492_summary_stats.csv | 10 + 5 files changed, 2685 insertions(+) create mode 100644 us/states/sc/sc_h3492_household_example.ipynb create mode 100644 us/states/sc/sc_h3492_household_summary.csv create mode 100644 us/states/sc/sc_h3492_income_brackets.csv create mode 100644 us/states/sc/sc_h3492_microsimulation.ipynb create mode 100644 us/states/sc/sc_h3492_summary_stats.csv diff --git a/us/states/sc/sc_h3492_household_example.ipynb b/us/states/sc/sc_h3492_household_example.ipynb new file mode 100644 index 0000000..db07405 --- /dev/null +++ b/us/states/sc/sc_h3492_household_example.ipynb @@ -0,0 +1,897 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# SC H.3492 Partially Refundable EITC - Household Examples\n", + "\n", + "This notebook demonstrates the impact of South Carolina H.3492 on individual households.\n", + "\n", + "## Reform Summary\n", + "- **Baseline**: SC EITC = 125% of federal EITC (non-refundable, limited by tax liability)\n", + "- **Reform**: Makes 25% of the excess SC EITC (above tax liability) refundable\n", + "\n", + "## Key Formula\n", + "- If SC EITC > tax liability:\n", + " - Non-refundable portion = tax liability\n", + " - Refundable portion = 25% × (SC EITC - tax liability)\n", + "\n", + "## Bill Reference\n", + "[SC H.3492 (126th Session, 2025-2026)](https://www.scstatehouse.gov/sess126_2025-2026/bills/3492.htm)" + ] + }, + { + "cell_type": "code", + "execution_count": 1, + "metadata": {}, + "outputs": [], + "source": [ + "from policyengine_us import Simulation\n", + "from policyengine_us.reforms.states.sc.h3492.sc_h3492_eitc_refundable import sc_h3492_eitc_refundable\n", + "import pandas as pd\n", + "import numpy as np" + ] + }, + { + "cell_type": "code", + "execution_count": 2, + "metadata": {}, + "outputs": [], + "source": [ + "def create_reform():\n", + " \"\"\"Create the SC H.3492 reform.\"\"\"\n", + " return sc_h3492_eitc_refundable" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Example 1: Single Parent with One Child, $20,000 Income\n", + "\n", + "This is the primary example from the issue specification. A low-income single parent should see significant benefit from the partial refundability." + ] + }, + { + "cell_type": "code", + "execution_count": 3, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "======================================================================\n", + "EXAMPLE 1: Single Parent with 1 child (age 5), $20k income\n", + "======================================================================\n", + "\n", + "Federal EITC: $4,328.00\n", + "SC EITC (125% of federal): $5,410.00\n", + "\n", + "Baseline (non-refundable):\n", + " SC tax before non-refundable credits: $0.00\n", + " SC non-refundable credits: $5,410.00\n", + " SC refundable credits: $0.00\n", + " SC income tax: $0.00\n", + " Household net income: $28,016.10\n", + "\n", + "Reform (25% of excess refundable):\n", + " SC non-refundable credits: $0.00\n", + " SC refundable credits: $1,352.50\n", + " SC income tax: $-1,352.50\n", + " Household net income: $29,368.60\n", + "\n", + ">>> BENEFIT FROM REFORM: $1,352.50\n" + ] + } + ], + "source": [ + "situation_1 = {\n", + " \"people\": {\n", + " \"parent\": {\n", + " \"age\": {2025: 30},\n", + " \"employment_income\": {2025: 20_000}\n", + " },\n", + " \"child\": {\n", + " \"age\": {2025: 5},\n", + " \"is_tax_unit_dependent\": {2025: True}\n", + " }\n", + " },\n", + " \"tax_units\": {\n", + " \"tax_unit\": {\n", + " \"members\": [\"parent\", \"child\"]\n", + " }\n", + " },\n", + " \"families\": {\n", + " \"family\": {\n", + " \"members\": [\"parent\", \"child\"]\n", + " }\n", + " },\n", + " \"spm_units\": {\n", + " \"spm_unit\": {\n", + " \"members\": [\"parent\", \"child\"]\n", + " }\n", + " },\n", + " \"households\": {\n", + " \"household\": {\n", + " \"members\": [\"parent\", \"child\"],\n", + " \"state_code\": {2025: \"SC\"}\n", + " }\n", + " }\n", + "}\n", + "\n", + "baseline_1 = Simulation(situation=situation_1)\n", + "reform_1 = Simulation(situation=situation_1, reform=create_reform())\n", + "\n", + "print(\"=\"*70)\n", + "print(\"EXAMPLE 1: Single Parent with 1 child (age 5), $20k income\")\n", + "print(\"=\"*70)\n", + "print(f\"\\nFederal EITC: ${baseline_1.calculate('eitc', 2025)[0]:,.2f}\")\n", + "print(f\"SC EITC (125% of federal): ${baseline_1.calculate('sc_eitc', 2025)[0]:,.2f}\")\n", + "print(f\"\\nBaseline (non-refundable):\")\n", + "print(f\" SC tax before non-refundable credits: ${baseline_1.calculate('sc_income_tax_before_non_refundable_credits', 2025)[0]:,.2f}\")\n", + "print(f\" SC non-refundable credits: ${baseline_1.calculate('sc_non_refundable_credits', 2025)[0]:,.2f}\")\n", + "print(f\" SC refundable credits: ${baseline_1.calculate('sc_refundable_credits', 2025)[0]:,.2f}\")\n", + "print(f\" SC income tax: ${baseline_1.calculate('sc_income_tax', 2025)[0]:,.2f}\")\n", + "print(f\" Household net income: ${baseline_1.calculate('household_net_income', 2025)[0]:,.2f}\")\n", + "print(f\"\\nReform (25% of excess refundable):\")\n", + "print(f\" SC non-refundable credits: ${reform_1.calculate('sc_non_refundable_credits', 2025)[0]:,.2f}\")\n", + "print(f\" SC refundable credits: ${reform_1.calculate('sc_refundable_credits', 2025)[0]:,.2f}\")\n", + "print(f\" SC income tax: ${reform_1.calculate('sc_income_tax', 2025)[0]:,.2f}\")\n", + "print(f\" Household net income: ${reform_1.calculate('household_net_income', 2025)[0]:,.2f}\")\n", + "print(f\"\\n>>> BENEFIT FROM REFORM: ${reform_1.calculate('household_net_income', 2025)[0] - baseline_1.calculate('household_net_income', 2025)[0]:,.2f}\")" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Example 2: Higher Income - Credit Absorbed by Tax Liability\n", + "\n", + "A household with higher income will have their SC EITC fully absorbed by their tax liability, meaning no refundable portion under the reform." + ] + }, + { + "cell_type": "code", + "execution_count": 4, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "======================================================================\n", + "EXAMPLE 2: Single Parent with 1 child, $40k income (credit absorbed)\n", + "======================================================================\n", + "\n", + "Federal EITC: $1,667.33\n", + "SC EITC (125% of federal): $2,084.20\n", + "\n", + "Baseline (non-refundable):\n", + " SC tax before non-refundable credits: $239.61\n", + " SC non-refundable credits: $2,084.20\n", + " SC refundable credits: $0.00\n", + " SC income tax: $0.00\n", + " Household net income: $39,169.83\n", + "\n", + "Reform (25% of excess refundable):\n", + " SC non-refundable credits: $239.61\n", + " SC refundable credits: $461.15\n", + " SC income tax: $-461.15\n", + " Household net income: $39,630.98\n", + "\n", + ">>> BENEFIT FROM REFORM: $461.15\n", + "\n", + "*** Credit is fully absorbed by tax liability - no excess to refund ***\n" + ] + } + ], + "source": [ + "situation_2 = {\n", + " \"people\": {\n", + " \"parent\": {\n", + " \"age\": {2025: 35},\n", + " \"employment_income\": {2025: 40_000}\n", + " },\n", + " \"child\": {\n", + " \"age\": {2025: 8},\n", + " \"is_tax_unit_dependent\": {2025: True}\n", + " }\n", + " },\n", + " \"tax_units\": {\n", + " \"tax_unit\": {\n", + " \"members\": [\"parent\", \"child\"]\n", + " }\n", + " },\n", + " \"families\": {\n", + " \"family\": {\n", + " \"members\": [\"parent\", \"child\"]\n", + " }\n", + " },\n", + " \"spm_units\": {\n", + " \"spm_unit\": {\n", + " \"members\": [\"parent\", \"child\"]\n", + " }\n", + " },\n", + " \"households\": {\n", + " \"household\": {\n", + " \"members\": [\"parent\", \"child\"],\n", + " \"state_code\": {2025: \"SC\"}\n", + " }\n", + " }\n", + "}\n", + "\n", + "baseline_2 = Simulation(situation=situation_2)\n", + "reform_2 = Simulation(situation=situation_2, reform=create_reform())\n", + "\n", + "print(\"=\"*70)\n", + "print(\"EXAMPLE 2: Single Parent with 1 child, $40k income (credit absorbed)\")\n", + "print(\"=\"*70)\n", + "print(f\"\\nFederal EITC: ${baseline_2.calculate('eitc', 2025)[0]:,.2f}\")\n", + "print(f\"SC EITC (125% of federal): ${baseline_2.calculate('sc_eitc', 2025)[0]:,.2f}\")\n", + "print(f\"\\nBaseline (non-refundable):\")\n", + "print(f\" SC tax before non-refundable credits: ${baseline_2.calculate('sc_income_tax_before_non_refundable_credits', 2025)[0]:,.2f}\")\n", + "print(f\" SC non-refundable credits: ${baseline_2.calculate('sc_non_refundable_credits', 2025)[0]:,.2f}\")\n", + "print(f\" SC refundable credits: ${baseline_2.calculate('sc_refundable_credits', 2025)[0]:,.2f}\")\n", + "print(f\" SC income tax: ${baseline_2.calculate('sc_income_tax', 2025)[0]:,.2f}\")\n", + "print(f\" Household net income: ${baseline_2.calculate('household_net_income', 2025)[0]:,.2f}\")\n", + "print(f\"\\nReform (25% of excess refundable):\")\n", + "print(f\" SC non-refundable credits: ${reform_2.calculate('sc_non_refundable_credits', 2025)[0]:,.2f}\")\n", + "print(f\" SC refundable credits: ${reform_2.calculate('sc_refundable_credits', 2025)[0]:,.2f}\")\n", + "print(f\" SC income tax: ${reform_2.calculate('sc_income_tax', 2025)[0]:,.2f}\")\n", + "print(f\" Household net income: ${reform_2.calculate('household_net_income', 2025)[0]:,.2f}\")\n", + "print(f\"\\n>>> BENEFIT FROM REFORM: ${reform_2.calculate('household_net_income', 2025)[0] - baseline_2.calculate('household_net_income', 2025)[0]:,.2f}\")\n", + "print(\"\\n*** Credit is fully absorbed by tax liability - no excess to refund ***\")" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Example 3: Married Couple with Two Children, Low Income\n", + "\n", + "A married couple with two children at very low income should see a significant benefit from the refundable portion." + ] + }, + { + "cell_type": "code", + "execution_count": 5, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "======================================================================\n", + "EXAMPLE 3: Married Couple with 2 children, $15k income\n", + "======================================================================\n", + "\n", + "Federal EITC: $6,000.00\n", + "SC EITC (125% of federal): $7,500.00\n", + "\n", + "Baseline (non-refundable):\n", + " SC tax before non-refundable credits: $0.00\n", + " SC non-refundable credits: $7,500.00\n", + " SC refundable credits: $0.00\n", + " SC income tax: $0.00\n", + " Household net income: $44,754.70\n", + "\n", + "Reform (25% of excess refundable):\n", + " SC non-refundable credits: $0.00\n", + " SC refundable credits: $1,875.00\n", + " SC income tax: $-1,875.00\n", + " Household net income: $46,629.70\n", + "\n", + ">>> BENEFIT FROM REFORM: $1,875.00\n" + ] + } + ], + "source": [ + "situation_3 = {\n", + " \"people\": {\n", + " \"parent1\": {\n", + " \"age\": {2025: 28},\n", + " \"employment_income\": {2025: 15_000}\n", + " },\n", + " \"parent2\": {\n", + " \"age\": {2025: 27},\n", + " \"employment_income\": {2025: 0}\n", + " },\n", + " \"child1\": {\n", + " \"age\": {2025: 3},\n", + " \"is_tax_unit_dependent\": {2025: True}\n", + " },\n", + " \"child2\": {\n", + " \"age\": {2025: 6},\n", + " \"is_tax_unit_dependent\": {2025: True}\n", + " }\n", + " },\n", + " \"tax_units\": {\n", + " \"tax_unit\": {\n", + " \"members\": [\"parent1\", \"parent2\", \"child1\", \"child2\"]\n", + " }\n", + " },\n", + " \"families\": {\n", + " \"family\": {\n", + " \"members\": [\"parent1\", \"parent2\", \"child1\", \"child2\"]\n", + " }\n", + " },\n", + " \"marital_units\": {\n", + " \"marital_unit\": {\n", + " \"members\": [\"parent1\", \"parent2\"]\n", + " }\n", + " },\n", + " \"spm_units\": {\n", + " \"spm_unit\": {\n", + " \"members\": [\"parent1\", \"parent2\", \"child1\", \"child2\"]\n", + " }\n", + " },\n", + " \"households\": {\n", + " \"household\": {\n", + " \"members\": [\"parent1\", \"parent2\", \"child1\", \"child2\"],\n", + " \"state_code\": {2025: \"SC\"}\n", + " }\n", + " }\n", + "}\n", + "\n", + "baseline_3 = Simulation(situation=situation_3)\n", + "reform_3 = Simulation(situation=situation_3, reform=create_reform())\n", + "\n", + "print(\"=\"*70)\n", + "print(\"EXAMPLE 3: Married Couple with 2 children, $15k income\")\n", + "print(\"=\"*70)\n", + "print(f\"\\nFederal EITC: ${baseline_3.calculate('eitc', 2025)[0]:,.2f}\")\n", + "print(f\"SC EITC (125% of federal): ${baseline_3.calculate('sc_eitc', 2025)[0]:,.2f}\")\n", + "print(f\"\\nBaseline (non-refundable):\")\n", + "print(f\" SC tax before non-refundable credits: ${baseline_3.calculate('sc_income_tax_before_non_refundable_credits', 2025)[0]:,.2f}\")\n", + "print(f\" SC non-refundable credits: ${baseline_3.calculate('sc_non_refundable_credits', 2025)[0]:,.2f}\")\n", + "print(f\" SC refundable credits: ${baseline_3.calculate('sc_refundable_credits', 2025)[0]:,.2f}\")\n", + "print(f\" SC income tax: ${baseline_3.calculate('sc_income_tax', 2025)[0]:,.2f}\")\n", + "print(f\" Household net income: ${baseline_3.calculate('household_net_income', 2025)[0]:,.2f}\")\n", + "print(f\"\\nReform (25% of excess refundable):\")\n", + "print(f\" SC non-refundable credits: ${reform_3.calculate('sc_non_refundable_credits', 2025)[0]:,.2f}\")\n", + "print(f\" SC refundable credits: ${reform_3.calculate('sc_refundable_credits', 2025)[0]:,.2f}\")\n", + "print(f\" SC income tax: ${reform_3.calculate('sc_income_tax', 2025)[0]:,.2f}\")\n", + "print(f\" Household net income: ${reform_3.calculate('household_net_income', 2025)[0]:,.2f}\")\n", + "print(f\"\\n>>> BENEFIT FROM REFORM: ${reform_3.calculate('household_net_income', 2025)[0] - baseline_3.calculate('household_net_income', 2025)[0]:,.2f}\")" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Example 4: Childless Worker (No EITC Benefit)\n", + "\n", + "A childless worker receives a smaller federal EITC. This verifies the reform works correctly for this population." + ] + }, + { + "cell_type": "code", + "execution_count": 6, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "======================================================================\n", + "EXAMPLE 4: Childless Adult, $12k income\n", + "======================================================================\n", + "\n", + "Federal EITC: $543.43\n", + "SC EITC (125% of federal): $679.30\n", + "\n", + "Baseline (non-refundable):\n", + " SC tax before non-refundable credits: $0.00\n", + " SC non-refundable credits: $679.30\n", + " SC refundable credits: $0.00\n", + " SC income tax: $0.00\n", + " Household net income: $13,006.33\n", + "\n", + "Reform (25% of excess refundable):\n", + " SC non-refundable credits: $0.00\n", + " SC refundable credits: $169.82\n", + " SC income tax: $-169.82\n", + " Household net income: $13,176.16\n", + "\n", + ">>> BENEFIT FROM REFORM: $169.83\n" + ] + } + ], + "source": [ + "situation_4 = {\n", + " \"people\": {\n", + " \"adult\": {\n", + " \"age\": {2025: 30},\n", + " \"employment_income\": {2025: 12_000}\n", + " }\n", + " },\n", + " \"tax_units\": {\n", + " \"tax_unit\": {\n", + " \"members\": [\"adult\"]\n", + " }\n", + " },\n", + " \"families\": {\n", + " \"family\": {\n", + " \"members\": [\"adult\"]\n", + " }\n", + " },\n", + " \"spm_units\": {\n", + " \"spm_unit\": {\n", + " \"members\": [\"adult\"]\n", + " }\n", + " },\n", + " \"households\": {\n", + " \"household\": {\n", + " \"members\": [\"adult\"],\n", + " \"state_code\": {2025: \"SC\"}\n", + " }\n", + " }\n", + "}\n", + "\n", + "baseline_4 = Simulation(situation=situation_4)\n", + "reform_4 = Simulation(situation=situation_4, reform=create_reform())\n", + "\n", + "print(\"=\"*70)\n", + "print(\"EXAMPLE 4: Childless Adult, $12k income\")\n", + "print(\"=\"*70)\n", + "print(f\"\\nFederal EITC: ${baseline_4.calculate('eitc', 2025)[0]:,.2f}\")\n", + "print(f\"SC EITC (125% of federal): ${baseline_4.calculate('sc_eitc', 2025)[0]:,.2f}\")\n", + "print(f\"\\nBaseline (non-refundable):\")\n", + "print(f\" SC tax before non-refundable credits: ${baseline_4.calculate('sc_income_tax_before_non_refundable_credits', 2025)[0]:,.2f}\")\n", + "print(f\" SC non-refundable credits: ${baseline_4.calculate('sc_non_refundable_credits', 2025)[0]:,.2f}\")\n", + "print(f\" SC refundable credits: ${baseline_4.calculate('sc_refundable_credits', 2025)[0]:,.2f}\")\n", + "print(f\" SC income tax: ${baseline_4.calculate('sc_income_tax', 2025)[0]:,.2f}\")\n", + "print(f\" Household net income: ${baseline_4.calculate('household_net_income', 2025)[0]:,.2f}\")\n", + "print(f\"\\nReform (25% of excess refundable):\")\n", + "print(f\" SC non-refundable credits: ${reform_4.calculate('sc_non_refundable_credits', 2025)[0]:,.2f}\")\n", + "print(f\" SC refundable credits: ${reform_4.calculate('sc_refundable_credits', 2025)[0]:,.2f}\")\n", + "print(f\" SC income tax: ${reform_4.calculate('sc_income_tax', 2025)[0]:,.2f}\")\n", + "print(f\" Household net income: ${reform_4.calculate('household_net_income', 2025)[0]:,.2f}\")\n", + "print(f\"\\n>>> BENEFIT FROM REFORM: ${reform_4.calculate('household_net_income', 2025)[0] - baseline_4.calculate('household_net_income', 2025)[0]:,.2f}\")" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Example 5: Zero Tax Liability (Maximum Refund Scenario)\n", + "\n", + "A household with zero tax liability represents the maximum refund scenario - the entire excess EITC is subject to the 25% refund rate." + ] + }, + { + "cell_type": "code", + "execution_count": 7, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "======================================================================\n", + "EXAMPLE 5: Single Parent with 1 child, $10k income (low/zero tax)\n", + "======================================================================\n", + "\n", + "Federal EITC: $3,400.00\n", + "SC EITC (125% of federal): $4,250.00\n", + "\n", + "Baseline (non-refundable):\n", + " SC tax before non-refundable credits: $0.00\n", + " SC non-refundable credits: $4,250.00\n", + " SC refundable credits: $0.00\n", + " SC income tax: $0.00\n", + " Household net income: $39,547.81\n", + "\n", + "Reform (25% of excess refundable):\n", + " SC non-refundable credits: $0.00\n", + " SC refundable credits: $1,062.50\n", + " SC income tax: $-1,062.50\n", + " Household net income: $40,610.31\n", + "\n", + ">>> BENEFIT FROM REFORM: $1,062.50\n", + "\n", + "*** Expected refund calculation: 25% × ($4,250.00 - $0.00) = $1,062.50 ***\n" + ] + } + ], + "source": [ + "situation_5 = {\n", + " \"people\": {\n", + " \"parent\": {\n", + " \"age\": {2025: 25},\n", + " \"employment_income\": {2025: 10_000}\n", + " },\n", + " \"child\": {\n", + " \"age\": {2025: 2},\n", + " \"is_tax_unit_dependent\": {2025: True}\n", + " }\n", + " },\n", + " \"tax_units\": {\n", + " \"tax_unit\": {\n", + " \"members\": [\"parent\", \"child\"]\n", + " }\n", + " },\n", + " \"families\": {\n", + " \"family\": {\n", + " \"members\": [\"parent\", \"child\"]\n", + " }\n", + " },\n", + " \"spm_units\": {\n", + " \"spm_unit\": {\n", + " \"members\": [\"parent\", \"child\"]\n", + " }\n", + " },\n", + " \"households\": {\n", + " \"household\": {\n", + " \"members\": [\"parent\", \"child\"],\n", + " \"state_code\": {2025: \"SC\"}\n", + " }\n", + " }\n", + "}\n", + "\n", + "baseline_5 = Simulation(situation=situation_5)\n", + "reform_5 = Simulation(situation=situation_5, reform=create_reform())\n", + "\n", + "print(\"=\"*70)\n", + "print(\"EXAMPLE 5: Single Parent with 1 child, $10k income (low/zero tax)\")\n", + "print(\"=\"*70)\n", + "print(f\"\\nFederal EITC: ${baseline_5.calculate('eitc', 2025)[0]:,.2f}\")\n", + "print(f\"SC EITC (125% of federal): ${baseline_5.calculate('sc_eitc', 2025)[0]:,.2f}\")\n", + "print(f\"\\nBaseline (non-refundable):\")\n", + "print(f\" SC tax before non-refundable credits: ${baseline_5.calculate('sc_income_tax_before_non_refundable_credits', 2025)[0]:,.2f}\")\n", + "print(f\" SC non-refundable credits: ${baseline_5.calculate('sc_non_refundable_credits', 2025)[0]:,.2f}\")\n", + "print(f\" SC refundable credits: ${baseline_5.calculate('sc_refundable_credits', 2025)[0]:,.2f}\")\n", + "print(f\" SC income tax: ${baseline_5.calculate('sc_income_tax', 2025)[0]:,.2f}\")\n", + "print(f\" Household net income: ${baseline_5.calculate('household_net_income', 2025)[0]:,.2f}\")\n", + "print(f\"\\nReform (25% of excess refundable):\")\n", + "print(f\" SC non-refundable credits: ${reform_5.calculate('sc_non_refundable_credits', 2025)[0]:,.2f}\")\n", + "print(f\" SC refundable credits: ${reform_5.calculate('sc_refundable_credits', 2025)[0]:,.2f}\")\n", + "print(f\" SC income tax: ${reform_5.calculate('sc_income_tax', 2025)[0]:,.2f}\")\n", + "print(f\" Household net income: ${reform_5.calculate('household_net_income', 2025)[0]:,.2f}\")\n", + "\n", + "sc_eitc = baseline_5.calculate('sc_eitc', 2025)[0]\n", + "tax_before = baseline_5.calculate('sc_income_tax_before_non_refundable_credits', 2025)[0]\n", + "expected_refund = 0.25 * max(0, sc_eitc - tax_before)\n", + "print(f\"\\n>>> BENEFIT FROM REFORM: ${reform_5.calculate('household_net_income', 2025)[0] - baseline_5.calculate('household_net_income', 2025)[0]:,.2f}\")\n", + "print(f\"\\n*** Expected refund calculation: 25% × (${sc_eitc:,.2f} - ${tax_before:,.2f}) = ${expected_refund:,.2f} ***\")" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Example 6: Three Children at Peak EITC\n", + "\n", + "A household with three children at peak federal EITC income to verify the reform handles larger credit amounts." + ] + }, + { + "cell_type": "code", + "execution_count": 8, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "======================================================================\n", + "EXAMPLE 6: Single Parent with 3 children, $17k income (peak EITC)\n", + "======================================================================\n", + "\n", + "Federal EITC: $7,650.00\n", + "SC EITC (125% of federal): $9,562.50\n", + "\n", + "Baseline (non-refundable):\n", + " SC tax before non-refundable credits: $0.00\n", + " SC non-refundable credits: $9,562.50\n", + " SC refundable credits: $0.00\n", + " SC income tax: $0.00\n", + " Household net income: $49,188.90\n", + "\n", + "Reform (25% of excess refundable):\n", + " SC non-refundable credits: $0.00\n", + " SC refundable credits: $2,390.62\n", + " SC income tax: $-2,390.62\n", + " Household net income: $51,579.52\n", + "\n", + ">>> BENEFIT FROM REFORM: $2,390.62\n" + ] + } + ], + "source": [ + "situation_6 = {\n", + " \"people\": {\n", + " \"parent\": {\n", + " \"age\": {2025: 35},\n", + " \"employment_income\": {2025: 17_000}\n", + " },\n", + " \"child1\": {\n", + " \"age\": {2025: 4},\n", + " \"is_tax_unit_dependent\": {2025: True}\n", + " },\n", + " \"child2\": {\n", + " \"age\": {2025: 7},\n", + " \"is_tax_unit_dependent\": {2025: True}\n", + " },\n", + " \"child3\": {\n", + " \"age\": {2025: 10},\n", + " \"is_tax_unit_dependent\": {2025: True}\n", + " }\n", + " },\n", + " \"tax_units\": {\n", + " \"tax_unit\": {\n", + " \"members\": [\"parent\", \"child1\", \"child2\", \"child3\"]\n", + " }\n", + " },\n", + " \"families\": {\n", + " \"family\": {\n", + " \"members\": [\"parent\", \"child1\", \"child2\", \"child3\"]\n", + " }\n", + " },\n", + " \"spm_units\": {\n", + " \"spm_unit\": {\n", + " \"members\": [\"parent\", \"child1\", \"child2\", \"child3\"]\n", + " }\n", + " },\n", + " \"households\": {\n", + " \"household\": {\n", + " \"members\": [\"parent\", \"child1\", \"child2\", \"child3\"],\n", + " \"state_code\": {2025: \"SC\"}\n", + " }\n", + " }\n", + "}\n", + "\n", + "baseline_6 = Simulation(situation=situation_6)\n", + "reform_6 = Simulation(situation=situation_6, reform=create_reform())\n", + "\n", + "print(\"=\"*70)\n", + "print(\"EXAMPLE 6: Single Parent with 3 children, $17k income (peak EITC)\")\n", + "print(\"=\"*70)\n", + "print(f\"\\nFederal EITC: ${baseline_6.calculate('eitc', 2025)[0]:,.2f}\")\n", + "print(f\"SC EITC (125% of federal): ${baseline_6.calculate('sc_eitc', 2025)[0]:,.2f}\")\n", + "print(f\"\\nBaseline (non-refundable):\")\n", + "print(f\" SC tax before non-refundable credits: ${baseline_6.calculate('sc_income_tax_before_non_refundable_credits', 2025)[0]:,.2f}\")\n", + "print(f\" SC non-refundable credits: ${baseline_6.calculate('sc_non_refundable_credits', 2025)[0]:,.2f}\")\n", + "print(f\" SC refundable credits: ${baseline_6.calculate('sc_refundable_credits', 2025)[0]:,.2f}\")\n", + "print(f\" SC income tax: ${baseline_6.calculate('sc_income_tax', 2025)[0]:,.2f}\")\n", + "print(f\" Household net income: ${baseline_6.calculate('household_net_income', 2025)[0]:,.2f}\")\n", + "print(f\"\\nReform (25% of excess refundable):\")\n", + "print(f\" SC non-refundable credits: ${reform_6.calculate('sc_non_refundable_credits', 2025)[0]:,.2f}\")\n", + "print(f\" SC refundable credits: ${reform_6.calculate('sc_refundable_credits', 2025)[0]:,.2f}\")\n", + "print(f\" SC income tax: ${reform_6.calculate('sc_income_tax', 2025)[0]:,.2f}\")\n", + "print(f\" Household net income: ${reform_6.calculate('household_net_income', 2025)[0]:,.2f}\")\n", + "print(f\"\\n>>> BENEFIT FROM REFORM: ${reform_6.calculate('household_net_income', 2025)[0] - baseline_6.calculate('household_net_income', 2025)[0]:,.2f}\")" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Example 7: No Income (No EITC)\n", + "\n", + "A household with no earned income receives no federal EITC, so there's no SC EITC to refund. This confirms the reform correctly handles edge cases." + ] + }, + { + "cell_type": "code", + "execution_count": 9, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "======================================================================\n", + "EXAMPLE 7: Single Parent with 1 child, $0 income (no EITC)\n", + "======================================================================\n", + "\n", + "Federal EITC: $0.00\n", + "SC EITC (125% of federal): $0.00\n", + "\n", + "Baseline (non-refundable):\n", + " SC tax before non-refundable credits: $0.00\n", + " SC non-refundable credits: $0.00\n", + " SC refundable credits: $0.00\n", + " SC income tax: $0.00\n", + " Household net income: $7,578.00\n", + "\n", + "Reform (25% of excess refundable):\n", + " SC non-refundable credits: $0.00\n", + " SC refundable credits: $0.00\n", + " SC income tax: $0.00\n", + " Household net income: $7,578.00\n", + "\n", + ">>> BENEFIT FROM REFORM: $0.00\n", + "\n", + "*** No EITC because EITC phases in with earned income ***\n" + ] + } + ], + "source": [ + "situation_7 = {\n", + " \"people\": {\n", + " \"parent\": {\n", + " \"age\": {2025: 30},\n", + " \"employment_income\": {2025: 0}\n", + " },\n", + " \"child\": {\n", + " \"age\": {2025: 5},\n", + " \"is_tax_unit_dependent\": {2025: True}\n", + " }\n", + " },\n", + " \"tax_units\": {\n", + " \"tax_unit\": {\n", + " \"members\": [\"parent\", \"child\"]\n", + " }\n", + " },\n", + " \"families\": {\n", + " \"family\": {\n", + " \"members\": [\"parent\", \"child\"]\n", + " }\n", + " },\n", + " \"spm_units\": {\n", + " \"spm_unit\": {\n", + " \"members\": [\"parent\", \"child\"]\n", + " }\n", + " },\n", + " \"households\": {\n", + " \"household\": {\n", + " \"members\": [\"parent\", \"child\"],\n", + " \"state_code\": {2025: \"SC\"}\n", + " }\n", + " }\n", + "}\n", + "\n", + "baseline_7 = Simulation(situation=situation_7)\n", + "reform_7 = Simulation(situation=situation_7, reform=create_reform())\n", + "\n", + "print(\"=\"*70)\n", + "print(\"EXAMPLE 7: Single Parent with 1 child, $0 income (no EITC)\")\n", + "print(\"=\"*70)\n", + "print(f\"\\nFederal EITC: ${baseline_7.calculate('eitc', 2025)[0]:,.2f}\")\n", + "print(f\"SC EITC (125% of federal): ${baseline_7.calculate('sc_eitc', 2025)[0]:,.2f}\")\n", + "print(f\"\\nBaseline (non-refundable):\")\n", + "print(f\" SC tax before non-refundable credits: ${baseline_7.calculate('sc_income_tax_before_non_refundable_credits', 2025)[0]:,.2f}\")\n", + "print(f\" SC non-refundable credits: ${baseline_7.calculate('sc_non_refundable_credits', 2025)[0]:,.2f}\")\n", + "print(f\" SC refundable credits: ${baseline_7.calculate('sc_refundable_credits', 2025)[0]:,.2f}\")\n", + "print(f\" SC income tax: ${baseline_7.calculate('sc_income_tax', 2025)[0]:,.2f}\")\n", + "print(f\" Household net income: ${baseline_7.calculate('household_net_income', 2025)[0]:,.2f}\")\n", + "print(f\"\\nReform (25% of excess refundable):\")\n", + "print(f\" SC non-refundable credits: ${reform_7.calculate('sc_non_refundable_credits', 2025)[0]:,.2f}\")\n", + "print(f\" SC refundable credits: ${reform_7.calculate('sc_refundable_credits', 2025)[0]:,.2f}\")\n", + "print(f\" SC income tax: ${reform_7.calculate('sc_income_tax', 2025)[0]:,.2f}\")\n", + "print(f\" Household net income: ${reform_7.calculate('household_net_income', 2025)[0]:,.2f}\")\n", + "print(f\"\\n>>> BENEFIT FROM REFORM: ${reform_7.calculate('household_net_income', 2025)[0] - baseline_7.calculate('household_net_income', 2025)[0]:,.2f}\")\n", + "print(\"\\n*** No EITC because EITC phases in with earned income ***\")" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Summary Table" + ] + }, + { + "cell_type": "code", + "execution_count": 10, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "====================================================================================================\n", + "SUMMARY: SC H.3492 Partially Refundable EITC Household Examples\n", + "====================================================================================================\n", + " Example Fed EITC SC EITC SC Tax Refund Benefit Status\n", + " 1: 1 child, $20k $4,328 $5,410 $0 $1,352 $1,352.50 PASS\n", + " 2: 1 child, $40k $1,667 $2,084 $240 $461 $461.15 FAIL\n", + "3: 2 children, $15k $6,000 $7,500 $0 $1,875 $1,875.00 PASS\n", + " 4: Childless, $12k $543 $679 $0 $170 $169.83 PASS\n", + " 5: 1 child, $10k $3,400 $4,250 $0 $1,062 $1,062.50 PASS\n", + "6: 3 children, $17k $7,650 $9,562 $0 $2,391 $2,390.62 PASS\n", + " 7: 1 child, $0 $0 $0 $0 $0 $0.00 PASS\n", + "====================================================================================================\n", + "\n", + "All tests passed: False\n", + "\n", + "----------------------------------------------------------------------\n", + "KEY INSIGHT - Refund Formula:\n", + "----------------------------------------------------------------------\n", + "Refundable portion = 25% × max(0, SC EITC - SC tax liability)\n", + "\n", + "Households benefit when their SC EITC exceeds their tax liability.\n", + "The lower the tax liability relative to the SC EITC, the larger the refund.\n", + "----------------------------------------------------------------------\n" + ] + } + ], + "source": [ + "examples = [\n", + " (\"1: 1 child, $20k\", baseline_1, reform_1, True),\n", + " (\"2: 1 child, $40k\", baseline_2, reform_2, False),\n", + " (\"3: 2 children, $15k\", baseline_3, reform_3, True),\n", + " (\"4: Childless, $12k\", baseline_4, reform_4, True),\n", + " (\"5: 1 child, $10k\", baseline_5, reform_5, True),\n", + " (\"6: 3 children, $17k\", baseline_6, reform_6, True),\n", + " (\"7: 1 child, $0\", baseline_7, reform_7, False),\n", + "]\n", + "\n", + "summary_data = []\n", + "for desc, baseline, reform, expected_benefit in examples:\n", + " baseline_income = baseline.calculate('household_net_income', 2025)[0]\n", + " reform_income = reform.calculate('household_net_income', 2025)[0]\n", + " benefit = reform_income - baseline_income\n", + " federal_eitc = baseline.calculate('eitc', 2025)[0]\n", + " sc_eitc = baseline.calculate('sc_eitc', 2025)[0]\n", + " sc_tax = baseline.calculate('sc_income_tax_before_non_refundable_credits', 2025)[0]\n", + " reform_refundable = reform.calculate('sc_refundable_credits', 2025)[0]\n", + " actual_benefit = benefit > 0.5\n", + " status = \"PASS\" if actual_benefit == expected_benefit else \"FAIL\"\n", + " \n", + " summary_data.append({\n", + " \"Example\": desc,\n", + " \"Fed EITC\": f\"${federal_eitc:,.0f}\",\n", + " \"SC EITC\": f\"${sc_eitc:,.0f}\",\n", + " \"SC Tax\": f\"${sc_tax:,.0f}\",\n", + " \"Refund\": f\"${reform_refundable:,.0f}\",\n", + " \"Benefit\": f\"${benefit:,.2f}\",\n", + " \"Status\": status,\n", + " })\n", + "\n", + "df_summary = pd.DataFrame(summary_data)\n", + "print(\"=\"*100)\n", + "print(\"SUMMARY: SC H.3492 Partially Refundable EITC Household Examples\")\n", + "print(\"=\"*100)\n", + "print(df_summary.to_string(index=False))\n", + "print(\"=\"*100)\n", + "\n", + "all_pass = all(row[\"Status\"] == \"PASS\" for row in summary_data)\n", + "print(f\"\\nAll tests passed: {all_pass}\")\n", + "\n", + "print(\"\\n\" + \"-\"*70)\n", + "print(\"KEY INSIGHT - Refund Formula:\")\n", + "print(\"-\"*70)\n", + "print(\"Refundable portion = 25% × max(0, SC EITC - SC tax liability)\")\n", + "print(\"\\nHouseholds benefit when their SC EITC exceeds their tax liability.\")\n", + "print(\"The lower the tax liability relative to the SC EITC, the larger the refund.\")\n", + "print(\"-\"*70)" + ] + }, + { + "cell_type": "code", + "execution_count": 11, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Summary table exported to: sc_h3492_household_summary.csv\n" + ] + } + ], + "source": [ + "# Export summary table to CSV\n", + "df_summary.to_csv(\"sc_h3492_household_summary.csv\", index=False)\n", + "print(f\"Summary table exported to: sc_h3492_household_summary.csv\")" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3", + "language": "python", + "name": "python3" + }, + "language_info": { + "name": "python", + "version": "3.11.5" + } + }, + "nbformat": 4, + "nbformat_minor": 4 +} diff --git a/us/states/sc/sc_h3492_household_summary.csv b/us/states/sc/sc_h3492_household_summary.csv new file mode 100644 index 0000000..6e1e516 --- /dev/null +++ b/us/states/sc/sc_h3492_household_summary.csv @@ -0,0 +1,8 @@ +Example,Fed EITC,SC EITC,SC Tax,Refund,Benefit,Status +"1: 1 child, $20k","$4,328","$5,410",$0,"$1,352","$1,352.50",PASS +"2: 1 child, $40k","$1,667","$2,084",$240,$461,$461.15,FAIL +"3: 2 children, $15k","$6,000","$7,500",$0,"$1,875","$1,875.00",PASS +"4: Childless, $12k",$543,$679,$0,$170,$169.83,PASS +"5: 1 child, $10k","$3,400","$4,250",$0,"$1,062","$1,062.50",PASS +"6: 3 children, $17k","$7,650","$9,562",$0,"$2,391","$2,390.62",PASS +"7: 1 child, $0",$0,$0,$0,$0,$0.00,PASS diff --git a/us/states/sc/sc_h3492_income_brackets.csv b/us/states/sc/sc_h3492_income_brackets.csv new file mode 100644 index 0000000..b09921d --- /dev/null +++ b/us/states/sc/sc_h3492_income_brackets.csv @@ -0,0 +1,6 @@ +Income Bracket,SC Households,Beneficiaries,% Benefiting,Total Benefit,Avg Benefit +Under $25k,"292,647","1,307",0.4%,$0.65M,$501 +$25k-$50k,"484,915","124,043",25.6%,$160.47M,"$1,294" +$50k-$75k,"511,027","33,115",6.5%,$18.94M,$572 +$75k-$100k,"500,246","54,216",10.8%,$83.17M,"$1,534" +Over $100k,"596,548","2,899",0.5%,$2.70M,$931 diff --git a/us/states/sc/sc_h3492_microsimulation.ipynb b/us/states/sc/sc_h3492_microsimulation.ipynb new file mode 100644 index 0000000..8d23106 --- /dev/null +++ b/us/states/sc/sc_h3492_microsimulation.ipynb @@ -0,0 +1,1764 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# SC H.3492 Partially Refundable EITC - Microsimulation Analysis\n", + "\n", + "This notebook calculates the aggregate impacts of South Carolina H.3492 using PolicyEngine microsimulation.\n", + "\n", + "## Reform Summary\n", + "- **Baseline**: SC EITC = 125% of federal EITC (non-refundable, limited by tax liability)\n", + "- **Reform**: Makes 25% of the excess SC EITC (above tax liability) refundable\n", + "\n", + "## Key Formula\n", + "- If SC EITC > tax liability:\n", + " - Non-refundable portion = tax liability\n", + " - Refundable portion = 25% × (SC EITC - tax liability)\n", + "\n", + "## Bill Reference\n", + "[SC H.3492 (126th Session, 2025-2026)](https://www.scstatehouse.gov/sess126_2025-2026/bills/3492.htm)" + ] + }, + { + "cell_type": "code", + "execution_count": 1, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "PolicyEngine SC H.3492 Analysis\n", + "==================================================\n" + ] + } + ], + "source": [ + "from policyengine_us import Microsimulation\n", + "from policyengine_us.reforms.states.sc.h3492.sc_h3492_eitc_refundable import sc_h3492_eitc_refundable\n", + "import pandas as pd\n", + "import numpy as np\n", + "import plotly.express as px\n", + "import plotly.graph_objects as go\n", + "from plotly.subplots import make_subplots\n", + "\n", + "# Set display options\n", + "pd.set_option('display.max_columns', None)\n", + "pd.set_option('display.width', None)\n", + "pd.set_option('display.max_rows', 20)\n", + "\n", + "# Dataset for analysis\n", + "ENHANCED_CPS = \"hf://policyengine/policyengine-us-data/enhanced_cps_2024.h5\"\n", + "\n", + "print(\"PolicyEngine SC H.3492 Analysis\")\n", + "print(\"=\"*50)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## 1. Setup Microsimulations" + ] + }, + { + "cell_type": "code", + "execution_count": 2, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Creating baseline microsimulation...\n", + "Creating reformed microsimulation with SC H.3492...\n", + "\n", + "Analysis year: 2025\n", + "Dataset: Enhanced CPS 2024\n" + ] + } + ], + "source": [ + "print(\"Creating baseline microsimulation...\")\n", + "baseline = Microsimulation(dataset=ENHANCED_CPS)\n", + "\n", + "print(\"Creating reformed microsimulation with SC H.3492...\")\n", + "reformed = Microsimulation(reform=sc_h3492_eitc_refundable, dataset=ENHANCED_CPS)\n", + "\n", + "YEAR = 2025\n", + "print(f\"\\nAnalysis year: {YEAR}\")\n", + "print(f\"Dataset: Enhanced CPS 2024\")" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## 2. Identify South Carolina Households\n", + "\n", + "Note: `.calculate()` returns MicroSeries which auto-weight on `.sum()`. We use `.values` to get raw numpy arrays when we need to do manual filtering/calculations." + ] + }, + { + "cell_type": "code", + "execution_count": 3, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "=== South Carolina Households ===\n", + "Records in dataset: 451\n", + "Weighted household count: 2,385,658\n" + ] + } + ], + "source": [ + "# Get state codes and weights as raw numpy arrays\n", + "state_code = baseline.calculate(\"state_code\", period=YEAR, map_to=\"household\").values\n", + "household_weight = baseline.calculate(\"household_weight\", period=YEAR).values\n", + "\n", + "# SC state code filter\n", + "sc_filter = state_code == \"SC\"\n", + "\n", + "# Count SC households (manual weighting since we're using raw arrays)\n", + "sc_households_weighted = household_weight[sc_filter].sum()\n", + "sc_households_records = sc_filter.sum()\n", + "\n", + "print(f\"=== South Carolina Households ===\")\n", + "print(f\"Records in dataset: {sc_households_records:,}\")\n", + "print(f\"Weighted household count: {sc_households_weighted:,.0f}\")" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## 3. Total Cost of the Reform" + ] + }, + { + "cell_type": "code", + "execution_count": 4, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "=== Total Cost of SC H.3492 ===\n", + "Annual cost to South Carolina: $265.94 million\n" + ] + } + ], + "source": [ + "# Calculate household net income under both scenarios (as raw arrays)\n", + "baseline_net_income = baseline.calculate(\"household_net_income\", period=YEAR).values\n", + "reformed_net_income = reformed.calculate(\"household_net_income\", period=YEAR).values\n", + "\n", + "# Calculate change in net income (benefit to households)\n", + "income_change = reformed_net_income - baseline_net_income\n", + "\n", + "# Filter to SC households and calculate total cost (weighted sum)\n", + "sc_income_change = income_change[sc_filter]\n", + "sc_weights = household_weight[sc_filter]\n", + "\n", + "# Total cost = weighted sum of increased benefits to SC households\n", + "total_cost = (sc_income_change * sc_weights).sum()\n", + "\n", + "print(f\"=== Total Cost of SC H.3492 ===\")\n", + "print(f\"Annual cost to South Carolina: ${total_cost/1e6:,.2f} million\")" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## 4. Number of Beneficiary Households" + ] + }, + { + "cell_type": "code", + "execution_count": 5, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "=== Beneficiary Households ===\n", + "Households benefiting: 215,580\n", + "Percentage of SC households: 9.0%\n", + "Records in dataset with benefit: 51\n" + ] + } + ], + "source": [ + "# Households that benefit are those with positive income change\n", + "beneficiaries_mask = (income_change > 0.5) & sc_filter # $0.50 threshold to avoid floating point issues\n", + "\n", + "# Count beneficiary households (weighted)\n", + "beneficiary_households = household_weight[beneficiaries_mask].sum()\n", + "beneficiary_records = beneficiaries_mask.sum()\n", + "\n", + "# Percentage of SC households that benefit\n", + "pct_benefiting = (beneficiary_households / sc_households_weighted) * 100\n", + "\n", + "print(f\"=== Beneficiary Households ===\")\n", + "print(f\"Households benefiting: {beneficiary_households:,.0f}\")\n", + "print(f\"Percentage of SC households: {pct_benefiting:.1f}%\")\n", + "print(f\"Records in dataset with benefit: {beneficiary_records:,}\")" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## 5. Average Benefit per Household" + ] + }, + { + "cell_type": "code", + "execution_count": 6, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "=== Average Benefit ===\n", + "Average benefit (all SC households): $111.47\n", + "Average benefit (beneficiaries only): $1,233.58\n" + ] + } + ], + "source": [ + "# Average benefit among all SC households\n", + "avg_benefit_all_sc = total_cost / sc_households_weighted\n", + "\n", + "# Average benefit among beneficiaries only\n", + "beneficiary_total = (income_change[beneficiaries_mask] * household_weight[beneficiaries_mask]).sum()\n", + "avg_benefit_beneficiaries = beneficiary_total / beneficiary_households\n", + "\n", + "print(f\"=== Average Benefit ===\")\n", + "print(f\"Average benefit (all SC households): ${avg_benefit_all_sc:,.2f}\")\n", + "print(f\"Average benefit (beneficiaries only): ${avg_benefit_beneficiaries:,.2f}\")" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## 6. Winners/Losers Analysis" + ] + }, + { + "cell_type": "code", + "execution_count": 7, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "=== Winners/Losers Analysis ===\n", + "Winners (gain > $0.50): 215,580 ( 9.0%)\n", + "Losers (loss > $0.50): 0 ( 0.0%)\n", + "Unchanged: 2,170,078 ( 91.0%)\n", + "Total SC households: 2,385,658\n", + "\n", + "*** As expected, this reform has no losers - it only adds a refundable credit ***\n" + ] + } + ], + "source": [ + "# Count winners (positive change), losers (negative change), and unchanged\n", + "winners_mask = (income_change > 0.5) & sc_filter\n", + "losers_mask = (income_change < -0.5) & sc_filter\n", + "unchanged_mask = (np.abs(income_change) <= 0.5) & sc_filter\n", + "\n", + "winners_count = household_weight[winners_mask].sum()\n", + "losers_count = household_weight[losers_mask].sum()\n", + "unchanged_count = household_weight[unchanged_mask].sum()\n", + "\n", + "print(f\"=== Winners/Losers Analysis ===\")\n", + "print(f\"Winners (gain > $0.50): {winners_count:>12,.0f} ({winners_count/sc_households_weighted*100:>5.1f}%)\")\n", + "print(f\"Losers (loss > $0.50): {losers_count:>12,.0f} ({losers_count/sc_households_weighted*100:>5.1f}%)\")\n", + "print(f\"Unchanged: {unchanged_count:>12,.0f} ({unchanged_count/sc_households_weighted*100:>5.1f}%)\")\n", + "print(f\"Total SC households: {sc_households_weighted:>12,.0f}\")\n", + "\n", + "# This reform should only have winners (or unchanged) - no losers expected\n", + "if losers_count < 100: # Allow for small floating point issues\n", + " print(\"\\n*** As expected, this reform has no losers - it only adds a refundable credit ***\")" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## 7. Impact by Income Bracket" + ] + }, + { + "cell_type": "code", + "execution_count": 8, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "=== Impact by Income Bracket ===\n", + "Income Bracket SC Households Beneficiaries % Benefiting Total Benefit Avg Benefit\n", + " Under $25k 292,647 1,307 0.4% $0.65M $501\n", + " $25k-$50k 484,915 124,043 25.6% $160.47M $1,294\n", + " $50k-$75k 511,027 33,115 6.5% $18.94M $572\n", + " $75k-$100k 500,246 54,216 10.8% $83.17M $1,534\n", + " Over $100k 596,548 2,899 0.5% $2.70M $931\n" + ] + } + ], + "source": [ + "# Get household income for bracketing (as raw array)\n", + "household_income = baseline.calculate(\"household_net_income\", period=YEAR).values\n", + "\n", + "# Define income brackets\n", + "brackets = [\n", + " (0, 25_000, \"Under $25k\"),\n", + " (25_000, 50_000, \"$25k-$50k\"),\n", + " (50_000, 75_000, \"$50k-$75k\"),\n", + " (75_000, 100_000, \"$75k-$100k\"),\n", + " (100_000, float('inf'), \"Over $100k\")\n", + "]\n", + "\n", + "bracket_data = []\n", + "for lower, upper, label in brackets:\n", + " # Filter to SC households in this bracket\n", + " bracket_mask = (household_income >= lower) & (household_income < upper) & sc_filter\n", + " \n", + " # Count households (weighted)\n", + " hh_count = household_weight[bracket_mask].sum()\n", + " \n", + " # Total benefit (weighted)\n", + " bracket_benefit = (income_change[bracket_mask] * household_weight[bracket_mask]).sum()\n", + " \n", + " # Beneficiaries in bracket\n", + " beneficiary_bracket = (income_change > 0.5) & bracket_mask\n", + " beneficiary_count = household_weight[beneficiary_bracket].sum()\n", + " \n", + " # Average benefit among beneficiaries\n", + " avg_benefit = bracket_benefit / beneficiary_count if beneficiary_count > 0 else 0\n", + " \n", + " bracket_data.append({\n", + " \"Income Bracket\": label,\n", + " \"SC Households\": f\"{hh_count:,.0f}\",\n", + " \"Beneficiaries\": f\"{beneficiary_count:,.0f}\",\n", + " \"% Benefiting\": f\"{beneficiary_count/hh_count*100:.1f}%\" if hh_count > 0 else \"0.0%\",\n", + " \"Total Benefit\": f\"${bracket_benefit/1e6:,.2f}M\",\n", + " \"Avg Benefit\": f\"${avg_benefit:,.0f}\"\n", + " })\n", + "\n", + "df_brackets = pd.DataFrame(bracket_data)\n", + "print(\"=== Impact by Income Bracket ===\")\n", + "print(df_brackets.to_string(index=False))" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## 8. Poverty Impact Analysis" + ] + }, + { + "cell_type": "code", + "execution_count": 9, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "=== Poverty Impact (Overall) ===\n", + "SC Population: 5,323,807\n", + "Baseline poverty rate: 15.53%\n", + "Reformed poverty rate: 15.07%\n", + "Change in poverty rate: -0.463 pp\n", + "Relative reduction: -2.98%\n", + "People lifted out of poverty: 24,661\n" + ] + } + ], + "source": [ + "# Calculate poverty status (as raw arrays)\n", + "baseline_in_poverty = baseline.calculate(\"in_poverty\", period=YEAR, map_to=\"person\").values.astype(float)\n", + "reformed_in_poverty = reformed.calculate(\"in_poverty\", period=YEAR, map_to=\"person\").values.astype(float)\n", + "person_weight = baseline.calculate(\"person_weight\", period=YEAR).values\n", + "\n", + "# Get state code at person level for filtering\n", + "person_state = baseline.calculate(\"state_code\", period=YEAR, map_to=\"person\").values\n", + "sc_person_filter = person_state == \"SC\"\n", + "\n", + "# Calculate poverty rates for SC\n", + "sc_person_weights = person_weight[sc_person_filter]\n", + "sc_total_people = sc_person_weights.sum()\n", + "\n", + "baseline_poverty_rate = (baseline_in_poverty[sc_person_filter] * sc_person_weights).sum() / sc_total_people\n", + "reformed_poverty_rate = (reformed_in_poverty[sc_person_filter] * sc_person_weights).sum() / sc_total_people\n", + "\n", + "poverty_rate_change = reformed_poverty_rate - baseline_poverty_rate\n", + "poverty_reduction_pct = (poverty_rate_change / baseline_poverty_rate) * 100 if baseline_poverty_rate > 0 else 0\n", + "\n", + "# People lifted out of poverty\n", + "people_lifted = (baseline_in_poverty[sc_person_filter] - reformed_in_poverty[sc_person_filter]) * sc_person_weights\n", + "people_lifted_count = people_lifted[people_lifted > 0].sum()\n", + "\n", + "print(f\"=== Poverty Impact (Overall) ===\")\n", + "print(f\"SC Population: {sc_total_people:,.0f}\")\n", + "print(f\"Baseline poverty rate: {baseline_poverty_rate*100:.2f}%\")\n", + "print(f\"Reformed poverty rate: {reformed_poverty_rate*100:.2f}%\")\n", + "print(f\"Change in poverty rate: {poverty_rate_change*100:+.3f} pp\")\n", + "print(f\"Relative reduction: {poverty_reduction_pct:+.2f}%\")\n", + "print(f\"People lifted out of poverty: {people_lifted_count:,.0f}\")" + ] + }, + { + "cell_type": "code", + "execution_count": 10, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "\n", + "=== Child Poverty Impact ===\n", + "SC Children (under 18): 1,133,734\n", + "Baseline child poverty rate: 12.12%\n", + "Reformed child poverty rate: 11.03%\n", + "Change in child poverty rate: -1.088 pp\n", + "Relative reduction: -8.97%\n", + "Children lifted out of poverty: 12,331\n" + ] + } + ], + "source": [ + "# Child poverty analysis\n", + "age = baseline.calculate(\"age\", period=YEAR).values\n", + "is_child = age < 18\n", + "\n", + "sc_child_filter = sc_person_filter & is_child\n", + "sc_child_weights = person_weight[sc_child_filter]\n", + "sc_total_children = sc_child_weights.sum()\n", + "\n", + "baseline_child_poverty_rate = (baseline_in_poverty[sc_child_filter] * sc_child_weights).sum() / sc_total_children\n", + "reformed_child_poverty_rate = (reformed_in_poverty[sc_child_filter] * sc_child_weights).sum() / sc_total_children\n", + "\n", + "child_poverty_change = reformed_child_poverty_rate - baseline_child_poverty_rate\n", + "child_poverty_reduction_pct = (child_poverty_change / baseline_child_poverty_rate) * 100 if baseline_child_poverty_rate > 0 else 0\n", + "\n", + "# Children lifted out of poverty\n", + "children_lifted = (baseline_in_poverty[sc_child_filter] - reformed_in_poverty[sc_child_filter]) * sc_child_weights\n", + "children_lifted_count = children_lifted[children_lifted > 0].sum()\n", + "\n", + "print(f\"\\n=== Child Poverty Impact ===\")\n", + "print(f\"SC Children (under 18): {sc_total_children:,.0f}\")\n", + "print(f\"Baseline child poverty rate: {baseline_child_poverty_rate*100:.2f}%\")\n", + "print(f\"Reformed child poverty rate: {reformed_child_poverty_rate*100:.2f}%\")\n", + "print(f\"Change in child poverty rate: {child_poverty_change*100:+.3f} pp\")\n", + "print(f\"Relative reduction: {child_poverty_reduction_pct:+.2f}%\")\n", + "print(f\"Children lifted out of poverty: {children_lifted_count:,.0f}\")" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## 9. Visualization: Impact by Income Bracket" + ] + }, + { + "cell_type": "code", + "execution_count": 11, + "metadata": {}, + "outputs": [ + { + "data": { + "application/vnd.plotly.v1+json": { + "config": { + "plotlyServerURL": "https://plot.ly" + }, + "data": [ + { + "marker": { + "color": "#2C6496" + }, + "name": "Total Benefit", + "type": "bar", + "x": [ + "Under $25k", + "$25k-$50k", + "$50k-$75k", + "$75k-$100k", + "Over $100k" + ], + "xaxis": "x", + "y": [ + 0.6544404, + 160.46896, + 18.944817, + 83.1688, + 2.6993296 + ], + "yaxis": "y" + }, + { + "marker": { + "color": "#4CAF50" + }, + "name": "Avg Benefit", + "type": "bar", + "x": [ + "Under $25k", + "$25k-$50k", + "$50k-$75k", + "$75k-$100k", + "Over $100k" + ], + "xaxis": "x2", + "y": [ + 500.65497, + 1293.6547, + 572.09546, + 1534.0271, + 931.0837 + ], + "yaxis": "y2" + }, + { + "marker": { + "color": "#FF9800" + }, + "name": "Beneficiaries", + "type": "bar", + "x": [ + "Under $25k", + "$25k-$50k", + "$50k-$75k", + "$75k-$100k", + "Over $100k" + ], + "xaxis": "x3", + "y": [ + 1307.1685, + 124043.12, + 33114.78, + 54215.992, + 2899.1267 + ], + "yaxis": "y3" + }, + { + "marker": { + "color": "#9C27B0" + }, + "name": "% Benefiting", + "type": "bar", + "x": [ + "Under $25k", + "$25k-$50k", + "$50k-$75k", + "$75k-$100k", + "Over $100k" + ], + "xaxis": "x4", + "y": [ + 0.4466705, + 25.580383, + 6.480042, + 10.837868, + 0.48598424 + ], + "yaxis": "y4" + } + ], + "layout": { + "annotations": [ + { + "font": { + "size": 16 + }, + "showarrow": false, + "text": "Total Benefit by Income Bracket", + "x": 0.225, + "xanchor": "center", + "xref": "paper", + "y": 1, + "yanchor": "bottom", + "yref": "paper" + }, + { + "font": { + "size": 16 + }, + "showarrow": false, + "text": "Average Benefit per Beneficiary", + "x": 0.775, + "xanchor": "center", + "xref": "paper", + "y": 1, + "yanchor": "bottom", + "yref": "paper" + }, + { + "font": { + "size": 16 + }, + "showarrow": false, + "text": "Number of Beneficiary Households", + "x": 0.225, + "xanchor": "center", + "xref": "paper", + "y": 0.375, + "yanchor": "bottom", + "yref": "paper" + }, + { + "font": { + "size": 16 + }, + "showarrow": false, + "text": "Percentage of Households Benefiting", + "x": 0.775, + "xanchor": "center", + "xref": "paper", + "y": 0.375, + "yanchor": "bottom", + "yref": "paper" + } + ], + "height": 700, + "showlegend": false, + "template": { + "data": { + "bar": [ + { + "error_x": { + "color": "#2a3f5f" + }, + "error_y": { + "color": "#2a3f5f" + }, + "marker": { + "line": { + "color": "#E5ECF6", + "width": 0.5 + }, + "pattern": { + "fillmode": "overlay", + "size": 10, + "solidity": 0.2 + } + }, + "type": "bar" + } + ], + "barpolar": [ + { + "marker": { + "line": { + "color": "#E5ECF6", + "width": 0.5 + }, + "pattern": { + "fillmode": "overlay", + "size": 10, + "solidity": 0.2 + } + }, + "type": "barpolar" + } + ], + "carpet": [ + { + "aaxis": { + "endlinecolor": "#2a3f5f", + "gridcolor": "white", + "linecolor": "white", + "minorgridcolor": "white", + "startlinecolor": "#2a3f5f" + }, + "baxis": { + "endlinecolor": "#2a3f5f", + "gridcolor": "white", + "linecolor": "white", + "minorgridcolor": "white", + "startlinecolor": "#2a3f5f" + }, + "type": "carpet" + } + ], + "choropleth": [ + { + "colorbar": { + "outlinewidth": 0, + "ticks": "" + }, + "type": "choropleth" + } + ], + "contour": [ + { + "colorbar": { + "outlinewidth": 0, + "ticks": "" + }, + "colorscale": [ + [ + 0, + "#0d0887" + ], + [ + 0.1111111111111111, + "#46039f" + ], + [ + 0.2222222222222222, + "#7201a8" + ], + [ + 0.3333333333333333, + "#9c179e" + ], + [ + 0.4444444444444444, + "#bd3786" + ], + [ + 0.5555555555555556, + "#d8576b" + ], + [ + 0.6666666666666666, + "#ed7953" + ], + [ + 0.7777777777777778, + "#fb9f3a" + ], + [ + 0.8888888888888888, + "#fdca26" + ], + [ + 1, + "#f0f921" + ] + ], + "type": "contour" + } + ], + "contourcarpet": [ + { + "colorbar": { + "outlinewidth": 0, + "ticks": "" + }, + "type": "contourcarpet" + } + ], + "heatmap": [ + { + "colorbar": { + "outlinewidth": 0, + "ticks": "" + }, + "colorscale": [ + [ + 0, + "#0d0887" + ], + [ + 0.1111111111111111, + "#46039f" + ], + [ + 0.2222222222222222, + "#7201a8" + ], + [ + 0.3333333333333333, + "#9c179e" + ], + [ + 0.4444444444444444, + "#bd3786" + ], + [ + 0.5555555555555556, + "#d8576b" + ], + [ + 0.6666666666666666, + "#ed7953" + ], + [ + 0.7777777777777778, + "#fb9f3a" + ], + [ + 0.8888888888888888, + "#fdca26" + ], + [ + 1, + "#f0f921" + ] + ], + "type": "heatmap" + } + ], + "heatmapgl": [ + { + "colorbar": { + "outlinewidth": 0, + "ticks": "" + }, + "colorscale": [ + [ + 0, + "#0d0887" + ], + [ + 0.1111111111111111, + "#46039f" + ], + [ + 0.2222222222222222, + "#7201a8" + ], + [ + 0.3333333333333333, + "#9c179e" + ], + [ + 0.4444444444444444, + "#bd3786" + ], + [ + 0.5555555555555556, + "#d8576b" + ], + [ + 0.6666666666666666, + "#ed7953" + ], + [ + 0.7777777777777778, + "#fb9f3a" + ], + [ + 0.8888888888888888, + "#fdca26" + ], + [ + 1, + "#f0f921" + ] + ], + "type": "heatmapgl" + } + ], + "histogram": [ + { + "marker": { + "pattern": { + "fillmode": "overlay", + "size": 10, + "solidity": 0.2 + } + }, + "type": "histogram" + } + ], + "histogram2d": [ + { + "colorbar": { + "outlinewidth": 0, + "ticks": "" + }, + "colorscale": [ + [ + 0, + "#0d0887" + ], + [ + 0.1111111111111111, + "#46039f" + ], + [ + 0.2222222222222222, + "#7201a8" + ], + [ + 0.3333333333333333, + "#9c179e" + ], + [ + 0.4444444444444444, + "#bd3786" + ], + [ + 0.5555555555555556, + "#d8576b" + ], + [ + 0.6666666666666666, + "#ed7953" + ], + [ + 0.7777777777777778, + "#fb9f3a" + ], + [ + 0.8888888888888888, + "#fdca26" + ], + [ + 1, + "#f0f921" + ] + ], + "type": "histogram2d" + } + ], + "histogram2dcontour": [ + { + "colorbar": { + "outlinewidth": 0, + "ticks": "" + }, + "colorscale": [ + [ + 0, + "#0d0887" + ], + [ + 0.1111111111111111, + "#46039f" + ], + [ + 0.2222222222222222, + "#7201a8" + ], + [ + 0.3333333333333333, + "#9c179e" + ], + [ + 0.4444444444444444, + "#bd3786" + ], + [ + 0.5555555555555556, + "#d8576b" + ], + [ + 0.6666666666666666, + "#ed7953" + ], + [ + 0.7777777777777778, + "#fb9f3a" + ], + [ + 0.8888888888888888, + "#fdca26" + ], + [ + 1, + "#f0f921" + ] + ], + "type": "histogram2dcontour" + } + ], + "mesh3d": [ + { + "colorbar": { + "outlinewidth": 0, + "ticks": "" + }, + "type": "mesh3d" + } + ], + "parcoords": [ + { + "line": { + "colorbar": { + "outlinewidth": 0, + "ticks": "" + } + }, + "type": "parcoords" + } + ], + "pie": [ + { + "automargin": true, + "type": "pie" + } + ], + "scatter": [ + { + "fillpattern": { + "fillmode": "overlay", + "size": 10, + "solidity": 0.2 + }, + "type": "scatter" + } + ], + "scatter3d": [ + { + "line": { + "colorbar": { + "outlinewidth": 0, + "ticks": "" + } + }, + "marker": { + "colorbar": { + "outlinewidth": 0, + "ticks": "" + } + }, + "type": "scatter3d" + } + ], + "scattercarpet": [ + { + "marker": { + "colorbar": { + "outlinewidth": 0, + "ticks": "" + } + }, + "type": "scattercarpet" + } + ], + "scattergeo": [ + { + "marker": { + "colorbar": { + "outlinewidth": 0, + "ticks": "" + } + }, + "type": "scattergeo" + } + ], + "scattergl": [ + { + "marker": { + "colorbar": { + "outlinewidth": 0, + "ticks": "" + } + }, + "type": "scattergl" + } + ], + "scattermapbox": [ + { + "marker": { + "colorbar": { + "outlinewidth": 0, + "ticks": "" + } + }, + "type": "scattermapbox" + } + ], + "scatterpolar": [ + { + "marker": { + "colorbar": { + "outlinewidth": 0, + "ticks": "" + } + }, + "type": "scatterpolar" + } + ], + "scatterpolargl": [ + { + "marker": { + "colorbar": { + "outlinewidth": 0, + "ticks": "" + } + }, + "type": "scatterpolargl" + } + ], + "scatterternary": [ + { + "marker": { + "colorbar": { + "outlinewidth": 0, + "ticks": "" + } + }, + "type": "scatterternary" + } + ], + "surface": [ + { + "colorbar": { + "outlinewidth": 0, + "ticks": "" + }, + "colorscale": [ + [ + 0, + "#0d0887" + ], + [ + 0.1111111111111111, + "#46039f" + ], + [ + 0.2222222222222222, + "#7201a8" + ], + [ + 0.3333333333333333, + "#9c179e" + ], + [ + 0.4444444444444444, + "#bd3786" + ], + [ + 0.5555555555555556, + "#d8576b" + ], + [ + 0.6666666666666666, + "#ed7953" + ], + [ + 0.7777777777777778, + "#fb9f3a" + ], + [ + 0.8888888888888888, + "#fdca26" + ], + [ + 1, + "#f0f921" + ] + ], + "type": "surface" + } + ], + "table": [ + { + "cells": { + "fill": { + "color": "#EBF0F8" + }, + "line": { + "color": "white" + } + }, + "header": { + "fill": { + "color": "#C8D4E3" + }, + "line": { + "color": "white" + } + }, + "type": "table" + } + ] + }, + "layout": { + "annotationdefaults": { + "arrowcolor": "#2a3f5f", + "arrowhead": 0, + "arrowwidth": 1 + }, + "autotypenumbers": "strict", + "coloraxis": { + "colorbar": { + "outlinewidth": 0, + "ticks": "" + } + }, + "colorscale": { + "diverging": [ + [ + 0, + "#8e0152" + ], + [ + 0.1, + "#c51b7d" + ], + [ + 0.2, + "#de77ae" + ], + [ + 0.3, + "#f1b6da" + ], + [ + 0.4, + "#fde0ef" + ], + [ + 0.5, + "#f7f7f7" + ], + [ + 0.6, + "#e6f5d0" + ], + [ + 0.7, + "#b8e186" + ], + [ + 0.8, + "#7fbc41" + ], + [ + 0.9, + "#4d9221" + ], + [ + 1, + "#276419" + ] + ], + "sequential": [ + [ + 0, + "#0d0887" + ], + [ + 0.1111111111111111, + "#46039f" + ], + [ + 0.2222222222222222, + "#7201a8" + ], + [ + 0.3333333333333333, + "#9c179e" + ], + [ + 0.4444444444444444, + "#bd3786" + ], + [ + 0.5555555555555556, + "#d8576b" + ], + [ + 0.6666666666666666, + "#ed7953" + ], + [ + 0.7777777777777778, + "#fb9f3a" + ], + [ + 0.8888888888888888, + "#fdca26" + ], + [ + 1, + "#f0f921" + ] + ], + "sequentialminus": [ + [ + 0, + "#0d0887" + ], + [ + 0.1111111111111111, + "#46039f" + ], + [ + 0.2222222222222222, + "#7201a8" + ], + [ + 0.3333333333333333, + "#9c179e" + ], + [ + 0.4444444444444444, + "#bd3786" + ], + [ + 0.5555555555555556, + "#d8576b" + ], + [ + 0.6666666666666666, + "#ed7953" + ], + [ + 0.7777777777777778, + "#fb9f3a" + ], + [ + 0.8888888888888888, + "#fdca26" + ], + [ + 1, + "#f0f921" + ] + ] + }, + "colorway": [ + "#636efa", + "#EF553B", + "#00cc96", + "#ab63fa", + "#FFA15A", + "#19d3f3", + "#FF6692", + "#B6E880", + "#FF97FF", + "#FECB52" + ], + "font": { + "color": "#2a3f5f" + }, + "geo": { + "bgcolor": "white", + "lakecolor": "white", + "landcolor": "#E5ECF6", + "showlakes": true, + "showland": true, + "subunitcolor": "white" + }, + "hoverlabel": { + "align": "left" + }, + "hovermode": "closest", + "mapbox": { + "style": "light" + }, + "paper_bgcolor": "white", + "plot_bgcolor": "#E5ECF6", + "polar": { + "angularaxis": { + "gridcolor": "white", + "linecolor": "white", + "ticks": "" + }, + "bgcolor": "#E5ECF6", + "radialaxis": { + "gridcolor": "white", + "linecolor": "white", + "ticks": "" + } + }, + "scene": { + "xaxis": { + "backgroundcolor": "#E5ECF6", + "gridcolor": "white", + "gridwidth": 2, + "linecolor": "white", + "showbackground": true, + "ticks": "", + "zerolinecolor": "white" + }, + "yaxis": { + "backgroundcolor": "#E5ECF6", + "gridcolor": "white", + "gridwidth": 2, + "linecolor": "white", + "showbackground": true, + "ticks": "", + "zerolinecolor": "white" + }, + "zaxis": { + "backgroundcolor": "#E5ECF6", + "gridcolor": "white", + "gridwidth": 2, + "linecolor": "white", + "showbackground": true, + "ticks": "", + "zerolinecolor": "white" + } + }, + "shapedefaults": { + "line": { + "color": "#2a3f5f" + } + }, + "ternary": { + "aaxis": { + "gridcolor": "white", + "linecolor": "white", + "ticks": "" + }, + "baxis": { + "gridcolor": "white", + "linecolor": "white", + "ticks": "" + }, + "bgcolor": "#E5ECF6", + "caxis": { + "gridcolor": "white", + "linecolor": "white", + "ticks": "" + } + }, + "title": { + "x": 0.05 + }, + "xaxis": { + "automargin": true, + "gridcolor": "white", + "linecolor": "white", + "ticks": "", + "title": { + "standoff": 15 + }, + "zerolinecolor": "white", + "zerolinewidth": 2 + }, + "yaxis": { + "automargin": true, + "gridcolor": "white", + "linecolor": "white", + "ticks": "", + "title": { + "standoff": 15 + }, + "zerolinecolor": "white", + "zerolinewidth": 2 + } + } + }, + "title": { + "text": "SC H.3492 Impact by Income Bracket" + }, + "xaxis": { + "anchor": "y", + "domain": [ + 0, + 0.45 + ] + }, + "xaxis2": { + "anchor": "y2", + "domain": [ + 0.55, + 1 + ] + }, + "xaxis3": { + "anchor": "y3", + "domain": [ + 0, + 0.45 + ] + }, + "xaxis4": { + "anchor": "y4", + "domain": [ + 0.55, + 1 + ] + }, + "yaxis": { + "anchor": "x", + "domain": [ + 0.625, + 1 + ], + "title": { + "text": "$ Millions" + } + }, + "yaxis2": { + "anchor": "x2", + "domain": [ + 0.625, + 1 + ], + "title": { + "text": "$ per Household" + } + }, + "yaxis3": { + "anchor": "x3", + "domain": [ + 0, + 0.375 + ], + "title": { + "text": "Households" + } + }, + "yaxis4": { + "anchor": "x4", + "domain": [ + 0, + 0.375 + ], + "title": { + "text": "Percent" + } + } + } + } + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "# Prepare data for visualization\n", + "viz_data = []\n", + "for lower, upper, label in brackets:\n", + " bracket_mask = (household_income >= lower) & (household_income < upper) & sc_filter\n", + " hh_count = household_weight[bracket_mask].sum()\n", + " bracket_benefit = (income_change[bracket_mask] * household_weight[bracket_mask]).sum()\n", + " beneficiary_bracket = (income_change > 0.5) & bracket_mask\n", + " beneficiary_count = household_weight[beneficiary_bracket].sum()\n", + " avg_benefit = bracket_benefit / beneficiary_count if beneficiary_count > 0 else 0\n", + " \n", + " viz_data.append({\n", + " \"bracket\": label,\n", + " \"total_benefit_millions\": bracket_benefit / 1e6,\n", + " \"avg_benefit\": avg_benefit,\n", + " \"beneficiaries\": beneficiary_count,\n", + " \"pct_benefiting\": beneficiary_count / hh_count * 100 if hh_count > 0 else 0\n", + " })\n", + "\n", + "viz_df = pd.DataFrame(viz_data)\n", + "\n", + "# Create subplot figure\n", + "fig = make_subplots(\n", + " rows=2, cols=2,\n", + " subplot_titles=(\n", + " 'Total Benefit by Income Bracket',\n", + " 'Average Benefit per Beneficiary',\n", + " 'Number of Beneficiary Households',\n", + " 'Percentage of Households Benefiting'\n", + " )\n", + ")\n", + "\n", + "# Total benefit\n", + "fig.add_trace(\n", + " go.Bar(x=viz_df['bracket'], y=viz_df['total_benefit_millions'],\n", + " marker_color='#2C6496', name='Total Benefit'),\n", + " row=1, col=1\n", + ")\n", + "\n", + "# Average benefit\n", + "fig.add_trace(\n", + " go.Bar(x=viz_df['bracket'], y=viz_df['avg_benefit'],\n", + " marker_color='#4CAF50', name='Avg Benefit'),\n", + " row=1, col=2\n", + ")\n", + "\n", + "# Beneficiaries\n", + "fig.add_trace(\n", + " go.Bar(x=viz_df['bracket'], y=viz_df['beneficiaries'],\n", + " marker_color='#FF9800', name='Beneficiaries'),\n", + " row=2, col=1\n", + ")\n", + "\n", + "# Percent benefiting\n", + "fig.add_trace(\n", + " go.Bar(x=viz_df['bracket'], y=viz_df['pct_benefiting'],\n", + " marker_color='#9C27B0', name='% Benefiting'),\n", + " row=2, col=2\n", + ")\n", + "\n", + "# Update layout\n", + "fig.update_layout(\n", + " height=700,\n", + " showlegend=False,\n", + " title_text=\"SC H.3492 Impact by Income Bracket\"\n", + ")\n", + "\n", + "fig.update_yaxes(title_text=\"$ Millions\", row=1, col=1)\n", + "fig.update_yaxes(title_text=\"$ per Household\", row=1, col=2)\n", + "fig.update_yaxes(title_text=\"Households\", row=2, col=1)\n", + "fig.update_yaxes(title_text=\"Percent\", row=2, col=2)\n", + "\n", + "fig.show()" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## 10. Summary Statistics" + ] + }, + { + "cell_type": "code", + "execution_count": 12, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "======================================================================\n", + "SC H.3492 PARTIALLY REFUNDABLE EITC - SUMMARY\n", + "======================================================================\n", + "\n", + "FISCAL IMPACT:\n", + " Annual cost: $265.94 million\n", + "\n", + "BENEFICIARY ANALYSIS:\n", + " SC households benefiting: 215,580\n", + " Percent of SC households: 9.0%\n", + " Average benefit (beneficiaries): $1,233.58\n", + "\n", + "POVERTY IMPACT:\n", + " Overall poverty rate change: -0.463 pp\n", + " Child poverty rate change: -1.088 pp\n", + " People lifted out of poverty: 24,661\n", + " Children lifted out of poverty: 12,331\n", + "\n", + "KEY INSIGHT:\n", + " This reform primarily benefits low-income working families\n", + " whose SC EITC exceeds their state tax liability.\n", + "======================================================================\n" + ] + } + ], + "source": [ + "print(\"=\"*70)\n", + "print(\"SC H.3492 PARTIALLY REFUNDABLE EITC - SUMMARY\")\n", + "print(\"=\"*70)\n", + "print(f\"\\nFISCAL IMPACT:\")\n", + "print(f\" Annual cost: ${total_cost/1e6:,.2f} million\")\n", + "print(f\"\\nBENEFICIARY ANALYSIS:\")\n", + "print(f\" SC households benefiting: {beneficiary_households:,.0f}\")\n", + "print(f\" Percent of SC households: {pct_benefiting:.1f}%\")\n", + "print(f\" Average benefit (beneficiaries): ${avg_benefit_beneficiaries:,.2f}\")\n", + "print(f\"\\nPOVERTY IMPACT:\")\n", + "print(f\" Overall poverty rate change: {poverty_rate_change*100:+.3f} pp\")\n", + "print(f\" Child poverty rate change: {child_poverty_change*100:+.3f} pp\")\n", + "print(f\" People lifted out of poverty: {people_lifted_count:,.0f}\")\n", + "print(f\" Children lifted out of poverty: {children_lifted_count:,.0f}\")\n", + "print(f\"\\nKEY INSIGHT:\")\n", + "print(f\" This reform primarily benefits low-income working families\")\n", + "print(f\" whose SC EITC exceeds their state tax liability.\")\n", + "print(\"=\"*70)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## 11. Export Results" + ] + }, + { + "cell_type": "code", + "execution_count": 13, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Summary Statistics:\n", + " Metric Value\n", + " Annual Cost ($ millions) 265.94\n", + " Beneficiary Households 215,580\n", + " Percent of SC Households Benefiting 9.0%\n", + " Average Benefit (all SC households) $111.47\n", + "Average Benefit (beneficiaries only) $1233.58\n", + " Overall Poverty Rate Change (pp) -0.463\n", + " Child Poverty Rate Change (pp) -1.088\n", + " People Lifted Out of Poverty 24,661\n", + " Children Lifted Out of Poverty 12,331\n", + "\n", + "Exported files:\n", + " - sc_h3492_summary_stats.csv\n", + " - sc_h3492_income_brackets.csv\n" + ] + } + ], + "source": [ + "# Create summary DataFrame\n", + "summary_stats = {\n", + " \"Metric\": [\n", + " \"Annual Cost ($ millions)\",\n", + " \"Beneficiary Households\",\n", + " \"Percent of SC Households Benefiting\",\n", + " \"Average Benefit (all SC households)\",\n", + " \"Average Benefit (beneficiaries only)\",\n", + " \"Overall Poverty Rate Change (pp)\",\n", + " \"Child Poverty Rate Change (pp)\",\n", + " \"People Lifted Out of Poverty\",\n", + " \"Children Lifted Out of Poverty\"\n", + " ],\n", + " \"Value\": [\n", + " f\"{total_cost/1e6:.2f}\",\n", + " f\"{beneficiary_households:,.0f}\",\n", + " f\"{pct_benefiting:.1f}%\",\n", + " f\"${avg_benefit_all_sc:.2f}\",\n", + " f\"${avg_benefit_beneficiaries:.2f}\",\n", + " f\"{poverty_rate_change*100:+.3f}\",\n", + " f\"{child_poverty_change*100:+.3f}\",\n", + " f\"{people_lifted_count:,.0f}\",\n", + " f\"{children_lifted_count:,.0f}\"\n", + " ]\n", + "}\n", + "\n", + "df_summary = pd.DataFrame(summary_stats)\n", + "print(\"Summary Statistics:\")\n", + "print(df_summary.to_string(index=False))\n", + "\n", + "# Export to CSV\n", + "df_summary.to_csv(\"sc_h3492_summary_stats.csv\", index=False)\n", + "df_brackets.to_csv(\"sc_h3492_income_brackets.csv\", index=False)\n", + "\n", + "print(\"\\nExported files:\")\n", + "print(\" - sc_h3492_summary_stats.csv\")\n", + "print(\" - sc_h3492_income_brackets.csv\")" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3", + "language": "python", + "name": "python3" + }, + "language_info": { + "name": "python", + "version": "3.11.5" + } + }, + "nbformat": 4, + "nbformat_minor": 4 +} diff --git a/us/states/sc/sc_h3492_summary_stats.csv b/us/states/sc/sc_h3492_summary_stats.csv new file mode 100644 index 0000000..92dedfe --- /dev/null +++ b/us/states/sc/sc_h3492_summary_stats.csv @@ -0,0 +1,10 @@ +Metric,Value +Annual Cost ($ millions),265.94 +Beneficiary Households,"215,580" +Percent of SC Households Benefiting,9.0% +Average Benefit (all SC households),$111.47 +Average Benefit (beneficiaries only),$1233.58 +Overall Poverty Rate Change (pp),-0.463 +Child Poverty Rate Change (pp),-1.088 +People Lifted Out of Poverty,"24,661" +Children Lifted Out of Poverty,"12,331"