From cf20566c0ddb278eca2e140866dd4ad2a4ccd582 Mon Sep 17 00:00:00 2001 From: PavelMakarchuk Date: Thu, 10 Jul 2025 15:51:26 +0200 Subject: [PATCH 1/6] Household simulation notebook --- educational/household_analysis.csv | 2 + educational/household_simulation.ipynb | 9563 ++++++++++++++++++++++++ 2 files changed, 9565 insertions(+) create mode 100644 educational/household_analysis.csv create mode 100644 educational/household_simulation.ipynb diff --git a/educational/household_analysis.csv b/educational/household_analysis.csv new file mode 100644 index 0000000..9fa3461 --- /dev/null +++ b/educational/household_analysis.csv @@ -0,0 +1,2 @@ +employment_income,adjusted_gross_income,taxable_income,income_tax,ctc_value,eitc,snap,household_net_income,household_id,scenario +120000.0,120000.0,90000.0,6323.0,4000.0,0.0,0.0,102116.85,household,baseline diff --git a/educational/household_simulation.ipynb b/educational/household_simulation.ipynb new file mode 100644 index 0000000..d7895f1 --- /dev/null +++ b/educational/household_simulation.ipynb @@ -0,0 +1,9563 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "id": "4bfc90c5", + "metadata": {}, + "source": [ + "# Notebook 1: Household Simulation with PolicyEngine\n" + ] + }, + { + "cell_type": "markdown", + "id": "2d86287e", + "metadata": {}, + "source": [ + "## Learning Objectives\n", + "By the end of this notebook, you will be able to:\n", + "1. **Understand PolicyEngine's core architecture** - entities, variables, parameters, and periods\n", + "2. **Create household situations** using the proper entity structure and relationships\n", + "3. **Run baseline simulations** and interpret variable calculations and dependencies\n", + "4. **Design and implement policy reforms** using parametric changes\n", + "5. **Compare baseline vs. reform scenarios** to quantify policy impacts\n", + "6. **Use advanced simulation features** like variable tracing and earnings variation analysis\n", + "7. **Work with PolicyEngine's data structures** including arrays, DataFrames, and aggregation methods\n" + ] + }, + { + "cell_type": "markdown", + "id": "742f494a", + "metadata": {}, + "source": [ + "## Introduction\n", + "This notebook demonstrates the core concepts of PolicyEngine's Python package by simulating household situations. We'll start with a baseline scenario, add a reform, and compare the results." + ] + }, + { + "cell_type": "code", + "execution_count": 1, + "id": "a46899e7", + "metadata": {}, + "outputs": [ + { + "name": "stderr", + "output_type": "stream", + "text": [ + "/Users/pavelmakarchuk/anaconda3/envs/pe/lib/python3.10/site-packages/tqdm/auto.py:21: TqdmWarning: IProgress not found. Please update jupyter and ipywidgets. See https://ipywidgets.readthedocs.io/en/stable/user_install.html\n", + " from .autonotebook import tqdm as notebook_tqdm\n" + ] + } + ], + "source": [ + "# Import necessary libraries\n", + "from policyengine_us import Simulation\n", + "from policyengine_core.reforms import Reform\n", + "import pandas as pd\n", + "import numpy as np" + ] + }, + { + "cell_type": "markdown", + "id": "39287e17", + "metadata": {}, + "source": [ + "## Part 1: Creating a Household Situation\n", + "\n", + "### Understanding Core Concepts\n", + "Before we begin, let's understand the key components:\n", + "- **Entities**: Person, tax unit, SPM unit, marital unit, family, household\n", + "- **Variables**: Can be inputs (employment_income) or calculations (ctc_value)\n", + "- **Parameters**: Policy features like tax rates, benefit amounts\n", + "- **Periods**: Time periods for which we calculate values" + ] + }, + { + "cell_type": "markdown", + "id": "c4a573c4", + "metadata": {}, + "source": [ + "### PolicyEngine Repositories\n", + "To explore the source code and understand how variables and parameters are defined:\n", + "- **[policyengine-us](https://github.com/PolicyEngine/policyengine-us)**: Contains all US-specific variables, parameters, and policy logic\n", + " - Variables: `/policyengine_us/variables/`\n", + " - Parameters: `/policyengine_us/parameters/`\n", + "- **[policyengine-core](https://github.com/PolicyEngine/policyengine-core)**: Contains the core simulation frameworks\n", + " - `Simulation` class: `/policyengine_core/simulations/simulation.py`\n", + " - `Microsimulation` class: `/policyengine_core/simulations/microsimulation.py`" + ] + }, + { + "cell_type": "markdown", + "id": "8bcf0214", + "metadata": {}, + "source": [ + "### Getting Started: Using PolicyEngine's Web Interface\n", + "The easiest way to create a household situation is to use PolicyEngine's web interface:\n", + "1. Go to https://policyengine.org/us/household\n", + "2. Enter your household details using the interactive form\n", + "3. Navigate to the \"Reproduce in Python\" section\n", + "4. Copy the generated code\n", + "\n", + "For example, after creating a household at:\n", + "https://policyengine.org/us/household?focus=householdOutput.pythonReproducibility&household=54688\n", + "\n", + "You'll find ready-to-use Python code that creates the situation dictionary for you!" + ] + }, + { + "cell_type": "code", + "execution_count": 2, + "id": "725bb985", + "metadata": {}, + "outputs": [], + "source": [ + "# Create a household situation dictionary\n", + "# This represents a family of 4: 2 adults and 2 children in California\n", + "\n", + "situation = {\n", + " \"people\": {\n", + " \"parent1\": {\n", + " \"age\": {\"2025\": 35},\n", + " \"employment_income\": {\"2025\": 75000}\n", + " },\n", + " \"parent2\": {\n", + " \"age\": {\"2025\": 33},\n", + " \"employment_income\": {\"2025\": 45000}\n", + " },\n", + " \"child1\": {\n", + " \"age\": {\"2025\": 10}\n", + " },\n", + " \"child2\": {\n", + " \"age\": {\"2025\": 7}\n", + " }\n", + " },\n", + " \"families\": {\n", + " \"family\": {\n", + " \"members\": [\"parent1\", \"parent2\", \"child1\", \"child2\"]\n", + " }\n", + " },\n", + " \"marital_units\": {\n", + " \"parents\": {\n", + " \"members\": [\"parent1\", \"parent2\"]\n", + " },\n", + " \"child1_marital_unit\": {\n", + " \"members\": [\"child1\"],\n", + " \"marital_unit_id\": {\"2025\": 1}\n", + " },\n", + " \"child2_marital_unit\": {\n", + " \"members\": [\"child2\"],\n", + " \"marital_unit_id\": {\"2025\": 2}\n", + " }\n", + " },\n", + " \"tax_units\": {\n", + " \"tax_unit\": {\n", + " \"members\": [\"parent1\", \"parent2\", \"child1\", \"child2\"]\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\": \"CA\"}\n", + " }\n", + " }\n", + "}" + ] + }, + { + "cell_type": "markdown", + "id": "3b518ab2", + "metadata": {}, + "source": [ + "## Part 2: Running a Baseline Simulation\n" + ] + }, + { + "cell_type": "code", + "execution_count": 3, + "id": "3257517a", + "metadata": {}, + "outputs": [], + "source": [ + "# Create a simulation object with our household situation\n", + "baseline_sim = Simulation(situation=situation)" + ] + }, + { + "cell_type": "code", + "execution_count": 4, + "id": "dd0c97dd", + "metadata": {}, + "outputs": [], + "source": [ + "# Calculate key variables for 2025\n", + "PERIOD = 2025" + ] + }, + { + "cell_type": "code", + "execution_count": 5, + "id": "bd5ebe5c", + "metadata": {}, + "outputs": [], + "source": [ + "# Income-related variables\n", + "employment_income = baseline_sim.calculate(\"employment_income\", PERIOD)\n", + "adjusted_gross_income = baseline_sim.calculate(\"adjusted_gross_income\", PERIOD)" + ] + }, + { + "cell_type": "code", + "execution_count": 6, + "id": "c049a19b", + "metadata": {}, + "outputs": [], + "source": [ + "# Tax-related variables\n", + "income_tax = baseline_sim.calculate(\"income_tax\", PERIOD)\n", + "ctc = baseline_sim.calculate(\"ctc_value\", PERIOD)\n", + "eitc = baseline_sim.calculate(\"eitc\", PERIOD)" + ] + }, + { + "cell_type": "code", + "execution_count": 7, + "id": "d89b98d0", + "metadata": {}, + "outputs": [], + "source": [ + "# Benefits\n", + "snap = baseline_sim.calculate(\"snap\", PERIOD)" + ] + }, + { + "cell_type": "code", + "execution_count": 8, + "id": "de0db305", + "metadata": {}, + "outputs": [], + "source": [ + "# Net income\n", + "household_net_income = baseline_sim.calculate(\"household_net_income\", PERIOD)" + ] + }, + { + "cell_type": "markdown", + "id": "bbcbe356", + "metadata": {}, + "source": [ + "### Understanding the calculate() Return Value\n", + "**IMPORTANT**: The calculate() method returns a NumPy array with values for each entity\n", + "- For person-level variables: array length = number of people (4 in our case)\n", + "- For household-level variables: array length = number of households (1 in our case)" + ] + }, + { + "cell_type": "code", + "execution_count": 9, + "id": "47f49cc7", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "PERSON-LEVEL VARIABLE (employment_income):\n", + " Array: [75000. 45000. 0. 0.]\n", + " Shape: (4,)\n", + " Length: 4 (one value per person)\n", + " Individual values: Parent1=$75,000, Parent2=$45,000, Child1=$0, Child2=$0\n" + ] + } + ], + "source": [ + "# Person-level variable: employment_income\n", + "print(\"PERSON-LEVEL VARIABLE (employment_income):\")\n", + "print(f\" Array: {employment_income}\")\n", + "print(f\" Shape: {employment_income.shape}\")\n", + "print(f\" Length: {len(employment_income)} (one value per person)\")\n", + "print(f\" Individual values: Parent1=${employment_income[0]:,.0f}, Parent2=${employment_income[1]:,.0f}, Child1=${employment_income[2]:,.0f}, Child2=${employment_income[3]:,.0f}\")" + ] + }, + { + "cell_type": "markdown", + "id": "cbfd834a", + "metadata": {}, + "source": [ + "#### Built-in Functions vs. Methods vs. Properties\n", + "Python provides different ways to interact with objects:\n", + "\n", + "| Type | Syntax | Description | Example |\n", + "|------|--------|-------------|---------|\n", + "| **Built-in function** | `function(object)` | Python's built-in functions | `len(array)`, `sum(array)`, `max(array)` |\n", + "| **Method** | `object.method()` | Functions that belong to the object (note parentheses) | `array.sum()`, `array.mean()`, `array.max()` |\n", + "| **Property/Attribute** | `object.property` | Data that belongs to the object (no parentheses) | `array.shape`, `array.dtype`, `array.size` |\n" + ] + }, + { + "cell_type": "markdown", + "id": "447383df", + "metadata": {}, + "source": [ + "#### Understanding Shape\n", + "The `shape` property tells you the dimensions of an array:\n", + "- **1D array**: `(n,)` where n is the number of elements\n", + "- **2D array**: `(rows, columns)`\n", + "- **Higher dimensions**: `(dim1, dim2, dim3, ...)`" + ] + }, + { + "cell_type": "code", + "execution_count": 10, + "id": "059949c5", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "\n", + "HOUSEHOLD-LEVEL VARIABLE (household_net_income):\n", + " Array: [102116.85]\n", + " Shape: (1,)\n", + " Length: 1 (one value per household)\n", + " Value: $102,117\n" + ] + } + ], + "source": [ + "# Household-level variable: household_net_income\n", + "print(\"\\nHOUSEHOLD-LEVEL VARIABLE (household_net_income):\")\n", + "print(f\" Array: {household_net_income}\")\n", + "print(f\" Shape: {household_net_income.shape}\")\n", + "print(f\" Length: {len(household_net_income)} (one value per household)\")\n", + "print(f\" Value: ${household_net_income[0]:,.0f}\")" + ] + }, + { + "cell_type": "code", + "execution_count": 11, + "id": "848a47c6", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "\n", + "Using .sum():\n", + " employment_income.sum() = $120,000 (adds all 4 person values)\n", + " household_net_income.sum() = $102,117 (just the 1 household value)\n" + ] + } + ], + "source": [ + "# This demonstrates why we use .sum() - it works correctly regardless of entity level\n", + "print(f\"\\nUsing .sum():\")\n", + "print(f\" employment_income.sum() = ${employment_income.sum():,.0f} (adds all 4 person values)\")\n", + "print(f\" household_net_income.sum() = ${household_net_income.sum():,.0f} (just the 1 household value)\")" + ] + }, + { + "cell_type": "markdown", + "id": "b40fe4d8", + "metadata": {}, + "source": [ + "### Common Array Operations\n", + "- `.sum()` - adds all values in the array\n", + "- `.mean()` - calculates the average\n", + "- `.max()` - finds the maximum value\n", + "- `.min()` - finds the minimum value\n", + "- `[index]` - accesses individual elements\n" + ] + }, + { + "cell_type": "code", + "execution_count": 12, + "id": "9e222d38", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "\n", + "=== BASELINE RESULTS ===\n", + "Total Employment Income: $120,000\n", + "Average Employment Income per Person: $30,000\n", + "Adjusted Gross Income: $120,000\n", + "Federal Income Tax: $6,323\n", + "SNAP Benefits: $0\n", + "Child Tax Credit: $4,000\n", + "Earned Income Tax Credit: $0\n", + "Household Net Income: $102,117\n" + ] + } + ], + "source": [ + "print(\"\\n=== BASELINE RESULTS ===\")\n", + "print(f\"Total Employment Income: ${employment_income.sum():,.0f}\")\n", + "print(f\"Average Employment Income per Person: ${employment_income.mean():,.0f}\")\n", + "print(f\"Adjusted Gross Income: ${adjusted_gross_income.sum():,.0f}\")\n", + "print(f\"Federal Income Tax: ${income_tax.sum():,.0f}\")\n", + "print(f\"SNAP Benefits: ${snap.sum():,.0f}\")\n", + "print(f\"Child Tax Credit: ${ctc.sum():,.0f}\")\n", + "print(f\"Earned Income Tax Credit: ${eitc.sum():,.0f}\")\n", + "print(f\"Household Net Income: ${household_net_income.sum():,.0f}\")" + ] + }, + { + "cell_type": "markdown", + "id": "37207a96", + "metadata": {}, + "source": [ + "## Part 3: Understanding Variable Calculation Flow\n" + ] + }, + { + "cell_type": "markdown", + "id": "1e357053", + "metadata": {}, + "source": [ + "xxx - purpose of trace" + ] + }, + { + "cell_type": "code", + "execution_count": 13, + "id": "e63152d0", + "metadata": {}, + "outputs": [], + "source": [ + "# Initialize a new simulation for tracing\n", + "sim_for_trace = Simulation(situation=situation)" + ] + }, + { + "cell_type": "code", + "execution_count": 14, + "id": "0c02d2b7", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "\n", + "=== CTC CALCULATION TRACE ===\n", + " ctc_value<2025, (default)> = [4000.]\n", + " ctc<2025, (default)> = [4000.]\n", + " ctc_maximum_with_arpa_addition<2025, (default)> = [4000.]\n", + " ctc_phase_out<2025, (default)> = [0.]\n", + " ctc_phase_in<2025, (default)> = [17625.]\n", + " tax_unit_earned_income<2025, (default)> = [120000.]\n", + " ctc_social_security_tax<2025, (default)> = [9180.]\n", + " eitc<2025, (default)> = [0.]\n", + " ctc_qualifying_children<2025, (default)> = [2]\n" + ] + } + ], + "source": [ + "# Let's trace how the Child Tax Credit is calculated\n", + "# This demonstrates the dependency tree of variables\n", + "\n", + "sim_for_trace.trace = True\n", + "sim_for_trace.calculate(\"ctc_value\", period=PERIOD)\n", + "# Now let's see the trace\n", + "print(\"\\n=== CTC CALCULATION TRACE ===\")\n", + "sim_for_trace.tracer.print_computation_log(max_depth=3)\n" + ] + }, + { + "cell_type": "markdown", + "id": "0d6ebb92", + "metadata": {}, + "source": [ + "### Anatomy of a Trace Line\n", + "Let's break down a typical trace line:\n", + "```\n", + "ctc_value<2025, (default)> = [4000.]\n", + "```\n", + "\n", + "| Component | Explanation |\n", + "|-----------|-------------|\n", + "| **ctc_value** | Variable name - the Child Tax Credit value |\n", + "| **<2025** | Period - the year this calculation applies to |\n", + "| **(default)** | Branch - calculation branch (ignore for now - used for advanced scenarios) |\n", + "| **= [4000.]** | Array of values - the calculated results |" + ] + }, + { + "cell_type": "markdown", + "id": "f4235278", + "metadata": {}, + "source": [ + "Variable calculation from the -us repository:\n", + "\n", + "https://github.com/PolicyEngine/policyengine-us/blob/master/policyengine_us/variables/gov/irs/credits/ctc/ctc_value.py" + ] + }, + { + "cell_type": "code", + "execution_count": 15, + "id": "5393ac49", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "\n", + "=== CTC CALCULATION TRACE DEPTH = 4 ===\n", + " ctc_value<2025, (default)> = [4000.]\n", + " ctc<2025, (default)> = [4000.]\n", + " ctc_maximum_with_arpa_addition<2025, (default)> = [4000.]\n", + " ctc_maximum<2025, (default)> = [4000.]\n", + " ctc_arpa_addition<2025, (default)> = [0.]\n", + " ctc_phase_out<2025, (default)> = [0.]\n", + " adjusted_gross_income<2025, (default)> = [120000.]\n", + " ctc_phase_out_threshold<2025, (default)> = [400000.]\n", + " ctc_phase_in<2025, (default)> = [17625.]\n", + " tax_unit_earned_income<2025, (default)> = [120000.]\n", + " earned_income<2025, (default)> = [75000. 45000. 0. 0.]\n", + " is_tax_unit_dependent<2025, (default)> = [False False True True]\n", + " ctc_social_security_tax<2025, (default)> = [9180.]\n", + " employee_social_security_tax<2025, (default)> = [4650. 2790. 0. 0.]\n", + " employee_medicare_tax<2025, (default)> = [1087.5 652.5 0. 0. ]\n", + " unreported_payroll_tax<2025, (default)> = [0.]\n", + " self_employment_tax_ald<2025, (default)> = [0.]\n", + " additional_medicare_tax<2025, (default)> = [0.]\n", + " excess_payroll_tax_withheld<2025, (default)> = [0.]\n", + " eitc<2025, (default)> = [0.]\n", + " eitc_eligible<2025, (default)> = [ True]\n", + " takes_up_eitc<2025, (default)> = [ True]\n", + " eitc_maximum<2025, (default)> = [7152.]\n", + " eitc_phased_in<2025, (default)> = [7152.]\n", + " eitc_reduction<2025, (default)> = [18855.018]\n", + " ctc_qualifying_children<2025, (default)> = [2]\n", + " ctc_qualifying_child<2025, (default)> = [False False True True]\n" + ] + } + ], + "source": [ + "# Let's increase the depth of the trace\n", + "\n", + "print(\"\\n=== CTC CALCULATION TRACE DEPTH = 4 ===\")\n", + "sim_for_trace.tracer.print_computation_log(max_depth=4)\n" + ] + }, + { + "cell_type": "markdown", + "id": "636de9bc", + "metadata": {}, + "source": [ + "# Notebook 1: Household Simulation with PolicyEngine\n" + ] + }, + { + "cell_type": "code", + "execution_count": 16, + "id": "e35ce716", + "metadata": {}, + "outputs": [], + "source": [ + "# Import necessary libraries\n", + "from policyengine_us import Simulation\n", + "from policyengine_core.reforms import Reform\n", + "import pandas as pd\n", + "import numpy as np" + ] + }, + { + "cell_type": "markdown", + "id": "6e4131ec", + "metadata": {}, + "source": [ + "## Part 1: Creating a Household Situation\n", + "\n", + "### Understanding Core Concepts\n", + "Before we begin, let's understand the key components:\n", + "- **Entities**: Person, tax unit, SPM unit, marital unit, family, household\n", + "- **Variables**: Can be inputs (employment_income) or calculations (ctc_value)\n", + "- **Parameters**: Policy features like tax rates, benefit amounts\n", + "- **Periods**: Time periods for which we calculate values" + ] + }, + { + "cell_type": "markdown", + "id": "9237bea2", + "metadata": {}, + "source": [ + "### PolicyEngine Repositories\n", + "To explore the source code and understand how variables and parameters are defined:\n", + "- **[policyengine-us](https://github.com/PolicyEngine/policyengine-us)**: Contains all US-specific variables, parameters, and policy logic\n", + " - Variables: `/policyengine_us/variables/`\n", + " - Parameters: `/policyengine_us/parameters/`\n", + "- **[policyengine-core](https://github.com/PolicyEngine/policyengine-core)**: Contains the core simulation frameworks\n", + " - `Simulation` class: `/policyengine_core/simulations/simulation.py`\n", + " - `Microsimulation` class: `/policyengine_core/simulations/microsimulation.py`" + ] + }, + { + "cell_type": "markdown", + "id": "276ba238", + "metadata": {}, + "source": [ + "### Getting Started: Using PolicyEngine's Web Interface\n", + "The easiest way to create a household situation is to use PolicyEngine's web interface:\n", + "1. Go to https://policyengine.org/us/household\n", + "2. Enter your household details using the interactive form\n", + "3. Navigate to the \"Reproduce in Python\" section\n", + "4. Copy the generated code\n", + "\n", + "For example, after creating a household at:\n", + "https://policyengine.org/us/household?focus=householdOutput.pythonReproducibility&household=54688\n", + "\n", + "You'll find ready-to-use Python code that creates the situation dictionary for you!" + ] + }, + { + "cell_type": "code", + "execution_count": 17, + "id": "fcbb04bd", + "metadata": {}, + "outputs": [], + "source": [ + "# Create a household situation dictionary\n", + "# This represents a family of 4: 2 adults and 2 children in California\n", + "\n", + "situation = {\n", + " \"people\": {\n", + " \"parent1\": {\n", + " \"age\": {\"2025\": 35},\n", + " \"employment_income\": {\"2025\": 75000}\n", + " },\n", + " \"parent2\": {\n", + " \"age\": {\"2025\": 33},\n", + " \"employment_income\": {\"2025\": 45000}\n", + " },\n", + " \"child1\": {\n", + " \"age\": {\"2025\": 10}\n", + " },\n", + " \"child2\": {\n", + " \"age\": {\"2025\": 7}\n", + " }\n", + " },\n", + " \"families\": {\n", + " \"family\": {\n", + " \"members\": [\"parent1\", \"parent2\", \"child1\", \"child2\"]\n", + " }\n", + " },\n", + " \"marital_units\": {\n", + " \"parents\": {\n", + " \"members\": [\"parent1\", \"parent2\"]\n", + " },\n", + " \"child1_marital_unit\": {\n", + " \"members\": [\"child1\"],\n", + " \"marital_unit_id\": {\"2025\": 1}\n", + " },\n", + " \"child2_marital_unit\": {\n", + " \"members\": [\"child2\"],\n", + " \"marital_unit_id\": {\"2025\": 2}\n", + " }\n", + " },\n", + " \"tax_units\": {\n", + " \"tax_unit\": {\n", + " \"members\": [\"parent1\", \"parent2\", \"child1\", \"child2\"]\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\": \"CA\"}\n", + " }\n", + " }\n", + "}" + ] + }, + { + "cell_type": "markdown", + "id": "459b1e35", + "metadata": {}, + "source": [ + "## Part 2: Running a Baseline Simulation\n" + ] + }, + { + "cell_type": "code", + "execution_count": 18, + "id": "9c8a29a0", + "metadata": {}, + "outputs": [], + "source": [ + "# Create a simulation object with our household situation\n", + "baseline_sim = Simulation(situation=situation)" + ] + }, + { + "cell_type": "code", + "execution_count": 19, + "id": "71c2953f", + "metadata": {}, + "outputs": [], + "source": [ + "# Calculate key variables for 2025\n", + "PERIOD = 2025" + ] + }, + { + "cell_type": "code", + "execution_count": 20, + "id": "42ec3ae4", + "metadata": {}, + "outputs": [], + "source": [ + "# Income-related variables\n", + "employment_income = baseline_sim.calculate(\"employment_income\", PERIOD)\n", + "adjusted_gross_income = baseline_sim.calculate(\"adjusted_gross_income\", PERIOD)" + ] + }, + { + "cell_type": "code", + "execution_count": 21, + "id": "f3d688f9", + "metadata": {}, + "outputs": [], + "source": [ + "# Tax-related variables\n", + "income_tax = baseline_sim.calculate(\"income_tax\", PERIOD)\n", + "ctc = baseline_sim.calculate(\"ctc_value\", PERIOD)\n", + "eitc = baseline_sim.calculate(\"eitc\", PERIOD)" + ] + }, + { + "cell_type": "code", + "execution_count": 22, + "id": "dde1834a", + "metadata": {}, + "outputs": [], + "source": [ + "# Benefits\n", + "snap = baseline_sim.calculate(\"snap\", PERIOD)" + ] + }, + { + "cell_type": "code", + "execution_count": 23, + "id": "2cafdaf6", + "metadata": {}, + "outputs": [], + "source": [ + "# Net income\n", + "household_net_income = baseline_sim.calculate(\"household_net_income\", PERIOD)" + ] + }, + { + "cell_type": "markdown", + "id": "1f4bbf09", + "metadata": {}, + "source": [ + "### Understanding the calculate() Return Value\n", + "**IMPORTANT**: The calculate() method returns a NumPy array with values for each entity\n", + "- For person-level variables: array length = number of people (4 in our case)\n", + "- For household-level variables: array length = number of households (1 in our case)" + ] + }, + { + "cell_type": "code", + "execution_count": 24, + "id": "f531d494", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "PERSON-LEVEL VARIABLE (employment_income):\n", + " Array: [75000. 45000. 0. 0.]\n", + " Shape: (4,)\n", + " Length: 4 (one value per person)\n", + " Individual values: Parent1=$75,000, Parent2=$45,000, Child1=$0, Child2=$0\n" + ] + } + ], + "source": [ + "# Person-level variable: employment_income\n", + "print(\"PERSON-LEVEL VARIABLE (employment_income):\")\n", + "print(f\" Array: {employment_income}\")\n", + "print(f\" Shape: {employment_income.shape}\")\n", + "print(f\" Length: {len(employment_income)} (one value per person)\")\n", + "print(f\" Individual values: Parent1=${employment_income[0]:,.0f}, Parent2=${employment_income[1]:,.0f}, Child1=${employment_income[2]:,.0f}, Child2=${employment_income[3]:,.0f}\")" + ] + }, + { + "cell_type": "code", + "execution_count": 25, + "id": "80b3a32d", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "\n", + "HOUSEHOLD-LEVEL VARIABLE (household_net_income):\n", + " Array: [102116.85]\n", + " Shape: (1,)\n", + " Length: 1 (one value per household)\n", + " Value: $102,117\n" + ] + } + ], + "source": [ + "# Household-level variable: household_net_income\n", + "print(\"\\nHOUSEHOLD-LEVEL VARIABLE (household_net_income):\")\n", + "print(f\" Array: {household_net_income}\")\n", + "print(f\" Shape: {household_net_income.shape}\")\n", + "print(f\" Length: {len(household_net_income)} (one value per household)\")\n", + "print(f\" Value: ${household_net_income[0]:,.0f}\")" + ] + }, + { + "cell_type": "code", + "execution_count": 26, + "id": "d1416ea7", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "\n", + "Using .sum():\n", + " employment_income.sum() = $120,000 (adds all 4 person values)\n", + " household_net_income.sum() = $102,117 (just the 1 household value)\n" + ] + } + ], + "source": [ + "# This demonstrates why we use .sum() - it works correctly regardless of entity level\n", + "print(f\"\\nUsing .sum():\")\n", + "print(f\" employment_income.sum() = ${employment_income.sum():,.0f} (adds all 4 person values)\")\n", + "print(f\" household_net_income.sum() = ${household_net_income.sum():,.0f} (just the 1 household value)\")" + ] + }, + { + "cell_type": "markdown", + "id": "41eae444", + "metadata": {}, + "source": [ + "### Common Array Operations\n", + "- `.sum()` - adds all values in the array\n", + "- `.mean()` - calculates the average\n", + "- `.max()` - finds the maximum value\n", + "- `.min()` - finds the minimum value\n", + "- `[index]` - accesses individual elements\n" + ] + }, + { + "cell_type": "code", + "execution_count": 27, + "id": "cb50cc54", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "\n", + "=== BASELINE RESULTS ===\n", + "Total Employment Income: $120,000\n", + "Average Employment Income per Person: $30,000\n", + "Adjusted Gross Income: $120,000\n", + "Federal Income Tax: $6,323\n", + "SNAP Benefits: $0\n", + "Child Tax Credit: $4,000\n", + "Earned Income Tax Credit: $0\n", + "Household Net Income: $102,117\n" + ] + } + ], + "source": [ + "print(\"\\n=== BASELINE RESULTS ===\")\n", + "print(f\"Total Employment Income: ${employment_income.sum():,.0f}\")\n", + "print(f\"Average Employment Income per Person: ${employment_income.mean():,.0f}\")\n", + "print(f\"Adjusted Gross Income: ${adjusted_gross_income.sum():,.0f}\")\n", + "print(f\"Federal Income Tax: ${income_tax.sum():,.0f}\")\n", + "print(f\"SNAP Benefits: ${snap.sum():,.0f}\")\n", + "print(f\"Child Tax Credit: ${ctc.sum():,.0f}\")\n", + "print(f\"Earned Income Tax Credit: ${eitc.sum():,.0f}\")\n", + "print(f\"Household Net Income: ${household_net_income.sum():,.0f}\")" + ] + }, + { + "cell_type": "markdown", + "id": "fc2b970c", + "metadata": {}, + "source": [ + "## Part 3: Understanding Variable Calculation Flow\n" + ] + }, + { + "cell_type": "markdown", + "id": "cb18e447", + "metadata": {}, + "source": [ + "xxx - purpose of trace" + ] + }, + { + "cell_type": "code", + "execution_count": 28, + "id": "1ae3551e", + "metadata": {}, + "outputs": [], + "source": [ + "# Initialize a new simulation for tracing\n", + "sim_for_trace = Simulation(situation=situation)" + ] + }, + { + "cell_type": "code", + "execution_count": 29, + "id": "389e33dc", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "\n", + "=== CTC CALCULATION TRACE ===\n", + " ctc_value<2025, (default)> = [4000.]\n", + " ctc<2025, (default)> = [4000.]\n", + " ctc_maximum_with_arpa_addition<2025, (default)> = [4000.]\n", + " ctc_phase_out<2025, (default)> = [0.]\n", + " ctc_phase_in<2025, (default)> = [17625.]\n", + " tax_unit_earned_income<2025, (default)> = [120000.]\n", + " ctc_social_security_tax<2025, (default)> = [9180.]\n", + " eitc<2025, (default)> = [0.]\n", + " ctc_qualifying_children<2025, (default)> = [2]\n" + ] + } + ], + "source": [ + "# Let's trace how the Child Tax Credit is calculated\n", + "# This demonstrates the dependency tree of variables\n", + "\n", + "sim_for_trace.trace = True\n", + "sim_for_trace.calculate(\"ctc_value\", period=PERIOD)\n", + "# Now let's see the trace\n", + "print(\"\\n=== CTC CALCULATION TRACE ===\")\n", + "sim_for_trace.tracer.print_computation_log(max_depth=3)\n" + ] + }, + { + "cell_type": "markdown", + "id": "3bc4056f", + "metadata": {}, + "source": [] + }, + { + "cell_type": "code", + "execution_count": 30, + "id": "88b54fa6", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "\n", + "=== CTC CALCULATION TRACE DEPTH = 4 ===\n", + " ctc_value<2025, (default)> = [4000.]\n", + " ctc<2025, (default)> = [4000.]\n", + " ctc_maximum_with_arpa_addition<2025, (default)> = [4000.]\n", + " ctc_maximum<2025, (default)> = [4000.]\n", + " ctc_arpa_addition<2025, (default)> = [0.]\n", + " ctc_phase_out<2025, (default)> = [0.]\n", + " adjusted_gross_income<2025, (default)> = [120000.]\n", + " ctc_phase_out_threshold<2025, (default)> = [400000.]\n", + " ctc_phase_in<2025, (default)> = [17625.]\n", + " tax_unit_earned_income<2025, (default)> = [120000.]\n", + " earned_income<2025, (default)> = [75000. 45000. 0. 0.]\n", + " is_tax_unit_dependent<2025, (default)> = [False False True True]\n", + " ctc_social_security_tax<2025, (default)> = [9180.]\n", + " employee_social_security_tax<2025, (default)> = [4650. 2790. 0. 0.]\n", + " employee_medicare_tax<2025, (default)> = [1087.5 652.5 0. 0. ]\n", + " unreported_payroll_tax<2025, (default)> = [0.]\n", + " self_employment_tax_ald<2025, (default)> = [0.]\n", + " additional_medicare_tax<2025, (default)> = [0.]\n", + " excess_payroll_tax_withheld<2025, (default)> = [0.]\n", + " eitc<2025, (default)> = [0.]\n", + " eitc_eligible<2025, (default)> = [ True]\n", + " takes_up_eitc<2025, (default)> = [ True]\n", + " eitc_maximum<2025, (default)> = [7152.]\n", + " eitc_phased_in<2025, (default)> = [7152.]\n", + " eitc_reduction<2025, (default)> = [18855.018]\n", + " ctc_qualifying_children<2025, (default)> = [2]\n", + " ctc_qualifying_child<2025, (default)> = [False False True True]\n" + ] + } + ], + "source": [ + "# Let's increase the depth of the trace\n", + "\n", + "print(\"\\n=== CTC CALCULATION TRACE DEPTH = 4 ===\")\n", + "sim_for_trace.tracer.print_computation_log(max_depth=4)\n" + ] + }, + { + "cell_type": "markdown", + "id": "8dde68eb", + "metadata": {}, + "source": [ + "### Understanding the Trace Output\n", + "The trace shows:\n", + "- **Indentation** = dependency depth (more indented = deeper in calculation tree)\n", + "- **Variable names** with PERIOD and year (e.g., `ctc_value<2025, (default)>`)\n", + "- **Calculated values** as arrays - the array length indicates the entity level:\n", + " - Arrays with 4 values `[x, x, x, x]` = person-level (4 people in household)\n", + " - Arrays with 1 value `[x]` = tax unit or household level\n", + "- **Time periods** when the calculation applies\n", + "- **With max_depth=None**, the most deeply indented variables are inputs (from user, defaults, or data) rather than calculations\n" + ] + }, + { + "cell_type": "code", + "execution_count": 31, + "id": "66d1bf86", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "\n", + "CTC Maximum per child: $[ 0. 0. 2000. 2000.]\n", + "CTC Phase-out amount: $[0.]\n" + ] + } + ], + "source": [ + "# Now let's look at individual variables outside of the computation tree\n", + "ctc_child_individual_maximum = baseline_sim.calculate(\"ctc_child_individual_maximum\", PERIOD)\n", + "ctc_phase_out = baseline_sim.calculate(\"ctc_phase_out\", PERIOD)\n", + "\n", + "print(f\"\\nCTC Maximum per child: ${ctc_child_individual_maximum}\")\n", + "print(f\"CTC Phase-out amount: ${ctc_phase_out}\")" + ] + }, + { + "cell_type": "markdown", + "id": "62a76a1f", + "metadata": {}, + "source": [ + "## Part 4: Creating and Running Reforms\n", + "\n", + "### Parametric Reforms\n", + "The most common type of reform modifies existing parameters (tax rates, benefit amounts, thresholds, etc.):" + ] + }, + { + "cell_type": "markdown", + "id": "9988116b", + "metadata": {}, + "source": [ + "### Understanding Parameter Types\n", + "\n", + "PolicyEngine uses different parameter types for various policy features. Here's how to work with each type:\n", + "\n", + "| Parameter Type | Example | Notes |\n", + "|---------------|---------|-------|\n", + "| **Single value** | `\"gov.irs.credits.ctc.amount.base[0].amount\": 3000`
`\"gov.irs.credits.ctc.phase_out.rate\": 0.05`
`\"gov.irs.credits.ctc.child.max_age\": 17` | Can be monetary amounts (USD), rates/percentages (as decimals: 5% = 0.05), ages (years), or other single values |\n", + "| **List parameter** | `\"gov.irs.credits.refundable\": [\"eitc\", \"refundable_ctc\"]` | Lists of values, often containing variable class names |\n", + "| **Scale parameter** | `\"gov.irs.credits.ctc.amount.base[0].amount\": 3000` | Graduated scales with thresholds and rates |\n", + "| **Breakdown parameters** | `\"gov.irs.credits.ctc.phase_out.threshold.JOINT\": 500000`| Parameters broken down by enums (e.g., filing status) or numeric ranges |" + ] + }, + { + "cell_type": "markdown", + "id": "33dc0d67", + "metadata": {}, + "source": [ + "### Accessing Scale Components\n", + "- `scale.thresholds`: list[float] - income thresholds\n", + "- `scale.rates`: list[float] - tax rates for each bracket\n", + "- `scale.amounts`: list[float] - fixed amounts (if applicable)\n", + "\n", + "Examples of accessing specific elements:\n", + "- `scale.thresholds[0]` - first threshold (usually 0)\n", + "- `scale.thresholds[-1]` - last threshold\n", + "- `scale.rates[2]` - rate for third bracket\n" + ] + }, + { + "cell_type": "code", + "execution_count": 32, + "id": "ad497e3f", + "metadata": {}, + "outputs": [], + "source": [ + "# Create a parametric reform that increases the CTC amount\n", + "# This is a simple parameter change - increasing CTC from $2,000 to $3,000 from 2025 forward\n", + "\n", + "ctc_reform = Reform.from_dict({\n", + " \"gov.irs.credits.ctc.amount.base[0].amount\": {\n", + " \"2025-01-01.2100-12-31\": 3000\n", + " }\n", + "}, country_id=\"us\")\n", + "\n", + "# You can also create more complex reforms with multiple parameters\n", + "comprehensive_reform = Reform.from_dict({\n", + " # Increase CTC amount\n", + " \"gov.irs.credits.ctc.amount.base[0].amount\": {\n", + " \"2025-01-01.2100-12-31\": 3000\n", + " },\n", + " # Make CTC fully refundable\n", + " \"gov.irs.credits.ctc.refundable.fully_refundable\": {\n", + " \"2025-01-01.2100-12-31\": True\n", + " },\n", + " # Increase phase-out threshold for JOINT filing statuses\n", + " \"gov.irs.credits.ctc.phase_out.threshold.JOINT\": {\n", + " \"2025-01-01.2100-12-31\": 500000\n", + " }\n", + "}, country_id=\"us\")" + ] + }, + { + "cell_type": "markdown", + "id": "3c9432e1", + "metadata": {}, + "source": [ + "### Running the Reformed Simulation\n" + ] + }, + { + "cell_type": "code", + "execution_count": 33, + "id": "a57152ad", + "metadata": {}, + "outputs": [], + "source": [ + "# Create a new simulation with the comprehensive reform\n", + "reformed_sim = Simulation(\n", + " situation=situation,\n", + " reform=comprehensive_reform\n", + ")" + ] + }, + { + "cell_type": "code", + "execution_count": 34, + "id": "f4aaf78c", + "metadata": {}, + "outputs": [], + "source": [ + "# Calculate the same variables under the reform\n", + "reformed_income_tax = reformed_sim.calculate(\"income_tax\", PERIOD)\n", + "reformed_ctc = reformed_sim.calculate(\"ctc_value\", PERIOD)\n", + "reformed_net_income = reformed_sim.calculate(\"household_net_income\", PERIOD)" + ] + }, + { + "cell_type": "code", + "execution_count": 35, + "id": "e6398b3b", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "=== REFORM RESULTS ===\n", + "Federal Income Tax: $4,323\n", + "Child Tax Credit: $6,000\n", + "Household Net Income: $104,117\n" + ] + } + ], + "source": [ + "print(\"=== REFORM RESULTS ===\")\n", + "print(f\"Federal Income Tax: ${reformed_income_tax.sum():,.0f}\")\n", + "print(f\"Child Tax Credit: ${reformed_ctc.sum():,.0f}\")\n", + "print(f\"Household Net Income: ${reformed_net_income.sum():,.0f}\")" + ] + }, + { + "cell_type": "markdown", + "id": "bde12be6", + "metadata": {}, + "source": [ + "### Structural Reforms (Advanced)\n", + "PolicyEngine also supports structural reforms that change how variables are calculated, not just parameter values. Here's an example that adds a phase-out to the SALT deduction cap:" + ] + }, + { + "cell_type": "code", + "execution_count": 36, + "id": "95104770", + "metadata": {}, + "outputs": [], + "source": [ + "from policyengine_us.model_api import *" + ] + }, + { + "cell_type": "code", + "execution_count": 37, + "id": "6b4b2bb2", + "metadata": {}, + "outputs": [], + "source": [ + "# Example of a structural reform that modifies the SALT cap calculation to add phase-out to the cap\n", + "class salt_cap(Variable):\n", + " value_type = float\n", + " entity = TaxUnit\n", + " label = \"SALT cap\"\n", + " unit = USD\n", + " definition_PERIOD = YEAR\n", + " reference = \"https://www.law.cornell.edu/uscode/text/26/164\"\n", + "\n", + " def formula(tax_unit, PERIOD, parameters):\n", + " filing_status = tax_unit(\"filing_status\", PERIOD)\n", + " p = parameters(\n", + " PERIOD\n", + " ).gov.irs.deductions.itemized.salt_and_real_estate\n", + " max_cap = p.cap[filing_status]\n", + " p_ref = parameters(PERIOD).gov.contrib.salt_phase_out\n", + " agi = tax_unit(\"adjusted_gross_income\", PERIOD)\n", + " agi_excess = max_(0, agi - p_ref.threshold[filing_status])\n", + " phase_out = p_ref.rate * agi_excess\n", + " phased_out_cap = max_(0, max_cap - phase_out)\n", + " if p_ref.floor.applies:\n", + " floor = p_ref.floor.amount[filing_status]\n", + " return max_(phased_out_cap, floor)\n", + " return phased_out_cap\n", + "\n", + "class reform(Reform):\n", + " def apply(self):\n", + " self.update_variable(salt_cap)\n", + "\n", + "# This would be applied as: Simulation(situation=situation, reform=reform())" + ] + }, + { + "cell_type": "markdown", + "id": "5fc17dc0", + "metadata": {}, + "source": [ + "**Best Practice**: While structural reforms are powerful, it's often better to:\n", + "1. File an issue in the [policyengine-us repository](https://github.com/PolicyEngine/policyengine-us/issues)\n", + "2. Discuss with PolicyEngine developers\n", + "3. Submit a pull request to add new parameters\n", + "4. Convert your structural reform into a parametric one\n", + "\n", + "This approach ensures:\n", + "- Your reform integrates well with the existing codebase\n", + "- Other users can benefit from the new functionality\n", + "- The reform is properly tested and documented\n", + "- Future updates won't break your analysis" + ] + }, + { + "cell_type": "markdown", + "id": "130ce1b3", + "metadata": {}, + "source": [ + "## Part 5: Comparing Baseline and Reform\n" + ] + }, + { + "cell_type": "code", + "execution_count": 38, + "id": "f8d906d8", + "metadata": {}, + "outputs": [], + "source": [ + "# Create a comparison dataframe\n", + "comparison_data = {\n", + " \"Metric\": [\n", + " \"Federal Income Tax\",\n", + " \"Child Tax Credit\", \n", + " \"Household Net Income\",\n", + " ],\n", + " \"Baseline\": [\n", + " income_tax.sum(),\n", + " ctc.sum(),\n", + " household_net_income.sum(),\n", + " ],\n", + " \"Reform\": [\n", + " reformed_income_tax.sum(),\n", + " reformed_ctc.sum(),\n", + " reformed_net_income.sum(),\n", + " ]\n", + "}" + ] + }, + { + "cell_type": "code", + "execution_count": 39, + "id": "f0487bce", + "metadata": {}, + "outputs": [], + "source": [ + "comparison_df = pd.DataFrame(comparison_data)\n", + "comparison_df[\"Change\"] = comparison_df[\"Reform\"] - comparison_df[\"Baseline\"]\n", + "comparison_df[\"% Change\"] = (comparison_df[\"Change\"] / comparison_df[\"Baseline\"].abs() * 100).round(2)" + ] + }, + { + "cell_type": "code", + "execution_count": 40, + "id": "aba01b47", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "\n", + "=== COMPARISON ===\n", + " Metric Baseline Reform Change % Change\n", + " Federal Income Tax 6323.000000 4323.000000 -2000.0 -31.629999\n", + " Child Tax Credit 4000.000000 6000.000000 2000.0 50.000000\n", + "Household Net Income 102116.851562 104116.851562 2000.0 1.960000\n" + ] + } + ], + "source": [ + "print(\"\\n=== COMPARISON ===\")\n", + "print(comparison_df.to_string(index=False))" + ] + }, + { + "cell_type": "markdown", + "id": "b2f5ef57", + "metadata": {}, + "source": [ + "## Part 6: Using the calculate_dataframe Method\n", + "\n", + "The `calculate_dataframe` method provides a convenient way to calculate multiple variables at once and return them in a pandas DataFrame format. This is particularly useful when you need to analyze several variables together or export results for further analysis.\n", + "\n", + "### Understanding Entity Levels and Data Structure\n", + "\n", + "PolicyEngine organizes data hierarchically across different entity levels:\n", + "\n", + "| Entity Level | Description | Example Variables |\n", + "|-------------|-------------|-------------------|\n", + "| **Person** | Individual-level data | employment_income, age, is_disabled |\n", + "| **Tax Unit** | Tax filing unit data | income_tax, ctc_value, eitc |\n", + "| **SPM Unit** | Supplemental Poverty Measure unit | snap, housing_assistance |\n", + "| **Household** | Household-level data | household_net_income, rent |\n", + "\n", + "When using `calculate_dataframe`, it's crucial to understand how these entity levels interact:" + ] + }, + { + "cell_type": "code", + "execution_count": 41, + "id": "cccdc298", + "metadata": {}, + "outputs": [], + "source": [ + "# For more complex analysis, we can use calculate_dataframe\n", + "# This returns a pandas DataFrame with multiple variables\n", + "\n", + "VARIABLES_TO_CALCULATE = [\n", + " \"employment_income\",\n", + " \"adjusted_gross_income\",\n", + " \"taxable_income\",\n", + " \"income_tax\",\n", + " \"ctc_value\",\n", + " \"eitc\",\n", + " \"snap\",\n", + " \"household_net_income\"\n", + "]" + ] + }, + { + "cell_type": "code", + "execution_count": 42, + "id": "a611cc57", + "metadata": {}, + "outputs": [], + "source": [ + "PERIOD = 2025" + ] + }, + { + "cell_type": "markdown", + "id": "d7c7a426", + "metadata": {}, + "source": [ + "### Default Behavior: Automatic Entity Resolution\n", + "\n", + "When you don't specify a `map_to` parameter, `calculate_dataframe` automatically determines the appropriate entity level based on the \"longest\" entity needed to represent all variables:" + ] + }, + { + "cell_type": "code", + "execution_count": 43, + "id": "5400bf99", + "metadata": {}, + "outputs": [], + "source": [ + "# Calculate for baseline\n", + "baseline_df = baseline_sim.calculate_dataframe(\n", + " VARIABLES_TO_CALCULATE,\n", + " period=PERIOD\n", + ")\n", + "\n", + "# Calculate for reform\n", + "reform_df = reformed_sim.calculate_dataframe(\n", + " VARIABLES_TO_CALCULATE,\n", + " period=PERIOD\n", + ")" + ] + }, + { + "cell_type": "code", + "execution_count": 44, + "id": "fb18dbe3", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "\n", + "=== BASELINE DATAFRAME (No map_to specified) ===\n", + " employment_income adjusted_gross_income taxable_income income_tax \\\n", + "0 75000.0 120000.0 90000.0 6323.0 \n", + "1 45000.0 120000.0 90000.0 6323.0 \n", + "2 0.0 120000.0 90000.0 6323.0 \n", + "3 0.0 120000.0 90000.0 6323.0 \n", + "\n", + " ctc_value eitc snap household_net_income \n", + "0 4000.0 0.0 0.0 102116.851562 \n", + "1 4000.0 0.0 0.0 102116.851562 \n", + "2 4000.0 0.0 0.0 102116.851562 \n", + "3 4000.0 0.0 0.0 102116.851562 \n", + "\n", + "Shape: (4, 8)\n", + "Note: Since employment_income is person-level, all other variables are disaggregated to person-level\n" + ] + } + ], + "source": [ + "print(\"\\n=== BASELINE DATAFRAME (No map_to specified) ===\")\n", + "print(baseline_df)\n", + "print(f\"\\nShape: {baseline_df.shape}\")\n", + "print(\"Note: Since employment_income is person-level, all other variables are disaggregated to person-level\")" + ] + }, + { + "cell_type": "markdown", + "id": "6da44f40", + "metadata": {}, + "source": [ + "In this case, because `employment_income` is a person-level variable and we have 4 people in our household, the DataFrame will have 4 rows. All higher-level variables (tax unit, household) will be repeated for each person they apply to." + ] + }, + { + "cell_type": "code", + "execution_count": 45, + "id": "4896ad23", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "\n", + "=== REFORM DATAFRAME ===\n", + " employment_income adjusted_gross_income taxable_income income_tax \\\n", + "0 75000.0 120000.0 90000.0 4323.0 \n", + "1 45000.0 120000.0 90000.0 4323.0 \n", + "2 0.0 120000.0 90000.0 4323.0 \n", + "3 0.0 120000.0 90000.0 4323.0 \n", + "\n", + " ctc_value eitc snap household_net_income \n", + "0 6000.0 0.0 0.0 104116.851562 \n", + "1 6000.0 0.0 0.0 104116.851562 \n", + "2 6000.0 0.0 0.0 104116.851562 \n", + "3 6000.0 0.0 0.0 104116.851562 \n" + ] + } + ], + "source": [ + "print(\"\\n=== REFORM DATAFRAME ===\")\n", + "print(reform_df)" + ] + }, + { + "cell_type": "markdown", + "id": "1cd8da39", + "metadata": {}, + "source": [ + "### Using map_to Parameter for Aggregation\n", + "\n", + "The `map_to` parameter allows you to aggregate all variables to a specific entity level. This is extremely useful when you want to analyze data at the household or tax unit level:" + ] + }, + { + "cell_type": "code", + "execution_count": 46, + "id": "9663cea6", + "metadata": {}, + "outputs": [], + "source": [ + "# Calculate for baseline\n", + "baseline_person_df = baseline_sim.calculate_dataframe(\n", + " VARIABLES_TO_CALCULATE,\n", + " map_to=\"person\",\n", + " period=PERIOD\n", + ")" + ] + }, + { + "cell_type": "code", + "execution_count": 47, + "id": "04506208", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "\n", + "=== BASELINE DATAFRAME (Mapped to person) ===\n", + " employment_income adjusted_gross_income taxable_income income_tax \\\n", + "0 75000.0 120000.0 90000.0 6323.0 \n", + "1 45000.0 120000.0 90000.0 6323.0 \n", + "2 0.0 120000.0 90000.0 6323.0 \n", + "3 0.0 120000.0 90000.0 6323.0 \n", + "\n", + " ctc_value eitc snap household_net_income \n", + "0 4000.0 0.0 0.0 102116.851562 \n", + "1 4000.0 0.0 0.0 102116.851562 \n", + "2 4000.0 0.0 0.0 102116.851562 \n", + "3 4000.0 0.0 0.0 102116.851562 \n", + "\n", + "Shape: (4, 8)\n", + "Note: All variables are now aggregated to person level (4 rows)\n" + ] + } + ], + "source": [ + "print(\"\\n=== BASELINE DATAFRAME (Mapped to person) ===\")\n", + "print(baseline_person_df)\n", + "print(f\"\\nShape: {baseline_person_df.shape}\")\n", + "print(\"Note: All variables are now aggregated to person level (4 rows)\")" + ] + }, + { + "cell_type": "code", + "execution_count": 48, + "id": "386a7fb2", + "metadata": {}, + "outputs": [], + "source": [ + "# Calculate for baseline\n", + "baseline_household_df = baseline_sim.calculate_dataframe(\n", + " VARIABLES_TO_CALCULATE,\n", + " map_to=\"household\",\n", + " period=PERIOD\n", + ")" + ] + }, + { + "cell_type": "code", + "execution_count": 49, + "id": "7964e2a7", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "\n", + "=== BASELINE DATAFRAME (Mapped to household) ===\n", + " employment_income adjusted_gross_income taxable_income income_tax \\\n", + "0 120000.0 120000.0 90000.0 6323.0 \n", + "\n", + " ctc_value eitc snap household_net_income \n", + "0 4000.0 0.0 0.0 102116.851562 \n", + "\n", + "Shape: (1, 8)\n", + "Note: All variables are now aggregated to household level (1 row)\n" + ] + } + ], + "source": [ + "print(\"\\n=== BASELINE DATAFRAME (Mapped to household) ===\")\n", + "print(baseline_household_df)\n", + "print(f\"\\nShape: {baseline_household_df.shape}\")\n", + "print(\"Note: All variables are now aggregated to household level (1 row)\")" + ] + }, + { + "cell_type": "markdown", + "id": "37472050", + "metadata": {}, + "source": [ + "### How map_to Aggregation Works\n", + "\n", + "When you use `map_to`, PolicyEngine performs the following operations:\n", + "\n", + "1. **Person-level variables** → Summed within the target entity\n", + " - Example: `employment_income` for all 4 people is summed to get total household employment income\n", + "\n", + "2. **Same-level variables** → Kept as-is\n", + " - Example: `household_net_income` is already at household level, so no change\n", + "\n", + "3. **Intermediate-level variables** → Summed to the target level\n", + " - Example: If there were multiple tax units, their `income_tax` would be summed" + ] + }, + { + "cell_type": "code", + "execution_count": 50, + "id": "9c129cef", + "metadata": {}, + "outputs": [], + "source": [ + "# Example: Export-ready DataFrame\n", + "export_df = baseline_sim.calculate_dataframe(\n", + " VARIABLES_TO_CALCULATE,\n", + " map_to=\"household\",\n", + " period=PERIOD\n", + ")\n", + "\n", + "# Add identifiers and save\n", + "export_df[\"household_id\"] = \"household\"\n", + "export_df[\"scenario\"] = \"baseline\"\n", + "\n", + "# You could save this to CSV:\n", + "export_df.to_csv(\"household_analysis.csv\", index=False)" + ] + }, + { + "cell_type": "markdown", + "id": "78663376", + "metadata": {}, + "source": [ + "## Part 7: Creating Earnings Variation Charts\n", + "\n", + "### Note: Using PolicyEngine's Web Interface for Earnings Variation\n", + "You can also generate earnings variation code directly from PolicyEngine's web interface:\n", + "1. Create your household at https://policyengine.org/us/household\n", + "2. Toggle \"Include earnings variation\" in the \"Reproduce in Python\" section\n", + "3. Copy the generated code with axes already configured\n", + "\n", + "Here's an example of what the generated code looks like:" + ] + }, + { + "cell_type": "code", + "execution_count": 51, + "id": "d084d8b7", + "metadata": {}, + "outputs": [], + "source": [ + "# Example from PolicyEngine's \"Reproduce in Python\" with earnings variation\n", + "situation_with_axes = {\n", + " \"people\": {\n", + " \"you\": {\n", + " \"age\": {\n", + " \"2025\": 35\n", + " }\n", + " },\n", + " \"your partner\": {\n", + " \"age\": {\n", + " \"2025\": 33\n", + " }\n", + " },\n", + " \"your first dependent\": {\n", + " \"age\": {\n", + " \"2025\": 10\n", + " }\n", + " },\n", + " \"your second dependent\": {\n", + " \"age\": {\n", + " \"2025\": 7\n", + " }\n", + " }\n", + " },\n", + " \"families\": {\n", + " \"your family\": {\n", + " \"members\": [\n", + " \"you\",\n", + " \"your partner\",\n", + " \"your first dependent\",\n", + " \"your second dependent\"\n", + " ]\n", + " }\n", + " },\n", + " \"marital_units\": {\n", + " \"your marital unit\": {\n", + " \"members\": [\n", + " \"you\",\n", + " \"your partner\"\n", + " ]\n", + " },\n", + " \"your first dependent's marital unit\": {\n", + " \"members\": [\n", + " \"your first dependent\"\n", + " ],\n", + " \"marital_unit_id\": {\n", + " \"2025\": 1\n", + " }\n", + " },\n", + " \"your second dependent's marital unit\": {\n", + " \"members\": [\n", + " \"your second dependent\"\n", + " ],\n", + " \"marital_unit_id\": {\n", + " \"2025\": 2\n", + " }\n", + " }\n", + " },\n", + " \"tax_units\": {\n", + " \"your tax unit\": {\n", + " \"members\": [\n", + " \"you\",\n", + " \"your partner\",\n", + " \"your first dependent\",\n", + " \"your second dependent\"\n", + " ]\n", + " }\n", + " },\n", + " \"spm_units\": {\n", + " \"your household\": {\n", + " \"members\": [\n", + " \"you\",\n", + " \"your partner\",\n", + " \"your first dependent\",\n", + " \"your second dependent\"\n", + " ]\n", + " }\n", + " },\n", + " \"households\": {\n", + " \"your household\": {\n", + " \"members\": [\n", + " \"you\",\n", + " \"your partner\",\n", + " \"your first dependent\",\n", + " \"your second dependent\"\n", + " ],\n", + " \"state_name\": {\n", + " \"2025\": \"CA\"\n", + " }\n", + " }\n", + " },\n", + " \"axes\": [\n", + " [\n", + " {\n", + " \"name\": \"employment_income\",\n", + " \"count\": 1200,\n", + " \"min\": 0,\n", + " \"max\": 600000\n", + " }\n", + " ]\n", + " ]\n", + "}" + ] + }, + { + "cell_type": "markdown", + "id": "e1bae271", + "metadata": {}, + "source": [ + "### Varying inputs with **axes**\n", + "\n", + "PolicyEngine inherits **axes** from OpenFisca, letting us replicate the household situation over a grid of values.\n", + "\n", + "| Key | Meaning |\n", + "|-----|---------|\n", + "| `name` | Variable to iterate over |\n", + "| `count` | Number of equally spaced points |\n", + "| `min` | Interval start |\n", + "| `max` | Interval end |\n", + "\n", + "See the OpenFisca documentation for details: " + ] + }, + { + "cell_type": "markdown", + "id": "5d286699", + "metadata": {}, + "source": [ + "### Note: Flexibility of Axes Variables\n", + "You can vary **ANY** variable in axes, not just employment_income! Examples include:\n", + "- `rent`\n", + "- `mortgage_interest` \n", + "- `medical_expenses`\n", + "- `childcare_expenses`\n", + "\n", + "Just change the \"name\" parameter in the axes configuration." + ] + }, + { + "cell_type": "code", + "execution_count": 52, + "id": "a7f22344", + "metadata": {}, + "outputs": [], + "source": [ + "# Create simulation with axes\n", + "axes_sim = Simulation(situation=situation_with_axes)" + ] + }, + { + "cell_type": "code", + "execution_count": 53, + "id": "5ff9f744", + "metadata": {}, + "outputs": [], + "source": [ + "# Calculate variables across the income range\n", + "income_range = axes_sim.calculate(\"employment_income\", map_to=\"household\", period=PERIOD)\n", + "net_income_range = axes_sim.calculate(\"household_net_income\", map_to=\"household\", period=PERIOD)\n", + "ctc_range = axes_sim.calculate(\"ctc_value\", map_to=\"household\", period=PERIOD)\n", + "eitc_range = axes_sim.calculate(\"eitc\", map_to=\"household\", period=PERIOD)\n", + "snap_range = axes_sim.calculate(\"snap\", map_to=\"household\", period=PERIOD)" + ] + }, + { + "cell_type": "code", + "execution_count": 54, + "id": "2f8c5308", + "metadata": {}, + "outputs": [], + "source": [ + "# Create a dataframe for analysis\n", + "results_df = pd.DataFrame({\n", + " \"Employment Income\": income_range,\n", + " \"Net Income\": net_income_range,\n", + " \"CTC\": ctc_range,\n", + " \"EITC\": eitc_range,\n", + " \"SNAP\": snap_range\n", + "})" + ] + }, + { + "cell_type": "code", + "execution_count": 55, + "id": "af9bd728", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "\n", + "=== INCOME VARIATION RESULTS (Sample) ===\n", + " Employment Income Net Income CTC EITC SNAP\n", + "0 0.000000 26000.064453 0.0 0.000000 7710.931641\n", + "1 524.860229 26749.869141 0.0 209.944092 7587.632324\n", + "2 1049.720459 27496.976562 0.0 419.888184 7461.632324\n", + "3 1574.580688 28244.080078 0.0 629.832275 7335.632324\n", + "4 2099.440918 28991.185547 0.0 839.776367 7209.632324\n" + ] + } + ], + "source": [ + "# Show sample of results\n", + "print(\"\\n=== INCOME VARIATION RESULTS (Sample) ===\")\n", + "print(results_df.head()) # Show the first 5 rows" + ] + }, + { + "cell_type": "markdown", + "id": "7e309a1a", + "metadata": {}, + "source": [ + "### Parallel and perpendicular axes\n", + "\n", + "* **Parallel axes** – objects inside the **same** inner list. Variables change **together** (lock‑step). \n", + "* **Perpendicular axes** – objects in **separate** inner lists. Variables change **independently** creating a Cartesian product." + ] + }, + { + "cell_type": "code", + "execution_count": 56, + "id": "6834e3d4", + "metadata": {}, + "outputs": [], + "source": [ + "# One parallel axis (employment_income)\n", + "example_axes = [[\n", + " {\"name\": \"employment_income\", \"count\": 1200, \"min\": 0, \"max\": 600_000}\n", + "]]\n", + "\n", + "# Two parallel axes (income and childcare cost) changing together\n", + "example_axes = [[\n", + " {\"name\": \"employment_income\", \"count\": 1200, \"min\": 0, \"max\": 600_000},\n", + " {\"name\": \"childcare_expenses\", \"count\": 1200, \"min\": 0, \"max\": 20_000}\n", + "]]\n", + "\n", + "# Two perpendicular axes (income × age) – 1 200 × 10 = 12 000 variants\n", + "example_axes = [\n", + " [{\"name\": \"employment_income\", \"count\": 1200, \"min\": 0, \"max\": 600_000}],\n", + " [{\"name\": \"age\", \"index\": 0, \"count\": 10, \"min\": 18, \"max\": 65}]\n", + "]" + ] + }, + { + "cell_type": "markdown", + "id": "6dd76b3d", + "metadata": {}, + "source": [ + "### Visualizing the data " + ] + }, + { + "cell_type": "code", + "execution_count": 57, + "id": "cf34eeb5", + "metadata": {}, + "outputs": [], + "source": [ + "import plotly.graph_objects as go\n", + "from policyengine_core.charts import format_fig" + ] + }, + { + "cell_type": "code", + "execution_count": 58, + "id": "c69f76eb", + "metadata": {}, + "outputs": [ + { + "data": { + "application/vnd.plotly.v1+json": { + "config": { + "plotlyServerURL": "https://plot.ly" + }, + "data": [ + { + "hovertemplate": "Employment Income: $%{x:,.0f}
CTC: $%{y:,.0f}", + "line": { + "color": "#2C6496", + "width": 3 + }, + "mode": "lines", + "name": "Child Tax Credit", + "type": "scatter", + "x": [ + 0, + 524.8602294921875, + 1049.720458984375, + 1574.5806884765625, + 2099.44091796875, + 2624.301025390625, + 3149.161376953125, + 3674.021484375, + 4198.8818359375, + 4723.74169921875, + 5248.60205078125, + 5773.4619140625, + 6298.32275390625, + 6823.18310546875, + 7348.04296875, + 7872.9033203125, + 8397.763671875, + 8922.623046875, + 9447.4833984375, + 9972.34375, + 10497.2041015625, + 11022.0634765625, + 11546.923828125, + 12071.78515625, + 12596.6455078125, + 13121.505859375, + 13646.3662109375, + 14171.2255859375, + 14696.0859375, + 15220.9462890625, + 15745.806640625, + 16270.6669921875, + 16795.52734375, + 17320.38671875, + 17845.24609375, + 18370.107421875, + 18894.966796875, + 19419.828125, + 19944.6875, + 20469.548828125, + 20994.408203125, + 21519.26953125, + 22044.126953125, + 22568.98828125, + 23093.84765625, + 23618.708984375, + 24143.5703125, + 24668.4296875, + 25193.291015625, + 25718.150390625, + 26243.01171875, + 26767.87109375, + 27292.732421875, + 27817.58984375, + 28342.451171875, + 28867.310546875, + 29392.171875, + 29917.03125, + 30441.892578125, + 30966.751953125, + 31491.61328125, + 32016.47265625, + 32541.333984375, + 33066.19140625, + 33591.0546875, + 34115.9140625, + 34640.7734375, + 35165.63671875, + 35690.4921875, + 36215.35546875, + 36740.21484375, + 37265.078125, + 37789.93359375, + 38314.79296875, + 38839.65625, + 39364.515625, + 39889.375, + 40414.234375, + 40939.09765625, + 41463.95703125, + 41988.81640625, + 42513.67578125, + 43038.5390625, + 43563.3984375, + 44088.25390625, + 44613.1171875, + 45137.9765625, + 45662.83984375, + 46187.6953125, + 46712.55859375, + 47237.41796875, + 47762.28125, + 48287.140625, + 48812, + 49336.859375, + 49861.71875, + 50386.58203125, + 50911.4375, + 51436.30078125, + 51961.16015625, + 52486.0234375, + 53010.87890625, + 53535.7421875, + 54060.6015625, + 54585.46484375, + 55110.3203125, + 55635.1796875, + 56160.04296875, + 56684.90234375, + 57209.76171875, + 57734.62109375, + 58259.484375, + 58784.34375, + 59309.203125, + 59834.0625, + 60358.92578125, + 60883.78515625, + 61408.64453125, + 61933.50390625, + 62458.36328125, + 62983.2265625, + 63508.08203125, + 64032.9453125, + 64557.8046875, + 65082.66796875, + 65607.5234375, + 66132.3828125, + 66657.25, + 67182.109375, + 67706.96875, + 68231.828125, + 68756.6796875, + 69281.546875, + 69806.40625, + 70331.2734375, + 70856.125, + 71380.984375, + 71905.8515625, + 72430.7109375, + 72955.5703125, + 73480.4296875, + 74005.2890625, + 74530.15625, + 75055.0078125, + 75579.8671875, + 76104.734375, + 76629.5859375, + 77154.453125, + 77679.3125, + 78204.171875, + 78729.03125, + 79253.890625, + 79778.75, + 80303.6171875, + 80828.46875, + 81353.3359375, + 81878.1953125, + 82403.046875, + 82927.9140625, + 83452.7734375, + 83977.6328125, + 84502.4921875, + 85027.3515625, + 85552.21875, + 86077.078125, + 86601.9296875, + 87126.796875, + 87651.65625, + 88176.5078125, + 88701.375, + 89226.234375, + 89751.1015625, + 90275.953125, + 90800.8125, + 91325.6796875, + 91850.5390625, + 92375.390625, + 92900.2578125, + 93425.1171875, + 93949.9765625, + 94474.8359375, + 94999.6953125, + 95524.5625, + 96049.4140625, + 96574.28125, + 97099.140625, + 97624, + 98148.859375, + 98673.71875, + 99198.578125, + 99723.4375, + 100248.296875, + 100773.1640625, + 101298.0234375, + 101822.875, + 102347.7421875, + 102872.6015625, + 103397.4609375, + 103922.3203125, + 104447.1796875, + 104972.046875, + 105496.8984375, + 106021.7578125, + 106546.625, + 107071.484375, + 107596.3359375, + 108121.203125, + 108646.0625, + 109170.9296875, + 109695.78125, + 110220.640625, + 110745.5078125, + 111270.359375, + 111795.21875, + 112320.0859375, + 112844.9453125, + 113369.8046875, + 113894.6640625, + 114419.5234375, + 114944.390625, + 115469.2421875, + 115994.1015625, + 116518.96875, + 117043.828125, + 117568.6875, + 118093.546875, + 118618.40625, + 119143.265625, + 119668.125, + 120192.9921875, + 120717.8515625, + 121242.703125, + 121767.5703125, + 122292.4296875, + 122817.2890625, + 123342.1484375, + 123867.0078125, + 124391.875, + 124916.7265625, + 125441.5859375, + 125966.453125, + 126491.3125, + 127016.1640625, + 127541.03125, + 128065.890625, + 128590.7578125, + 129115.609375, + 129640.46875, + 130165.3359375, + 130690.1875, + 131215.046875, + 131739.90625, + 132264.765625, + 132789.640625, + 133314.5, + 133839.34375, + 134364.21875, + 134889.078125, + 135413.9375, + 135938.796875, + 136463.65625, + 136988.515625, + 137513.359375, + 138038.234375, + 138563.09375, + 139087.96875, + 139612.8125, + 140137.671875, + 140662.546875, + 141187.390625, + 141712.25, + 142237.125, + 142761.96875, + 143286.828125, + 143811.703125, + 144336.546875, + 144861.421875, + 145386.28125, + 145911.140625, + 146436, + 146960.859375, + 147485.71875, + 148010.578125, + 148535.4375, + 149060.3125, + 149585.15625, + 150110.015625, + 150634.890625, + 151159.734375, + 151684.59375, + 152209.46875, + 152734.3125, + 153259.171875, + 153784.046875, + 154308.90625, + 154833.75, + 155358.625, + 155883.484375, + 156408.34375, + 156933.203125, + 157458.0625, + 157982.921875, + 158507.78125, + 159032.640625, + 159557.5, + 160082.359375, + 160607.234375, + 161132.078125, + 161656.9375, + 162181.8125, + 162706.671875, + 163231.515625, + 163756.390625, + 164281.25, + 164806.09375, + 165330.96875, + 165855.828125, + 166380.671875, + 166905.546875, + 167430.40625, + 167955.265625, + 168480.125, + 169004.984375, + 169529.859375, + 170054.703125, + 170579.5625, + 171104.4375, + 171629.28125, + 172154.15625, + 172679.015625, + 173203.859375, + 173728.734375, + 174253.59375, + 174778.4375, + 175303.3125, + 175828.171875, + 176353.015625, + 176877.890625, + 177402.75, + 177927.625, + 178452.46875, + 178977.328125, + 179502.203125, + 180027.046875, + 180551.90625, + 181076.78125, + 181601.625, + 182126.484375, + 182651.359375, + 183176.203125, + 183701.078125, + 184225.9375, + 184750.78125, + 185275.65625, + 185800.515625, + 186325.375, + 186850.234375, + 187375.09375, + 187899.953125, + 188424.8125, + 188949.671875, + 189474.546875, + 189999.390625, + 190524.25, + 191049.125, + 191573.96875, + 192098.828125, + 192623.703125, + 193148.5625, + 193673.40625, + 194198.28125, + 194723.140625, + 195248, + 195772.859375, + 196297.71875, + 196822.578125, + 197347.4375, + 197872.296875, + 198397.15625, + 198922.015625, + 199446.875, + 199971.734375, + 200496.59375, + 201021.46875, + 201546.328125, + 202071.171875, + 202596.046875, + 203120.90625, + 203645.75, + 204170.625, + 204695.484375, + 205220.328125, + 205745.203125, + 206270.0625, + 206794.921875, + 207319.78125, + 207844.640625, + 208369.5, + 208894.359375, + 209419.21875, + 209944.09375, + 210468.9375, + 210993.796875, + 211518.671875, + 212043.515625, + 212568.390625, + 213093.25, + 213618.09375, + 214142.96875, + 214667.828125, + 215192.671875, + 215717.546875, + 216242.40625, + 216767.28125, + 217292.125, + 217816.984375, + 218341.859375, + 218866.703125, + 219391.5625, + 219916.4375, + 220441.28125, + 220966.140625, + 221491.015625, + 222015.859375, + 222540.71875, + 223065.59375, + 223590.4375, + 224115.3125, + 224640.171875, + 225165.03125, + 225689.890625, + 226214.75, + 226739.609375, + 227264.46875, + 227789.328125, + 228314.203125, + 228839.046875, + 229363.90625, + 229888.78125, + 230413.625, + 230938.484375, + 231463.359375, + 231988.203125, + 232513.0625, + 233037.9375, + 233562.796875, + 234087.65625, + 234612.515625, + 235137.375, + 235662.234375, + 236187.09375, + 236711.953125, + 237236.8125, + 237761.671875, + 238286.53125, + 238811.390625, + 239336.25, + 239861.125, + 240385.984375, + 240910.828125, + 241435.703125, + 241960.5625, + 242485.40625, + 243010.28125, + 243535.140625, + 244059.984375, + 244584.859375, + 245109.71875, + 245634.578125, + 246159.4375, + 246684.296875, + 247209.15625, + 247734.015625, + 248258.875, + 248783.75, + 249308.59375, + 249833.453125, + 250358.328125, + 250883.171875, + 251408.046875, + 251932.90625, + 252457.75, + 252982.625, + 253507.484375, + 254032.328125, + 254557.203125, + 255082.0625, + 255606.90625, + 256131.78125, + 256656.640625, + 257181.515625, + 257706.359375, + 258231.21875, + 258756.09375, + 259280.9375, + 259805.796875, + 260330.671875, + 260855.515625, + 261380.375, + 261905.25, + 262430.09375, + 262954.96875, + 263479.8125, + 264004.6875, + 264529.53125, + 265054.40625, + 265579.28125, + 266104.125, + 266629, + 267153.84375, + 267678.6875, + 268203.5625, + 268728.4375, + 269253.28125, + 269778.15625, + 270303, + 270827.875, + 271352.71875, + 271877.59375, + 272402.46875, + 272927.3125, + 273452.15625, + 273977.03125, + 274501.875, + 275026.71875, + 275551.625, + 276076.46875, + 276601.34375, + 277126.1875, + 277651.03125, + 278175.9375, + 278700.78125, + 279225.625, + 279750.5, + 280275.34375, + 280800.1875, + 281325.09375, + 281849.9375, + 282374.78125, + 282899.65625, + 283424.5, + 283949.375, + 284474.25, + 284999.09375, + 285523.9375, + 286048.8125, + 286573.65625, + 287098.53125, + 287623.40625, + 288148.25, + 288673.09375, + 289197.96875, + 289722.84375, + 290247.6875, + 290772.5625, + 291297.40625, + 291822.28125, + 292347.125, + 292872, + 293396.875, + 293921.71875, + 294446.5625, + 294971.4375, + 295496.3125, + 296021.15625, + 296546.03125, + 297070.875, + 297595.71875, + 298120.625, + 298645.46875, + 299170.3125, + 299695.1875, + 300220.03125, + 300744.875, + 301269.78125, + 301794.625, + 302319.46875, + 302844.34375, + 303369.1875, + 303894.03125, + 304418.9375, + 304943.78125, + 305468.625, + 305993.5, + 306518.34375, + 307043.25, + 307568.09375, + 308092.9375, + 308617.8125, + 309142.65625, + 309667.5, + 310192.40625, + 310717.25, + 311242.09375, + 311766.96875, + 312291.8125, + 312816.6875, + 313341.5625, + 313866.40625, + 314391.25, + 314916.125, + 315441, + 315965.84375, + 316490.71875, + 317015.5625, + 317540.40625, + 318065.28125, + 318590.15625, + 319115, + 319639.875, + 320164.71875, + 320689.5625, + 321214.46875, + 321739.3125, + 322264.15625, + 322789.03125, + 323313.875, + 323838.75, + 324363.625, + 324888.46875, + 325413.34375, + 325938.1875, + 326463.03125, + 326987.9375, + 327512.78125, + 328037.625, + 328562.5, + 329087.34375, + 329612.1875, + 330137.09375, + 330661.9375, + 331186.78125, + 331711.65625, + 332236.5, + 332761.34375, + 333286.25, + 333811.09375, + 334335.9375, + 334860.8125, + 335385.65625, + 335910.53125, + 336435.40625, + 336960.25, + 337485.09375, + 338009.96875, + 338534.84375, + 339059.71875, + 339584.5625, + 340109.40625, + 340634.28125, + 341159.125, + 341684, + 342208.875, + 342733.71875, + 343258.5625, + 343783.4375, + 344308.3125, + 344833.15625, + 345358.03125, + 345882.875, + 346407.71875, + 346932.59375, + 347457.46875, + 347982.3125, + 348507.1875, + 349032.03125, + 349556.875, + 350081.78125, + 350606.625, + 351131.46875, + 351656.34375, + 352181.1875, + 352706.03125, + 353230.9375, + 353755.78125, + 354280.65625, + 354805.5, + 355330.34375, + 355855.25, + 356380.09375, + 356904.9375, + 357429.8125, + 357954.65625, + 358479.5, + 359004.40625, + 359529.25, + 360054.09375, + 360578.96875, + 361103.8125, + 361628.6875, + 362153.5625, + 362678.40625, + 363203.25, + 363728.125, + 364252.96875, + 364777.84375, + 365302.71875, + 365827.5625, + 366352.40625, + 366877.28125, + 367402.15625, + 367927, + 368451.875, + 368976.71875, + 369501.5625, + 370026.4375, + 370551.3125, + 371076.1875, + 371601.03125, + 372125.875, + 372650.75, + 373175.625, + 373700.46875, + 374225.34375, + 374750.1875, + 375275.03125, + 375799.90625, + 376324.78125, + 376849.625, + 377374.5, + 377899.34375, + 378424.1875, + 378949.09375, + 379473.9375, + 379998.78125, + 380523.65625, + 381048.5, + 381573.34375, + 382098.25, + 382623.09375, + 383147.9375, + 383672.8125, + 384197.65625, + 384722.53125, + 385247.40625, + 385772.25, + 386297.125, + 386821.96875, + 387346.8125, + 387871.71875, + 388396.5625, + 388921.40625, + 389446.28125, + 389971.125, + 390496, + 391020.875, + 391545.71875, + 392070.5625, + 392595.4375, + 393120.28125, + 393645.15625, + 394170.03125, + 394694.875, + 395219.71875, + 395744.59375, + 396269.46875, + 396794.3125, + 397319.1875, + 397844.03125, + 398368.875, + 398893.75, + 399418.625, + 399943.46875, + 400468.34375, + 400993.1875, + 401518.0625, + 402042.9375, + 402567.78125, + 403092.65625, + 403617.5, + 404142.34375, + 404667.21875, + 405192.09375, + 405716.9375, + 406241.8125, + 406766.65625, + 407291.5, + 407816.40625, + 408341.25, + 408866.09375, + 409390.96875, + 409915.8125, + 410440.65625, + 410965.5625, + 411490.40625, + 412015.25, + 412540.125, + 413064.96875, + 413589.84375, + 414114.71875, + 414639.5625, + 415164.40625, + 415689.28125, + 416214.125, + 416739, + 417263.875, + 417788.71875, + 418313.59375, + 418838.4375, + 419363.3125, + 419888.1875, + 420413.03125, + 420937.875, + 421462.75, + 421987.59375, + 422512.46875, + 423037.34375, + 423562.1875, + 424087.03125, + 424611.90625, + 425136.78125, + 425661.625, + 426186.5, + 426711.34375, + 427236.1875, + 427761.09375, + 428285.9375, + 428810.78125, + 429335.65625, + 429860.5, + 430385.34375, + 430910.25, + 431435.09375, + 431959.9375, + 432484.8125, + 433009.65625, + 433534.5625, + 434059.40625, + 434584.25, + 435109.125, + 435633.96875, + 436158.8125, + 436683.71875, + 437208.5625, + 437733.40625, + 438258.28125, + 438783.125, + 439307.96875, + 439832.875, + 440357.71875, + 440882.5625, + 441407.4375, + 441932.28125, + 442457.15625, + 442982.03125, + 443506.875, + 444031.71875, + 444556.59375, + 445081.4375, + 445606.3125, + 446131.1875, + 446656.03125, + 447180.875, + 447705.75, + 448230.625, + 448755.5, + 449280.34375, + 449805.1875, + 450330.0625, + 450854.9375, + 451379.78125, + 451904.65625, + 452429.5, + 452954.34375, + 453479.21875, + 454004.09375, + 454528.9375, + 455053.8125, + 455578.65625, + 456103.5, + 456628.40625, + 457153.25, + 457678.09375, + 458202.96875, + 458727.8125, + 459252.65625, + 459777.5625, + 460302.40625, + 460827.25, + 461352.125, + 461876.96875, + 462401.8125, + 462926.71875, + 463451.5625, + 463976.40625, + 464501.28125, + 465026.125, + 465551.03125, + 466075.875, + 466600.71875, + 467125.59375, + 467650.4375, + 468175.3125, + 468700.1875, + 469225.03125, + 469749.875, + 470274.75, + 470799.59375, + 471324.46875, + 471849.34375, + 472374.1875, + 472899.03125, + 473423.90625, + 473948.78125, + 474473.625, + 474998.5, + 475523.34375, + 476048.1875, + 476573.0625, + 477097.9375, + 477622.78125, + 478147.65625, + 478672.5, + 479197.34375, + 479722.25, + 480247.09375, + 480771.96875, + 481296.8125, + 481821.65625, + 482346.53125, + 482871.40625, + 483396.25, + 483921.125, + 484445.96875, + 484970.8125, + 485495.71875, + 486020.5625, + 486545.40625, + 487070.28125, + 487595.125, + 488119.96875, + 488644.875, + 489169.71875, + 489694.5625, + 490219.4375, + 490744.28125, + 491269.15625, + 491794.03125, + 492318.875, + 492843.71875, + 493368.59375, + 493893.4375, + 494418.3125, + 494943.1875, + 495468.03125, + 495992.90625, + 496517.75, + 497042.625, + 497567.5, + 498092.34375, + 498617.1875, + 499142.0625, + 499666.90625, + 500191.78125, + 500716.65625, + 501241.5, + 501766.34375, + 502291.21875, + 502816.09375, + 503340.9375, + 503865.8125, + 504390.65625, + 504915.5, + 505440.375, + 505965.25, + 506490.09375, + 507014.96875, + 507539.8125, + 508064.65625, + 508589.5625, + 509114.40625, + 509639.25, + 510164.125, + 510688.96875, + 511213.8125, + 511738.71875, + 512263.5625, + 512788.4375, + 513313.28125, + 513838.125, + 514363.03125, + 514887.875, + 515412.71875, + 515937.59375, + 516462.4375, + 516987.28125, + 517512.1875, + 518037.03125, + 518561.875, + 519086.75, + 519611.59375, + 520136.46875, + 520661.34375, + 521186.1875, + 521711.03125, + 522235.90625, + 522760.75, + 523285.625, + 523810.5, + 524335.375, + 524860.1875, + 525385.0625, + 525909.9375, + 526434.75, + 526959.625, + 527484.5, + 528009.375, + 528534.25, + 529059.0625, + 529583.9375, + 530108.8125, + 530633.6875, + 531158.5625, + 531683.375, + 532208.25, + 532733.125, + 533258, + 533782.8125, + 534307.6875, + 534832.5625, + 535357.375, + 535882.3125, + 536407.125, + 536932, + 537456.875, + 537981.6875, + 538506.5625, + 539031.4375, + 539556.3125, + 540081.125, + 540606, + 541130.875, + 541655.75, + 542180.625, + 542705.4375, + 543230.3125, + 543755.1875, + 544280, + 544804.9375, + 545329.75, + 545854.625, + 546379.5, + 546904.3125, + 547429.1875, + 547954.0625, + 548478.9375, + 549003.75, + 549528.625, + 550053.4375, + 550578.375, + 551103.25, + 551628.125, + 552152.9375, + 552677.8125, + 553202.6875, + 553727.5, + 554252.375, + 554777.25, + 555302.0625, + 555826.9375, + 556351.875, + 556876.6875, + 557401.5625, + 557926.4375, + 558451.25, + 558976.125, + 559501, + 560025.8125, + 560550.6875, + 561075.5625, + 561600.375, + 562125.3125, + 562650.1875, + 563175, + 563699.875, + 564224.75, + 564749.5625, + 565274.4375, + 565799.3125, + 566324.125, + 566849, + 567373.875, + 567898.75, + 568423.625, + 568948.5, + 569473.3125, + 569998.1875, + 570523.0625, + 571047.875, + 571572.75, + 572097.625, + 572622.4375, + 573147.3125, + 573672.25, + 574197.0625, + 574721.9375, + 575246.8125, + 575771.625, + 576296.5, + 576821.375, + 577346.1875, + 577871.0625, + 578395.9375, + 578920.75, + 579445.6875, + 579970.5625, + 580495.375, + 581020.25, + 581545.125, + 582069.9375, + 582594.8125, + 583119.6875, + 583644.5625, + 584169.375, + 584694.25, + 585219.1875, + 585744, + 586268.875, + 586793.75, + 587318.5625, + 587843.4375, + 588368.3125, + 588893.125, + 589418, + 589942.875, + 590467.6875, + 590992.625, + 591517.5, + 592042.3125, + 592567.1875, + 593092.0625, + 593616.875, + 594141.75, + 594666.625, + 595191.4375, + 595716.3125, + 596241.25, + 596766.0625, + 597290.9375, + 597815.8125, + 598340.625, + 598865.5, + 599390.375, + 599915.1875, + 600440.0625, + 600964.9375, + 601489.75, + 602014.625, + 602539.5625, + 603064.375, + 603589.25, + 604114.125, + 604638.9375, + 605163.8125, + 605688.6875, + 606213.5, + 606738.375, + 607263.25, + 607788.0625, + 608313, + 608837.875, + 609362.6875, + 609887.5625, + 610412.4375, + 610937.25, + 611462.125, + 611987, + 612511.8125, + 613036.6875, + 613561.625, + 614086.5, + 614611.3125, + 615136.1875, + 615661.0625, + 616185.875, + 616710.75, + 617235.625, + 617760.4375, + 618285.3125, + 618810.1875, + 619335, + 619859.9375, + 620384.8125, + 620909.625, + 621434.5, + 621959.375, + 622484.1875, + 623009.0625, + 623533.9375, + 624058.75, + 624583.625, + 625108.5625, + 625633.375, + 626158.25, + 626683.125, + 627207.9375, + 627732.8125, + 628257.6875, + 628782.5, + 629307.375 + ], + "y": [ + 0, + 0, + 0, + 0, + 0, + 18.64515495300293, + 97.37421417236328, + 176.1032257080078, + 254.83229064941406, + 333.561279296875, + 412.2903137207031, + 491.0193176269531, + 569.7484130859375, + 648.4774780273438, + 727.2064819335938, + 805.935546875, + 884.6646118164062, + 963.3934936523438, + 1042.12255859375, + 1120.8515625, + 1199.5806884765625, + 1278.3095703125, + 1357.03857421875, + 1435.767822265625, + 1514.496826171875, + 1593.2259521484375, + 1671.9549560546875, + 1750.6839599609375, + 1829.4129638671875, + 1908.1419677734375, + 1986.87109375, + 2065.60009765625, + 2144.3291015625, + 2223.05810546875, + 2301.787109375, + 2380.51611328125, + 2459.2451171875, + 2537.974365234375, + 2616.703125, + 2695.432373046875, + 2774.161376953125, + 2852.890625, + 2931.619140625, + 3010.348388671875, + 3089.077392578125, + 3167.806396484375, + 3246.53564453125, + 3325.2646484375, + 3403.993896484375, + 3482.72265625, + 3561.451904296875, + 3640.180908203125, + 3718.909912109375, + 3797.638671875, + 3876.367919921875, + 3955.0966796875, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 3950, + 3950, + 3900, + 3850, + 3850, + 3800, + 3800, + 3750, + 3750, + 3700, + 3700, + 3650, + 3650, + 3600, + 3600, + 3550, + 3550, + 3500, + 3500, + 3450, + 3450, + 3400, + 3350, + 3350, + 3300, + 3300, + 3250, + 3250, + 3200, + 3200, + 3150, + 3150, + 3100, + 3100, + 3050, + 3050, + 3000, + 3000, + 2950, + 2950, + 2900, + 2900, + 2850, + 2800, + 2800, + 2750, + 2750, + 2700, + 2700, + 2650, + 2650, + 2600, + 2600, + 2550, + 2550, + 2500, + 2500, + 2450, + 2450, + 2400, + 2400, + 2350, + 2300, + 2300, + 2250, + 2250, + 2200, + 2200, + 2150, + 2150, + 2100, + 2100, + 2050, + 2050, + 2000, + 2000, + 1950, + 1950, + 1900, + 1900, + 1850, + 1850, + 1800, + 1750, + 1750, + 1700, + 1700, + 1650, + 1650, + 1600, + 1600, + 1550, + 1550, + 1500, + 1500, + 1450, + 1450, + 1400, + 1400, + 1350, + 1350, + 1300, + 1250, + 1250, + 1200, + 1200, + 1150, + 1150, + 1100, + 1100, + 1050, + 1050, + 1000, + 1000, + 950, + 950, + 900, + 900, + 850, + 850, + 800, + 800, + 750, + 700, + 700, + 650, + 650, + 600, + 600, + 550, + 550, + 500, + 500, + 450, + 450, + 400, + 400, + 350, + 350, + 300, + 300, + 250, + 250, + 200, + 150, + 150, + 100, + 100, + 50, + 50, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0 + ] + } + ], + "layout": { + "font": { + "color": "black", + "family": "Roboto Serif" + }, + "height": 600, + "hovermode": "x unified", + "images": [ + { + "sizex": 0.15, + "sizey": 0.15, + "source": "https://raw.githubusercontent.com/PolicyEngine/policyengine-app/master/src/images/logos/policyengine/blue.png", + "x": 1.1, + "xanchor": "right", + "xref": "paper", + "y": -0.15, + "yanchor": "bottom", + "yref": "paper" + } + ], + "modebar": { + "bgcolor": "rgba(0,0,0,0)", + "color": "rgba(0,0,0,0)" + }, + "template": { + "data": { + "bar": [ + { + "error_x": { + "color": "#2a3f5f" + }, + "error_y": { + "color": "#2a3f5f" + }, + "marker": { + "line": { + "color": "white", + "width": 0.5 + }, + "pattern": { + "fillmode": "overlay", + "size": 10, + "solidity": 0.2 + } + }, + "type": "bar" + } + ], + "barpolar": [ + { + "marker": { + "line": { + "color": "white", + "width": 0.5 + }, + "pattern": { + "fillmode": "overlay", + "size": 10, + "solidity": 0.2 + } + }, + "type": "barpolar" + } + ], + "carpet": [ + { + "aaxis": { + "endlinecolor": "#2a3f5f", + "gridcolor": "#C8D4E3", + "linecolor": "#C8D4E3", + "minorgridcolor": "#C8D4E3", + "startlinecolor": "#2a3f5f" + }, + "baxis": { + "endlinecolor": "#2a3f5f", + "gridcolor": "#C8D4E3", + "linecolor": "#C8D4E3", + "minorgridcolor": "#C8D4E3", + "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": "white", + "showlakes": true, + "showland": true, + "subunitcolor": "#C8D4E3" + }, + "hoverlabel": { + "align": "left" + }, + "hovermode": "closest", + "mapbox": { + "style": "light" + }, + "paper_bgcolor": "white", + "plot_bgcolor": "white", + "polar": { + "angularaxis": { + "gridcolor": "#EBF0F8", + "linecolor": "#EBF0F8", + "ticks": "" + }, + "bgcolor": "white", + "radialaxis": { + "gridcolor": "#EBF0F8", + "linecolor": "#EBF0F8", + "ticks": "" + } + }, + "scene": { + "xaxis": { + "backgroundcolor": "white", + "gridcolor": "#DFE8F3", + "gridwidth": 2, + "linecolor": "#EBF0F8", + "showbackground": true, + "ticks": "", + "zerolinecolor": "#EBF0F8" + }, + "yaxis": { + "backgroundcolor": "white", + "gridcolor": "#DFE8F3", + "gridwidth": 2, + "linecolor": "#EBF0F8", + "showbackground": true, + "ticks": "", + "zerolinecolor": "#EBF0F8" + }, + "zaxis": { + "backgroundcolor": "white", + "gridcolor": "#DFE8F3", + "gridwidth": 2, + "linecolor": "#EBF0F8", + "showbackground": true, + "ticks": "", + "zerolinecolor": "#EBF0F8" + } + }, + "shapedefaults": { + "line": { + "color": "#2a3f5f" + } + }, + "ternary": { + "aaxis": { + "gridcolor": "#DFE8F3", + "linecolor": "#A2B1C6", + "ticks": "" + }, + "baxis": { + "gridcolor": "#DFE8F3", + "linecolor": "#A2B1C6", + "ticks": "" + }, + "bgcolor": "white", + "caxis": { + "gridcolor": "#DFE8F3", + "linecolor": "#A2B1C6", + "ticks": "" + } + }, + "title": { + "x": 0.05 + }, + "xaxis": { + "automargin": true, + "gridcolor": "#EBF0F8", + "linecolor": "#EBF0F8", + "ticks": "", + "title": { + "standoff": 15 + }, + "zerolinecolor": "#EBF0F8", + "zerolinewidth": 2 + }, + "yaxis": { + "automargin": true, + "gridcolor": "#EBF0F8", + "linecolor": "#EBF0F8", + "ticks": "", + "title": { + "standoff": 15 + }, + "zerolinecolor": "#EBF0F8", + "zerolinewidth": 2 + } + } + }, + "title": { + "text": "Child Tax Credit by Employment Income
Family of 4 in California" + }, + "width": 800, + "xaxis": { + "tickformat": "$,.0f", + "title": { + "text": "Employment Income" + } + }, + "yaxis": { + "tickformat": "$,.0f", + "title": { + "text": "Child Tax Credit" + } + } + } + } + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "# Create a Plotly plot of CTC vs Employment Income\n", + "fig = go.Figure()\n", + "\n", + "fig.add_trace(go.Scatter(\n", + " x=income_range,\n", + " y=ctc_range,\n", + " mode='lines',\n", + " name='Child Tax Credit',\n", + " line=dict(color='#2C6496', width=3),\n", + " hovertemplate='Employment Income: $%{x:,.0f}
CTC: $%{y:,.0f}'\n", + "))\n", + "\n", + "fig.update_layout(\n", + " title='Child Tax Credit by Employment Income
Family of 4 in California',\n", + " xaxis_title='Employment Income',\n", + " yaxis_title='Child Tax Credit',\n", + " xaxis=dict(tickformat='$,.0f'),\n", + " yaxis=dict(tickformat='$,.0f'),\n", + " hovermode='x unified',\n", + " template='plotly_white'\n", + ")\n", + "\n", + "# Apply PolicyEngine formatting\n", + "fig = format_fig(fig)\n", + "fig.show()" + ] + }, + { + "cell_type": "markdown", + "id": "84083393", + "metadata": {}, + "source": [ + "## Summary\n", + "\n", + "This notebook demonstrated:\n", + "1. **Creating household situations** with proper entity relationships\n", + "2. **Running simulations** and calculating variables\n", + "3. **Understanding variable dependencies** through the calculation tree\n", + "4. **Creating and applying reforms** (both simple and complex)\n", + "5. **Comparing baseline and reform** scenarios\n", + "6. **Using calculate_dataframe** for efficient multi-variable analysis\n", + "7. **Calculating marginal tax rates** through incremental changes\n", + "8. **Using axes** to vary parameters and analyze responses\n", + "\n", + "Key takeaways:\n", + "- PolicyEngine uses a hierarchical entity structure (person → tax unit → household)\n", + "- Variables can depend on other variables, creating a calculation tree\n", + "- Reforms can modify parameters or add new calculation logic\n", + "- The Simulation class provides flexible methods for analysis\n", + "- DataFrames integration makes it easy to analyze results with pandas" + ] + }, + { + "cell_type": "markdown", + "id": "6a87d52a", + "metadata": {}, + "source": [ + "### Understanding the Trace Output\n", + "The trace shows:\n", + "- **Indentation** = dependency depth (more indented = deeper in calculation tree)\n", + "- **Variable names** with PERIOD and year (e.g., `ctc_value<2025, (default)>`)\n", + "- **Calculated values** as arrays - the array length indicates the entity level:\n", + " - Arrays with 4 values `[x, x, x, x]` = person-level (4 people in household)\n", + " - Arrays with 1 value `[x]` = tax unit or household level\n", + "- **Time periods** when the calculation applies\n", + "- **With max_depth=None**, the most deeply indented variables are inputs (from user, defaults, or data) rather than calculations\n" + ] + }, + { + "cell_type": "code", + "execution_count": 59, + "id": "fd3b7035", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "\n", + "CTC Maximum per child: $[ 0. 0. 2000. 2000.]\n", + "CTC Phase-out amount: $[0.]\n" + ] + } + ], + "source": [ + "# Now let's look at individual variables outside of the computation tree\n", + "ctc_child_individual_maximum = baseline_sim.calculate(\"ctc_child_individual_maximum\", PERIOD)\n", + "ctc_phase_out = baseline_sim.calculate(\"ctc_phase_out\", PERIOD)\n", + "\n", + "print(f\"\\nCTC Maximum per child: ${ctc_child_individual_maximum}\")\n", + "print(f\"CTC Phase-out amount: ${ctc_phase_out}\")" + ] + }, + { + "cell_type": "markdown", + "id": "1b8f4175", + "metadata": {}, + "source": [ + "## Part 4: Creating and Running Reforms\n", + "\n", + "### Parametric Reforms\n", + "The most common type of reform modifies existing parameters (tax rates, benefit amounts, thresholds, etc.):" + ] + }, + { + "cell_type": "markdown", + "id": "f73a6858", + "metadata": {}, + "source": [ + "### Understanding Parameter Types\n", + "\n", + "PolicyEngine uses different parameter types for various policy features. Here's how to work with each type:\n", + "\n", + "| Parameter Type | Example | Notes |\n", + "|---------------|---------|-------|\n", + "| **Single value** | `\"gov.irs.credits.ctc.amount.base[0].amount\": 3000`
`\"gov.irs.credits.ctc.phase_out.rate\": 0.05`
`\"gov.irs.credits.ctc.child.max_age\": 17` | Can be monetary amounts (USD), rates/percentages (as decimals: 5% = 0.05), ages (years), or other single values |\n", + "| **List parameter** | `\"gov.irs.credits.refundable\": [\"eitc\", \"refundable_ctc\"]` | Lists of values, often containing variable class names |\n", + "| **Scale parameter** | `\"gov.irs.credits.ctc.amount.base[0].amount\": 3000` | Graduated scales with thresholds and rates |\n", + "| **Breakdown parameters** | `\"gov.irs.credits.ctc.phase_out.threshold.JOINT\": 500000`| Parameters broken down by enums (e.g., filing status) or numeric ranges |" + ] + }, + { + "cell_type": "markdown", + "id": "a2bf5e5f", + "metadata": {}, + "source": [ + "### Accessing Scale Components\n", + "- `scale.thresholds`: list[float] - income thresholds\n", + "- `scale.rates`: list[float] - tax rates for each bracket\n", + "- `scale.amounts`: list[float] - fixed amounts (if applicable)\n", + "\n", + "Examples of accessing specific elements:\n", + "- `scale.thresholds[0]` - first threshold (usually 0)\n", + "- `scale.thresholds[-1]` - last threshold\n", + "- `scale.rates[2]` - rate for third bracket\n" + ] + }, + { + "cell_type": "code", + "execution_count": 60, + "id": "c531aaf1", + "metadata": {}, + "outputs": [], + "source": [ + "# Create a parametric reform that increases the CTC amount\n", + "# This is a simple parameter change - increasing CTC from $2,000 to $3,000 from 2025 forward\n", + "\n", + "ctc_reform = Reform.from_dict({\n", + " \"gov.irs.credits.ctc.amount.base[0].amount\": {\n", + " \"2025-01-01.2100-12-31\": 3000\n", + " }\n", + "}, country_id=\"us\")\n", + "\n", + "# You can also create more complex reforms with multiple parameters\n", + "comprehensive_reform = Reform.from_dict({\n", + " # Increase CTC amount\n", + " \"gov.irs.credits.ctc.amount.base[0].amount\": {\n", + " \"2025-01-01.2100-12-31\": 3000\n", + " },\n", + " # Make CTC fully refundable\n", + " \"gov.irs.credits.ctc.refundable.fully_refundable\": {\n", + " \"2025-01-01.2100-12-31\": True\n", + " },\n", + " # Increase phase-out threshold for JOINT filing statuses\n", + " \"gov.irs.credits.ctc.phase_out.threshold.JOINT\": {\n", + " \"2025-01-01.2100-12-31\": 500000\n", + " }\n", + "}, country_id=\"us\")" + ] + }, + { + "cell_type": "markdown", + "id": "92381be3", + "metadata": {}, + "source": [ + "### Running the Reformed Simulation\n" + ] + }, + { + "cell_type": "code", + "execution_count": 61, + "id": "9de46e04", + "metadata": {}, + "outputs": [], + "source": [ + "# Create a new simulation with the comprehensive reform\n", + "reformed_sim = Simulation(\n", + " situation=situation,\n", + " reform=comprehensive_reform\n", + ")" + ] + }, + { + "cell_type": "code", + "execution_count": 62, + "id": "1b6bd06f", + "metadata": {}, + "outputs": [], + "source": [ + "# Calculate the same variables under the reform\n", + "reformed_income_tax = reformed_sim.calculate(\"income_tax\", PERIOD)\n", + "reformed_ctc = reformed_sim.calculate(\"ctc_value\", PERIOD)\n", + "reformed_net_income = reformed_sim.calculate(\"household_net_income\", PERIOD)" + ] + }, + { + "cell_type": "code", + "execution_count": 63, + "id": "4febbe91", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "=== REFORM RESULTS ===\n", + "Federal Income Tax: $4,323\n", + "Child Tax Credit: $6,000\n", + "Household Net Income: $104,117\n" + ] + } + ], + "source": [ + "print(\"=== REFORM RESULTS ===\")\n", + "print(f\"Federal Income Tax: ${reformed_income_tax.sum():,.0f}\")\n", + "print(f\"Child Tax Credit: ${reformed_ctc.sum():,.0f}\")\n", + "print(f\"Household Net Income: ${reformed_net_income.sum():,.0f}\")" + ] + }, + { + "cell_type": "markdown", + "id": "77394ef5", + "metadata": {}, + "source": [ + "### Structural Reforms (Advanced)\n", + "PolicyEngine also supports structural reforms that change how variables are calculated, not just parameter values. Here's an example that adds a phase-out to the SALT deduction cap:" + ] + }, + { + "cell_type": "code", + "execution_count": 64, + "id": "e8ba1547", + "metadata": {}, + "outputs": [], + "source": [ + "from policyengine_us.model_api import *" + ] + }, + { + "cell_type": "code", + "execution_count": 65, + "id": "3bd5a755", + "metadata": {}, + "outputs": [], + "source": [ + "# Example of a structural reform that modifies the SALT cap calculation to add phase-out to the cap\n", + "class salt_cap(Variable):\n", + " value_type = float\n", + " entity = TaxUnit\n", + " label = \"SALT cap\"\n", + " unit = USD\n", + " definition_PERIOD = YEAR\n", + " reference = \"https://www.law.cornell.edu/uscode/text/26/164\"\n", + "\n", + " def formula(tax_unit, PERIOD, parameters):\n", + " filing_status = tax_unit(\"filing_status\", PERIOD)\n", + " p = parameters(\n", + " PERIOD\n", + " ).gov.irs.deductions.itemized.salt_and_real_estate\n", + " max_cap = p.cap[filing_status]\n", + " p_ref = parameters(PERIOD).gov.contrib.salt_phase_out\n", + " agi = tax_unit(\"adjusted_gross_income\", PERIOD)\n", + " agi_excess = max_(0, agi - p_ref.threshold[filing_status])\n", + " phase_out = p_ref.rate * agi_excess\n", + " phased_out_cap = max_(0, max_cap - phase_out)\n", + " if p_ref.floor.applies:\n", + " floor = p_ref.floor.amount[filing_status]\n", + " return max_(phased_out_cap, floor)\n", + " return phased_out_cap\n", + "\n", + "class reform(Reform):\n", + " def apply(self):\n", + " self.update_variable(salt_cap)\n", + "\n", + "# This would be applied as: Simulation(situation=situation, reform=reform())" + ] + }, + { + "cell_type": "markdown", + "id": "372a5d4e", + "metadata": {}, + "source": [ + "**Best Practice**: While structural reforms are powerful, it's often better to:\n", + "1. File an issue in the [policyengine-us repository](https://github.com/PolicyEngine/policyengine-us/issues)\n", + "2. Discuss with PolicyEngine developers\n", + "3. Submit a pull request to add new parameters\n", + "4. Convert your structural reform into a parametric one\n", + "\n", + "This approach ensures:\n", + "- Your reform integrates well with the existing codebase\n", + "- Other users can benefit from the new functionality\n", + "- The reform is properly tested and documented\n", + "- Future updates won't break your analysis" + ] + }, + { + "cell_type": "markdown", + "id": "999dc1eb", + "metadata": {}, + "source": [ + "## Part 5: Comparing Baseline and Reform\n" + ] + }, + { + "cell_type": "code", + "execution_count": 66, + "id": "5fc447b1", + "metadata": {}, + "outputs": [], + "source": [ + "# Create a comparison dataframe\n", + "comparison_data = {\n", + " \"Metric\": [\n", + " \"Federal Income Tax\",\n", + " \"Child Tax Credit\", \n", + " \"Household Net Income\",\n", + " ],\n", + " \"Baseline\": [\n", + " income_tax.sum(),\n", + " ctc.sum(),\n", + " household_net_income.sum(),\n", + " ],\n", + " \"Reform\": [\n", + " reformed_income_tax.sum(),\n", + " reformed_ctc.sum(),\n", + " reformed_net_income.sum(),\n", + " ]\n", + "}" + ] + }, + { + "cell_type": "code", + "execution_count": 67, + "id": "747cd298", + "metadata": {}, + "outputs": [], + "source": [ + "comparison_df = pd.DataFrame(comparison_data)\n", + "comparison_df[\"Change\"] = comparison_df[\"Reform\"] - comparison_df[\"Baseline\"]\n", + "comparison_df[\"% Change\"] = (comparison_df[\"Change\"] / comparison_df[\"Baseline\"].abs() * 100).round(2)" + ] + }, + { + "cell_type": "code", + "execution_count": 68, + "id": "6ef41abc", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "\n", + "=== COMPARISON ===\n", + " Metric Baseline Reform Change % Change\n", + " Federal Income Tax 6323.000000 4323.000000 -2000.0 -31.629999\n", + " Child Tax Credit 4000.000000 6000.000000 2000.0 50.000000\n", + "Household Net Income 102116.851562 104116.851562 2000.0 1.960000\n" + ] + } + ], + "source": [ + "print(\"\\n=== COMPARISON ===\")\n", + "print(comparison_df.to_string(index=False))" + ] + }, + { + "cell_type": "markdown", + "id": "f62b8c0a", + "metadata": {}, + "source": [ + "## Part 6: Using the calculate_dataframe Method\n", + "\n", + "The `calculate_dataframe` method provides a convenient way to calculate multiple variables at once and return them in a pandas DataFrame format. This is particularly useful when you need to analyze several variables together or export results for further analysis.\n", + "\n", + "### Understanding Entity Levels and Data Structure\n", + "\n", + "PolicyEngine organizes data hierarchically across different entity levels:\n", + "\n", + "| Entity Level | Description | Example Variables |\n", + "|-------------|-------------|-------------------|\n", + "| **Person** | Individual-level data | employment_income, age, is_disabled |\n", + "| **Tax Unit** | Tax filing unit data | income_tax, ctc_value, eitc |\n", + "| **SPM Unit** | Supplemental Poverty Measure unit | snap, housing_assistance |\n", + "| **Household** | Household-level data | household_net_income, rent |\n", + "\n", + "When using `calculate_dataframe`, it's crucial to understand how these entity levels interact:" + ] + }, + { + "cell_type": "code", + "execution_count": 69, + "id": "34ec5e06", + "metadata": {}, + "outputs": [], + "source": [ + "# For more complex analysis, we can use calculate_dataframe\n", + "# This returns a pandas DataFrame with multiple variables\n", + "\n", + "VARIABLES_TO_CALCULATE = [\n", + " \"employment_income\",\n", + " \"adjusted_gross_income\",\n", + " \"taxable_income\",\n", + " \"income_tax\",\n", + " \"ctc_value\",\n", + " \"eitc\",\n", + " \"snap\",\n", + " \"household_net_income\"\n", + "]" + ] + }, + { + "cell_type": "code", + "execution_count": 70, + "id": "557316f9", + "metadata": {}, + "outputs": [], + "source": [ + "PERIOD = 2025" + ] + }, + { + "cell_type": "markdown", + "id": "80daebf6", + "metadata": {}, + "source": [ + "### Default Behavior: Automatic Entity Resolution\n", + "\n", + "When you don't specify a `map_to` parameter, `calculate_dataframe` automatically determines the appropriate entity level based on the \"longest\" entity needed to represent all variables:" + ] + }, + { + "cell_type": "code", + "execution_count": 71, + "id": "e6b6d7d6", + "metadata": {}, + "outputs": [], + "source": [ + "# Calculate for baseline\n", + "baseline_df = baseline_sim.calculate_dataframe(\n", + " VARIABLES_TO_CALCULATE,\n", + " period=PERIOD\n", + ")\n", + "\n", + "# Calculate for reform\n", + "reform_df = reformed_sim.calculate_dataframe(\n", + " VARIABLES_TO_CALCULATE,\n", + " period=PERIOD\n", + ")" + ] + }, + { + "cell_type": "code", + "execution_count": 72, + "id": "52f41be8", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "\n", + "=== BASELINE DATAFRAME (No map_to specified) ===\n", + " employment_income adjusted_gross_income taxable_income income_tax \\\n", + "0 75000.0 120000.0 90000.0 6323.0 \n", + "1 45000.0 120000.0 90000.0 6323.0 \n", + "2 0.0 120000.0 90000.0 6323.0 \n", + "3 0.0 120000.0 90000.0 6323.0 \n", + "\n", + " ctc_value eitc snap household_net_income \n", + "0 4000.0 0.0 0.0 102116.851562 \n", + "1 4000.0 0.0 0.0 102116.851562 \n", + "2 4000.0 0.0 0.0 102116.851562 \n", + "3 4000.0 0.0 0.0 102116.851562 \n", + "\n", + "Shape: (4, 8)\n", + "Note: Since employment_income is person-level, all other variables are disaggregated to person-level\n" + ] + } + ], + "source": [ + "print(\"\\n=== BASELINE DATAFRAME (No map_to specified) ===\")\n", + "print(baseline_df)\n", + "print(f\"\\nShape: {baseline_df.shape}\")\n", + "print(\"Note: Since employment_income is person-level, all other variables are disaggregated to person-level\")" + ] + }, + { + "cell_type": "markdown", + "id": "3ec115b2", + "metadata": {}, + "source": [ + "In this case, because `employment_income` is a person-level variable and we have 4 people in our household, the DataFrame will have 4 rows. All higher-level variables (tax unit, household) will be repeated for each person they apply to." + ] + }, + { + "cell_type": "code", + "execution_count": 73, + "id": "4b966e17", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "\n", + "=== REFORM DATAFRAME ===\n", + " employment_income adjusted_gross_income taxable_income income_tax \\\n", + "0 75000.0 120000.0 90000.0 4323.0 \n", + "1 45000.0 120000.0 90000.0 4323.0 \n", + "2 0.0 120000.0 90000.0 4323.0 \n", + "3 0.0 120000.0 90000.0 4323.0 \n", + "\n", + " ctc_value eitc snap household_net_income \n", + "0 6000.0 0.0 0.0 104116.851562 \n", + "1 6000.0 0.0 0.0 104116.851562 \n", + "2 6000.0 0.0 0.0 104116.851562 \n", + "3 6000.0 0.0 0.0 104116.851562 \n" + ] + } + ], + "source": [ + "print(\"\\n=== REFORM DATAFRAME ===\")\n", + "print(reform_df)" + ] + }, + { + "cell_type": "markdown", + "id": "4c9d1152", + "metadata": {}, + "source": [ + "### Using map_to Parameter for Aggregation\n", + "\n", + "The `map_to` parameter allows you to aggregate all variables to a specific entity level. This is extremely useful when you want to analyze data at the household or tax unit level:" + ] + }, + { + "cell_type": "code", + "execution_count": 74, + "id": "0781b09c", + "metadata": {}, + "outputs": [], + "source": [ + "# Calculate for baseline\n", + "baseline_person_df = baseline_sim.calculate_dataframe(\n", + " VARIABLES_TO_CALCULATE,\n", + " map_to=\"person\",\n", + " period=PERIOD\n", + ")" + ] + }, + { + "cell_type": "code", + "execution_count": 75, + "id": "31197f1a", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "\n", + "=== BASELINE DATAFRAME (Mapped to person) ===\n", + " employment_income adjusted_gross_income taxable_income income_tax \\\n", + "0 75000.0 120000.0 90000.0 6323.0 \n", + "1 45000.0 120000.0 90000.0 6323.0 \n", + "2 0.0 120000.0 90000.0 6323.0 \n", + "3 0.0 120000.0 90000.0 6323.0 \n", + "\n", + " ctc_value eitc snap household_net_income \n", + "0 4000.0 0.0 0.0 102116.851562 \n", + "1 4000.0 0.0 0.0 102116.851562 \n", + "2 4000.0 0.0 0.0 102116.851562 \n", + "3 4000.0 0.0 0.0 102116.851562 \n", + "\n", + "Shape: (4, 8)\n", + "Note: All variables are now aggregated to person level (4 rows)\n" + ] + } + ], + "source": [ + "print(\"\\n=== BASELINE DATAFRAME (Mapped to person) ===\")\n", + "print(baseline_person_df)\n", + "print(f\"\\nShape: {baseline_person_df.shape}\")\n", + "print(\"Note: All variables are now aggregated to person level (4 rows)\")" + ] + }, + { + "cell_type": "code", + "execution_count": 76, + "id": "85346780", + "metadata": {}, + "outputs": [], + "source": [ + "# Calculate for baseline\n", + "baseline_household_df = baseline_sim.calculate_dataframe(\n", + " VARIABLES_TO_CALCULATE,\n", + " map_to=\"household\",\n", + " period=PERIOD\n", + ")" + ] + }, + { + "cell_type": "code", + "execution_count": 77, + "id": "52753049", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "\n", + "=== BASELINE DATAFRAME (Mapped to household) ===\n", + " employment_income adjusted_gross_income taxable_income income_tax \\\n", + "0 120000.0 120000.0 90000.0 6323.0 \n", + "\n", + " ctc_value eitc snap household_net_income \n", + "0 4000.0 0.0 0.0 102116.851562 \n", + "\n", + "Shape: (1, 8)\n", + "Note: All variables are now aggregated to household level (1 row)\n" + ] + } + ], + "source": [ + "print(\"\\n=== BASELINE DATAFRAME (Mapped to household) ===\")\n", + "print(baseline_household_df)\n", + "print(f\"\\nShape: {baseline_household_df.shape}\")\n", + "print(\"Note: All variables are now aggregated to household level (1 row)\")" + ] + }, + { + "cell_type": "markdown", + "id": "4a4a7d25", + "metadata": {}, + "source": [ + "### How map_to Aggregation Works\n", + "\n", + "When you use `map_to`, PolicyEngine performs the following operations:\n", + "\n", + "1. **Person-level variables** → Summed within the target entity\n", + " - Example: `employment_income` for all 4 people is summed to get total household employment income\n", + "\n", + "2. **Same-level variables** → Kept as-is\n", + " - Example: `household_net_income` is already at household level, so no change\n", + "\n", + "3. **Intermediate-level variables** → Summed to the target level\n", + " - Example: If there were multiple tax units, their `income_tax` would be summed" + ] + }, + { + "cell_type": "code", + "execution_count": 78, + "id": "66524251", + "metadata": {}, + "outputs": [], + "source": [ + "# Example: Export-ready DataFrame\n", + "export_df = baseline_sim.calculate_dataframe(\n", + " VARIABLES_TO_CALCULATE,\n", + " map_to=\"household\",\n", + " period=PERIOD\n", + ")\n", + "\n", + "# Add identifiers and save\n", + "export_df[\"household_id\"] = \"household\"\n", + "export_df[\"scenario\"] = \"baseline\"\n", + "\n", + "# You could save this to CSV:\n", + "export_df.to_csv(\"household_analysis.csv\", index=False)" + ] + }, + { + "cell_type": "markdown", + "id": "27200583", + "metadata": {}, + "source": [ + "## Part 7: Creating Earnings Variation Charts\n", + "\n", + "### Note: Using PolicyEngine's Web Interface for Earnings Variation\n", + "You can also generate earnings variation code directly from PolicyEngine's web interface:\n", + "1. Create your household at https://policyengine.org/us/household\n", + "2. Toggle \"Include earnings variation\" in the \"Reproduce in Python\" section\n", + "3. Copy the generated code with axes already configured\n", + "\n", + "Here's an example of what the generated code looks like:" + ] + }, + { + "cell_type": "code", + "execution_count": 79, + "id": "904110de", + "metadata": {}, + "outputs": [], + "source": [ + "# Example from PolicyEngine's \"Reproduce in Python\" with earnings variation\n", + "situation_with_axes = {\n", + " \"people\": {\n", + " \"you\": {\n", + " \"age\": {\n", + " \"2025\": 35\n", + " }\n", + " },\n", + " \"your partner\": {\n", + " \"age\": {\n", + " \"2025\": 33\n", + " }\n", + " },\n", + " \"your first dependent\": {\n", + " \"age\": {\n", + " \"2025\": 10\n", + " }\n", + " },\n", + " \"your second dependent\": {\n", + " \"age\": {\n", + " \"2025\": 7\n", + " }\n", + " }\n", + " },\n", + " \"families\": {\n", + " \"your family\": {\n", + " \"members\": [\n", + " \"you\",\n", + " \"your partner\",\n", + " \"your first dependent\",\n", + " \"your second dependent\"\n", + " ]\n", + " }\n", + " },\n", + " \"marital_units\": {\n", + " \"your marital unit\": {\n", + " \"members\": [\n", + " \"you\",\n", + " \"your partner\"\n", + " ]\n", + " },\n", + " \"your first dependent's marital unit\": {\n", + " \"members\": [\n", + " \"your first dependent\"\n", + " ],\n", + " \"marital_unit_id\": {\n", + " \"2025\": 1\n", + " }\n", + " },\n", + " \"your second dependent's marital unit\": {\n", + " \"members\": [\n", + " \"your second dependent\"\n", + " ],\n", + " \"marital_unit_id\": {\n", + " \"2025\": 2\n", + " }\n", + " }\n", + " },\n", + " \"tax_units\": {\n", + " \"your tax unit\": {\n", + " \"members\": [\n", + " \"you\",\n", + " \"your partner\",\n", + " \"your first dependent\",\n", + " \"your second dependent\"\n", + " ]\n", + " }\n", + " },\n", + " \"spm_units\": {\n", + " \"your household\": {\n", + " \"members\": [\n", + " \"you\",\n", + " \"your partner\",\n", + " \"your first dependent\",\n", + " \"your second dependent\"\n", + " ]\n", + " }\n", + " },\n", + " \"households\": {\n", + " \"your household\": {\n", + " \"members\": [\n", + " \"you\",\n", + " \"your partner\",\n", + " \"your first dependent\",\n", + " \"your second dependent\"\n", + " ],\n", + " \"state_name\": {\n", + " \"2025\": \"CA\"\n", + " }\n", + " }\n", + " },\n", + " \"axes\": [\n", + " [\n", + " {\n", + " \"name\": \"employment_income\",\n", + " \"count\": 1200,\n", + " \"min\": 0,\n", + " \"max\": 600000\n", + " }\n", + " ]\n", + " ]\n", + "}" + ] + }, + { + "cell_type": "markdown", + "id": "a0451dea", + "metadata": {}, + "source": [ + "### Varying inputs with **axes**\n", + "\n", + "PolicyEngine inherits **axes** from OpenFisca, letting us replicate the household situation over a grid of values.\n", + "\n", + "| Key | Meaning |\n", + "|-----|---------|\n", + "| `name` | Variable to iterate over |\n", + "| `count` | Number of equally spaced points |\n", + "| `min` | Interval start |\n", + "| `max` | Interval end |\n", + "\n", + "See the OpenFisca documentation for details: " + ] + }, + { + "cell_type": "markdown", + "id": "e98232cf", + "metadata": {}, + "source": [ + "### Note: Flexibility of Axes Variables\n", + "You can vary **ANY** variable in axes, not just employment_income! Examples include:\n", + "- `rent`\n", + "- `mortgage_interest` \n", + "- `medical_expenses`\n", + "- `childcare_expenses`\n", + "\n", + "Just change the \"name\" parameter in the axes configuration." + ] + }, + { + "cell_type": "code", + "execution_count": 80, + "id": "6476cefa", + "metadata": {}, + "outputs": [], + "source": [ + "# Create simulation with axes\n", + "axes_sim = Simulation(situation=situation_with_axes)" + ] + }, + { + "cell_type": "code", + "execution_count": 81, + "id": "7d3c93f7", + "metadata": {}, + "outputs": [], + "source": [ + "# Calculate variables across the income range\n", + "income_range = axes_sim.calculate(\"employment_income\", map_to=\"household\", period=PERIOD)\n", + "net_income_range = axes_sim.calculate(\"household_net_income\", map_to=\"household\", period=PERIOD)\n", + "ctc_range = axes_sim.calculate(\"ctc_value\", map_to=\"household\", period=PERIOD)\n", + "eitc_range = axes_sim.calculate(\"eitc\", map_to=\"household\", period=PERIOD)\n", + "snap_range = axes_sim.calculate(\"snap\", map_to=\"household\", period=PERIOD)" + ] + }, + { + "cell_type": "code", + "execution_count": 82, + "id": "b1be657d", + "metadata": {}, + "outputs": [], + "source": [ + "# Create a dataframe for analysis\n", + "results_df = pd.DataFrame({\n", + " \"Employment Income\": income_range,\n", + " \"Net Income\": net_income_range,\n", + " \"CTC\": ctc_range,\n", + " \"EITC\": eitc_range,\n", + " \"SNAP\": snap_range\n", + "})" + ] + }, + { + "cell_type": "code", + "execution_count": 83, + "id": "f5e1a1d0", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "\n", + "=== INCOME VARIATION RESULTS (Sample) ===\n", + " Employment Income Net Income CTC EITC SNAP\n", + "0 0.000000 26000.064453 0.0 0.000000 7710.931641\n", + "1 524.860229 26749.869141 0.0 209.944092 7587.632324\n", + "2 1049.720459 27496.976562 0.0 419.888184 7461.632324\n", + "3 1574.580688 28244.080078 0.0 629.832275 7335.632324\n", + "4 2099.440918 28991.185547 0.0 839.776367 7209.632324\n" + ] + } + ], + "source": [ + "# Show sample of results\n", + "print(\"\\n=== INCOME VARIATION RESULTS (Sample) ===\")\n", + "print(results_df.head()) # Show the first 5 rows" + ] + }, + { + "cell_type": "markdown", + "id": "bfb80419", + "metadata": {}, + "source": [ + "### Parallel and perpendicular axes\n", + "\n", + "* **Parallel axes** – objects inside the **same** inner list. Variables change **together** (lock‑step). \n", + "* **Perpendicular axes** – objects in **separate** inner lists. Variables change **independently** creating a Cartesian product." + ] + }, + { + "cell_type": "code", + "execution_count": 84, + "id": "d4540c7e", + "metadata": {}, + "outputs": [], + "source": [ + "# One parallel axis (employment_income)\n", + "example_axes = [[\n", + " {\"name\": \"employment_income\", \"count\": 1200, \"min\": 0, \"max\": 600_000}\n", + "]]\n", + "\n", + "# Two parallel axes (income and childcare cost) changing together\n", + "example_axes = [[\n", + " {\"name\": \"employment_income\", \"count\": 1200, \"min\": 0, \"max\": 600_000},\n", + " {\"name\": \"childcare_expenses\", \"count\": 1200, \"min\": 0, \"max\": 20_000}\n", + "]]\n", + "\n", + "# Two perpendicular axes (income × age) – 1 200 × 10 = 12 000 variants\n", + "example_axes = [\n", + " [{\"name\": \"employment_income\", \"count\": 1200, \"min\": 0, \"max\": 600_000}],\n", + " [{\"name\": \"age\", \"index\": 0, \"count\": 10, \"min\": 18, \"max\": 65}]\n", + "]" + ] + }, + { + "cell_type": "markdown", + "id": "a7d8ead9", + "metadata": {}, + "source": [ + "### Visualizing the data " + ] + }, + { + "cell_type": "code", + "execution_count": 85, + "id": "b6aac408", + "metadata": {}, + "outputs": [], + "source": [ + "import plotly.graph_objects as go\n", + "from policyengine_core.charts import format_fig" + ] + }, + { + "cell_type": "code", + "execution_count": 86, + "id": "82a06b36", + "metadata": {}, + "outputs": [ + { + "data": { + "application/vnd.plotly.v1+json": { + "config": { + "plotlyServerURL": "https://plot.ly" + }, + "data": [ + { + "hovertemplate": "Employment Income: $%{x:,.0f}
CTC: $%{y:,.0f}", + "line": { + "color": "#2C6496", + "width": 3 + }, + "mode": "lines", + "name": "Child Tax Credit", + "type": "scatter", + "x": [ + 0, + 524.8602294921875, + 1049.720458984375, + 1574.5806884765625, + 2099.44091796875, + 2624.301025390625, + 3149.161376953125, + 3674.021484375, + 4198.8818359375, + 4723.74169921875, + 5248.60205078125, + 5773.4619140625, + 6298.32275390625, + 6823.18310546875, + 7348.04296875, + 7872.9033203125, + 8397.763671875, + 8922.623046875, + 9447.4833984375, + 9972.34375, + 10497.2041015625, + 11022.0634765625, + 11546.923828125, + 12071.78515625, + 12596.6455078125, + 13121.505859375, + 13646.3662109375, + 14171.2255859375, + 14696.0859375, + 15220.9462890625, + 15745.806640625, + 16270.6669921875, + 16795.52734375, + 17320.38671875, + 17845.24609375, + 18370.107421875, + 18894.966796875, + 19419.828125, + 19944.6875, + 20469.548828125, + 20994.408203125, + 21519.26953125, + 22044.126953125, + 22568.98828125, + 23093.84765625, + 23618.708984375, + 24143.5703125, + 24668.4296875, + 25193.291015625, + 25718.150390625, + 26243.01171875, + 26767.87109375, + 27292.732421875, + 27817.58984375, + 28342.451171875, + 28867.310546875, + 29392.171875, + 29917.03125, + 30441.892578125, + 30966.751953125, + 31491.61328125, + 32016.47265625, + 32541.333984375, + 33066.19140625, + 33591.0546875, + 34115.9140625, + 34640.7734375, + 35165.63671875, + 35690.4921875, + 36215.35546875, + 36740.21484375, + 37265.078125, + 37789.93359375, + 38314.79296875, + 38839.65625, + 39364.515625, + 39889.375, + 40414.234375, + 40939.09765625, + 41463.95703125, + 41988.81640625, + 42513.67578125, + 43038.5390625, + 43563.3984375, + 44088.25390625, + 44613.1171875, + 45137.9765625, + 45662.83984375, + 46187.6953125, + 46712.55859375, + 47237.41796875, + 47762.28125, + 48287.140625, + 48812, + 49336.859375, + 49861.71875, + 50386.58203125, + 50911.4375, + 51436.30078125, + 51961.16015625, + 52486.0234375, + 53010.87890625, + 53535.7421875, + 54060.6015625, + 54585.46484375, + 55110.3203125, + 55635.1796875, + 56160.04296875, + 56684.90234375, + 57209.76171875, + 57734.62109375, + 58259.484375, + 58784.34375, + 59309.203125, + 59834.0625, + 60358.92578125, + 60883.78515625, + 61408.64453125, + 61933.50390625, + 62458.36328125, + 62983.2265625, + 63508.08203125, + 64032.9453125, + 64557.8046875, + 65082.66796875, + 65607.5234375, + 66132.3828125, + 66657.25, + 67182.109375, + 67706.96875, + 68231.828125, + 68756.6796875, + 69281.546875, + 69806.40625, + 70331.2734375, + 70856.125, + 71380.984375, + 71905.8515625, + 72430.7109375, + 72955.5703125, + 73480.4296875, + 74005.2890625, + 74530.15625, + 75055.0078125, + 75579.8671875, + 76104.734375, + 76629.5859375, + 77154.453125, + 77679.3125, + 78204.171875, + 78729.03125, + 79253.890625, + 79778.75, + 80303.6171875, + 80828.46875, + 81353.3359375, + 81878.1953125, + 82403.046875, + 82927.9140625, + 83452.7734375, + 83977.6328125, + 84502.4921875, + 85027.3515625, + 85552.21875, + 86077.078125, + 86601.9296875, + 87126.796875, + 87651.65625, + 88176.5078125, + 88701.375, + 89226.234375, + 89751.1015625, + 90275.953125, + 90800.8125, + 91325.6796875, + 91850.5390625, + 92375.390625, + 92900.2578125, + 93425.1171875, + 93949.9765625, + 94474.8359375, + 94999.6953125, + 95524.5625, + 96049.4140625, + 96574.28125, + 97099.140625, + 97624, + 98148.859375, + 98673.71875, + 99198.578125, + 99723.4375, + 100248.296875, + 100773.1640625, + 101298.0234375, + 101822.875, + 102347.7421875, + 102872.6015625, + 103397.4609375, + 103922.3203125, + 104447.1796875, + 104972.046875, + 105496.8984375, + 106021.7578125, + 106546.625, + 107071.484375, + 107596.3359375, + 108121.203125, + 108646.0625, + 109170.9296875, + 109695.78125, + 110220.640625, + 110745.5078125, + 111270.359375, + 111795.21875, + 112320.0859375, + 112844.9453125, + 113369.8046875, + 113894.6640625, + 114419.5234375, + 114944.390625, + 115469.2421875, + 115994.1015625, + 116518.96875, + 117043.828125, + 117568.6875, + 118093.546875, + 118618.40625, + 119143.265625, + 119668.125, + 120192.9921875, + 120717.8515625, + 121242.703125, + 121767.5703125, + 122292.4296875, + 122817.2890625, + 123342.1484375, + 123867.0078125, + 124391.875, + 124916.7265625, + 125441.5859375, + 125966.453125, + 126491.3125, + 127016.1640625, + 127541.03125, + 128065.890625, + 128590.7578125, + 129115.609375, + 129640.46875, + 130165.3359375, + 130690.1875, + 131215.046875, + 131739.90625, + 132264.765625, + 132789.640625, + 133314.5, + 133839.34375, + 134364.21875, + 134889.078125, + 135413.9375, + 135938.796875, + 136463.65625, + 136988.515625, + 137513.359375, + 138038.234375, + 138563.09375, + 139087.96875, + 139612.8125, + 140137.671875, + 140662.546875, + 141187.390625, + 141712.25, + 142237.125, + 142761.96875, + 143286.828125, + 143811.703125, + 144336.546875, + 144861.421875, + 145386.28125, + 145911.140625, + 146436, + 146960.859375, + 147485.71875, + 148010.578125, + 148535.4375, + 149060.3125, + 149585.15625, + 150110.015625, + 150634.890625, + 151159.734375, + 151684.59375, + 152209.46875, + 152734.3125, + 153259.171875, + 153784.046875, + 154308.90625, + 154833.75, + 155358.625, + 155883.484375, + 156408.34375, + 156933.203125, + 157458.0625, + 157982.921875, + 158507.78125, + 159032.640625, + 159557.5, + 160082.359375, + 160607.234375, + 161132.078125, + 161656.9375, + 162181.8125, + 162706.671875, + 163231.515625, + 163756.390625, + 164281.25, + 164806.09375, + 165330.96875, + 165855.828125, + 166380.671875, + 166905.546875, + 167430.40625, + 167955.265625, + 168480.125, + 169004.984375, + 169529.859375, + 170054.703125, + 170579.5625, + 171104.4375, + 171629.28125, + 172154.15625, + 172679.015625, + 173203.859375, + 173728.734375, + 174253.59375, + 174778.4375, + 175303.3125, + 175828.171875, + 176353.015625, + 176877.890625, + 177402.75, + 177927.625, + 178452.46875, + 178977.328125, + 179502.203125, + 180027.046875, + 180551.90625, + 181076.78125, + 181601.625, + 182126.484375, + 182651.359375, + 183176.203125, + 183701.078125, + 184225.9375, + 184750.78125, + 185275.65625, + 185800.515625, + 186325.375, + 186850.234375, + 187375.09375, + 187899.953125, + 188424.8125, + 188949.671875, + 189474.546875, + 189999.390625, + 190524.25, + 191049.125, + 191573.96875, + 192098.828125, + 192623.703125, + 193148.5625, + 193673.40625, + 194198.28125, + 194723.140625, + 195248, + 195772.859375, + 196297.71875, + 196822.578125, + 197347.4375, + 197872.296875, + 198397.15625, + 198922.015625, + 199446.875, + 199971.734375, + 200496.59375, + 201021.46875, + 201546.328125, + 202071.171875, + 202596.046875, + 203120.90625, + 203645.75, + 204170.625, + 204695.484375, + 205220.328125, + 205745.203125, + 206270.0625, + 206794.921875, + 207319.78125, + 207844.640625, + 208369.5, + 208894.359375, + 209419.21875, + 209944.09375, + 210468.9375, + 210993.796875, + 211518.671875, + 212043.515625, + 212568.390625, + 213093.25, + 213618.09375, + 214142.96875, + 214667.828125, + 215192.671875, + 215717.546875, + 216242.40625, + 216767.28125, + 217292.125, + 217816.984375, + 218341.859375, + 218866.703125, + 219391.5625, + 219916.4375, + 220441.28125, + 220966.140625, + 221491.015625, + 222015.859375, + 222540.71875, + 223065.59375, + 223590.4375, + 224115.3125, + 224640.171875, + 225165.03125, + 225689.890625, + 226214.75, + 226739.609375, + 227264.46875, + 227789.328125, + 228314.203125, + 228839.046875, + 229363.90625, + 229888.78125, + 230413.625, + 230938.484375, + 231463.359375, + 231988.203125, + 232513.0625, + 233037.9375, + 233562.796875, + 234087.65625, + 234612.515625, + 235137.375, + 235662.234375, + 236187.09375, + 236711.953125, + 237236.8125, + 237761.671875, + 238286.53125, + 238811.390625, + 239336.25, + 239861.125, + 240385.984375, + 240910.828125, + 241435.703125, + 241960.5625, + 242485.40625, + 243010.28125, + 243535.140625, + 244059.984375, + 244584.859375, + 245109.71875, + 245634.578125, + 246159.4375, + 246684.296875, + 247209.15625, + 247734.015625, + 248258.875, + 248783.75, + 249308.59375, + 249833.453125, + 250358.328125, + 250883.171875, + 251408.046875, + 251932.90625, + 252457.75, + 252982.625, + 253507.484375, + 254032.328125, + 254557.203125, + 255082.0625, + 255606.90625, + 256131.78125, + 256656.640625, + 257181.515625, + 257706.359375, + 258231.21875, + 258756.09375, + 259280.9375, + 259805.796875, + 260330.671875, + 260855.515625, + 261380.375, + 261905.25, + 262430.09375, + 262954.96875, + 263479.8125, + 264004.6875, + 264529.53125, + 265054.40625, + 265579.28125, + 266104.125, + 266629, + 267153.84375, + 267678.6875, + 268203.5625, + 268728.4375, + 269253.28125, + 269778.15625, + 270303, + 270827.875, + 271352.71875, + 271877.59375, + 272402.46875, + 272927.3125, + 273452.15625, + 273977.03125, + 274501.875, + 275026.71875, + 275551.625, + 276076.46875, + 276601.34375, + 277126.1875, + 277651.03125, + 278175.9375, + 278700.78125, + 279225.625, + 279750.5, + 280275.34375, + 280800.1875, + 281325.09375, + 281849.9375, + 282374.78125, + 282899.65625, + 283424.5, + 283949.375, + 284474.25, + 284999.09375, + 285523.9375, + 286048.8125, + 286573.65625, + 287098.53125, + 287623.40625, + 288148.25, + 288673.09375, + 289197.96875, + 289722.84375, + 290247.6875, + 290772.5625, + 291297.40625, + 291822.28125, + 292347.125, + 292872, + 293396.875, + 293921.71875, + 294446.5625, + 294971.4375, + 295496.3125, + 296021.15625, + 296546.03125, + 297070.875, + 297595.71875, + 298120.625, + 298645.46875, + 299170.3125, + 299695.1875, + 300220.03125, + 300744.875, + 301269.78125, + 301794.625, + 302319.46875, + 302844.34375, + 303369.1875, + 303894.03125, + 304418.9375, + 304943.78125, + 305468.625, + 305993.5, + 306518.34375, + 307043.25, + 307568.09375, + 308092.9375, + 308617.8125, + 309142.65625, + 309667.5, + 310192.40625, + 310717.25, + 311242.09375, + 311766.96875, + 312291.8125, + 312816.6875, + 313341.5625, + 313866.40625, + 314391.25, + 314916.125, + 315441, + 315965.84375, + 316490.71875, + 317015.5625, + 317540.40625, + 318065.28125, + 318590.15625, + 319115, + 319639.875, + 320164.71875, + 320689.5625, + 321214.46875, + 321739.3125, + 322264.15625, + 322789.03125, + 323313.875, + 323838.75, + 324363.625, + 324888.46875, + 325413.34375, + 325938.1875, + 326463.03125, + 326987.9375, + 327512.78125, + 328037.625, + 328562.5, + 329087.34375, + 329612.1875, + 330137.09375, + 330661.9375, + 331186.78125, + 331711.65625, + 332236.5, + 332761.34375, + 333286.25, + 333811.09375, + 334335.9375, + 334860.8125, + 335385.65625, + 335910.53125, + 336435.40625, + 336960.25, + 337485.09375, + 338009.96875, + 338534.84375, + 339059.71875, + 339584.5625, + 340109.40625, + 340634.28125, + 341159.125, + 341684, + 342208.875, + 342733.71875, + 343258.5625, + 343783.4375, + 344308.3125, + 344833.15625, + 345358.03125, + 345882.875, + 346407.71875, + 346932.59375, + 347457.46875, + 347982.3125, + 348507.1875, + 349032.03125, + 349556.875, + 350081.78125, + 350606.625, + 351131.46875, + 351656.34375, + 352181.1875, + 352706.03125, + 353230.9375, + 353755.78125, + 354280.65625, + 354805.5, + 355330.34375, + 355855.25, + 356380.09375, + 356904.9375, + 357429.8125, + 357954.65625, + 358479.5, + 359004.40625, + 359529.25, + 360054.09375, + 360578.96875, + 361103.8125, + 361628.6875, + 362153.5625, + 362678.40625, + 363203.25, + 363728.125, + 364252.96875, + 364777.84375, + 365302.71875, + 365827.5625, + 366352.40625, + 366877.28125, + 367402.15625, + 367927, + 368451.875, + 368976.71875, + 369501.5625, + 370026.4375, + 370551.3125, + 371076.1875, + 371601.03125, + 372125.875, + 372650.75, + 373175.625, + 373700.46875, + 374225.34375, + 374750.1875, + 375275.03125, + 375799.90625, + 376324.78125, + 376849.625, + 377374.5, + 377899.34375, + 378424.1875, + 378949.09375, + 379473.9375, + 379998.78125, + 380523.65625, + 381048.5, + 381573.34375, + 382098.25, + 382623.09375, + 383147.9375, + 383672.8125, + 384197.65625, + 384722.53125, + 385247.40625, + 385772.25, + 386297.125, + 386821.96875, + 387346.8125, + 387871.71875, + 388396.5625, + 388921.40625, + 389446.28125, + 389971.125, + 390496, + 391020.875, + 391545.71875, + 392070.5625, + 392595.4375, + 393120.28125, + 393645.15625, + 394170.03125, + 394694.875, + 395219.71875, + 395744.59375, + 396269.46875, + 396794.3125, + 397319.1875, + 397844.03125, + 398368.875, + 398893.75, + 399418.625, + 399943.46875, + 400468.34375, + 400993.1875, + 401518.0625, + 402042.9375, + 402567.78125, + 403092.65625, + 403617.5, + 404142.34375, + 404667.21875, + 405192.09375, + 405716.9375, + 406241.8125, + 406766.65625, + 407291.5, + 407816.40625, + 408341.25, + 408866.09375, + 409390.96875, + 409915.8125, + 410440.65625, + 410965.5625, + 411490.40625, + 412015.25, + 412540.125, + 413064.96875, + 413589.84375, + 414114.71875, + 414639.5625, + 415164.40625, + 415689.28125, + 416214.125, + 416739, + 417263.875, + 417788.71875, + 418313.59375, + 418838.4375, + 419363.3125, + 419888.1875, + 420413.03125, + 420937.875, + 421462.75, + 421987.59375, + 422512.46875, + 423037.34375, + 423562.1875, + 424087.03125, + 424611.90625, + 425136.78125, + 425661.625, + 426186.5, + 426711.34375, + 427236.1875, + 427761.09375, + 428285.9375, + 428810.78125, + 429335.65625, + 429860.5, + 430385.34375, + 430910.25, + 431435.09375, + 431959.9375, + 432484.8125, + 433009.65625, + 433534.5625, + 434059.40625, + 434584.25, + 435109.125, + 435633.96875, + 436158.8125, + 436683.71875, + 437208.5625, + 437733.40625, + 438258.28125, + 438783.125, + 439307.96875, + 439832.875, + 440357.71875, + 440882.5625, + 441407.4375, + 441932.28125, + 442457.15625, + 442982.03125, + 443506.875, + 444031.71875, + 444556.59375, + 445081.4375, + 445606.3125, + 446131.1875, + 446656.03125, + 447180.875, + 447705.75, + 448230.625, + 448755.5, + 449280.34375, + 449805.1875, + 450330.0625, + 450854.9375, + 451379.78125, + 451904.65625, + 452429.5, + 452954.34375, + 453479.21875, + 454004.09375, + 454528.9375, + 455053.8125, + 455578.65625, + 456103.5, + 456628.40625, + 457153.25, + 457678.09375, + 458202.96875, + 458727.8125, + 459252.65625, + 459777.5625, + 460302.40625, + 460827.25, + 461352.125, + 461876.96875, + 462401.8125, + 462926.71875, + 463451.5625, + 463976.40625, + 464501.28125, + 465026.125, + 465551.03125, + 466075.875, + 466600.71875, + 467125.59375, + 467650.4375, + 468175.3125, + 468700.1875, + 469225.03125, + 469749.875, + 470274.75, + 470799.59375, + 471324.46875, + 471849.34375, + 472374.1875, + 472899.03125, + 473423.90625, + 473948.78125, + 474473.625, + 474998.5, + 475523.34375, + 476048.1875, + 476573.0625, + 477097.9375, + 477622.78125, + 478147.65625, + 478672.5, + 479197.34375, + 479722.25, + 480247.09375, + 480771.96875, + 481296.8125, + 481821.65625, + 482346.53125, + 482871.40625, + 483396.25, + 483921.125, + 484445.96875, + 484970.8125, + 485495.71875, + 486020.5625, + 486545.40625, + 487070.28125, + 487595.125, + 488119.96875, + 488644.875, + 489169.71875, + 489694.5625, + 490219.4375, + 490744.28125, + 491269.15625, + 491794.03125, + 492318.875, + 492843.71875, + 493368.59375, + 493893.4375, + 494418.3125, + 494943.1875, + 495468.03125, + 495992.90625, + 496517.75, + 497042.625, + 497567.5, + 498092.34375, + 498617.1875, + 499142.0625, + 499666.90625, + 500191.78125, + 500716.65625, + 501241.5, + 501766.34375, + 502291.21875, + 502816.09375, + 503340.9375, + 503865.8125, + 504390.65625, + 504915.5, + 505440.375, + 505965.25, + 506490.09375, + 507014.96875, + 507539.8125, + 508064.65625, + 508589.5625, + 509114.40625, + 509639.25, + 510164.125, + 510688.96875, + 511213.8125, + 511738.71875, + 512263.5625, + 512788.4375, + 513313.28125, + 513838.125, + 514363.03125, + 514887.875, + 515412.71875, + 515937.59375, + 516462.4375, + 516987.28125, + 517512.1875, + 518037.03125, + 518561.875, + 519086.75, + 519611.59375, + 520136.46875, + 520661.34375, + 521186.1875, + 521711.03125, + 522235.90625, + 522760.75, + 523285.625, + 523810.5, + 524335.375, + 524860.1875, + 525385.0625, + 525909.9375, + 526434.75, + 526959.625, + 527484.5, + 528009.375, + 528534.25, + 529059.0625, + 529583.9375, + 530108.8125, + 530633.6875, + 531158.5625, + 531683.375, + 532208.25, + 532733.125, + 533258, + 533782.8125, + 534307.6875, + 534832.5625, + 535357.375, + 535882.3125, + 536407.125, + 536932, + 537456.875, + 537981.6875, + 538506.5625, + 539031.4375, + 539556.3125, + 540081.125, + 540606, + 541130.875, + 541655.75, + 542180.625, + 542705.4375, + 543230.3125, + 543755.1875, + 544280, + 544804.9375, + 545329.75, + 545854.625, + 546379.5, + 546904.3125, + 547429.1875, + 547954.0625, + 548478.9375, + 549003.75, + 549528.625, + 550053.4375, + 550578.375, + 551103.25, + 551628.125, + 552152.9375, + 552677.8125, + 553202.6875, + 553727.5, + 554252.375, + 554777.25, + 555302.0625, + 555826.9375, + 556351.875, + 556876.6875, + 557401.5625, + 557926.4375, + 558451.25, + 558976.125, + 559501, + 560025.8125, + 560550.6875, + 561075.5625, + 561600.375, + 562125.3125, + 562650.1875, + 563175, + 563699.875, + 564224.75, + 564749.5625, + 565274.4375, + 565799.3125, + 566324.125, + 566849, + 567373.875, + 567898.75, + 568423.625, + 568948.5, + 569473.3125, + 569998.1875, + 570523.0625, + 571047.875, + 571572.75, + 572097.625, + 572622.4375, + 573147.3125, + 573672.25, + 574197.0625, + 574721.9375, + 575246.8125, + 575771.625, + 576296.5, + 576821.375, + 577346.1875, + 577871.0625, + 578395.9375, + 578920.75, + 579445.6875, + 579970.5625, + 580495.375, + 581020.25, + 581545.125, + 582069.9375, + 582594.8125, + 583119.6875, + 583644.5625, + 584169.375, + 584694.25, + 585219.1875, + 585744, + 586268.875, + 586793.75, + 587318.5625, + 587843.4375, + 588368.3125, + 588893.125, + 589418, + 589942.875, + 590467.6875, + 590992.625, + 591517.5, + 592042.3125, + 592567.1875, + 593092.0625, + 593616.875, + 594141.75, + 594666.625, + 595191.4375, + 595716.3125, + 596241.25, + 596766.0625, + 597290.9375, + 597815.8125, + 598340.625, + 598865.5, + 599390.375, + 599915.1875, + 600440.0625, + 600964.9375, + 601489.75, + 602014.625, + 602539.5625, + 603064.375, + 603589.25, + 604114.125, + 604638.9375, + 605163.8125, + 605688.6875, + 606213.5, + 606738.375, + 607263.25, + 607788.0625, + 608313, + 608837.875, + 609362.6875, + 609887.5625, + 610412.4375, + 610937.25, + 611462.125, + 611987, + 612511.8125, + 613036.6875, + 613561.625, + 614086.5, + 614611.3125, + 615136.1875, + 615661.0625, + 616185.875, + 616710.75, + 617235.625, + 617760.4375, + 618285.3125, + 618810.1875, + 619335, + 619859.9375, + 620384.8125, + 620909.625, + 621434.5, + 621959.375, + 622484.1875, + 623009.0625, + 623533.9375, + 624058.75, + 624583.625, + 625108.5625, + 625633.375, + 626158.25, + 626683.125, + 627207.9375, + 627732.8125, + 628257.6875, + 628782.5, + 629307.375 + ], + "y": [ + 0, + 0, + 0, + 0, + 0, + 18.64515495300293, + 97.37421417236328, + 176.1032257080078, + 254.83229064941406, + 333.561279296875, + 412.2903137207031, + 491.0193176269531, + 569.7484130859375, + 648.4774780273438, + 727.2064819335938, + 805.935546875, + 884.6646118164062, + 963.3934936523438, + 1042.12255859375, + 1120.8515625, + 1199.5806884765625, + 1278.3095703125, + 1357.03857421875, + 1435.767822265625, + 1514.496826171875, + 1593.2259521484375, + 1671.9549560546875, + 1750.6839599609375, + 1829.4129638671875, + 1908.1419677734375, + 1986.87109375, + 2065.60009765625, + 2144.3291015625, + 2223.05810546875, + 2301.787109375, + 2380.51611328125, + 2459.2451171875, + 2537.974365234375, + 2616.703125, + 2695.432373046875, + 2774.161376953125, + 2852.890625, + 2931.619140625, + 3010.348388671875, + 3089.077392578125, + 3167.806396484375, + 3246.53564453125, + 3325.2646484375, + 3403.993896484375, + 3482.72265625, + 3561.451904296875, + 3640.180908203125, + 3718.909912109375, + 3797.638671875, + 3876.367919921875, + 3955.0966796875, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 4000, + 3950, + 3950, + 3900, + 3850, + 3850, + 3800, + 3800, + 3750, + 3750, + 3700, + 3700, + 3650, + 3650, + 3600, + 3600, + 3550, + 3550, + 3500, + 3500, + 3450, + 3450, + 3400, + 3350, + 3350, + 3300, + 3300, + 3250, + 3250, + 3200, + 3200, + 3150, + 3150, + 3100, + 3100, + 3050, + 3050, + 3000, + 3000, + 2950, + 2950, + 2900, + 2900, + 2850, + 2800, + 2800, + 2750, + 2750, + 2700, + 2700, + 2650, + 2650, + 2600, + 2600, + 2550, + 2550, + 2500, + 2500, + 2450, + 2450, + 2400, + 2400, + 2350, + 2300, + 2300, + 2250, + 2250, + 2200, + 2200, + 2150, + 2150, + 2100, + 2100, + 2050, + 2050, + 2000, + 2000, + 1950, + 1950, + 1900, + 1900, + 1850, + 1850, + 1800, + 1750, + 1750, + 1700, + 1700, + 1650, + 1650, + 1600, + 1600, + 1550, + 1550, + 1500, + 1500, + 1450, + 1450, + 1400, + 1400, + 1350, + 1350, + 1300, + 1250, + 1250, + 1200, + 1200, + 1150, + 1150, + 1100, + 1100, + 1050, + 1050, + 1000, + 1000, + 950, + 950, + 900, + 900, + 850, + 850, + 800, + 800, + 750, + 700, + 700, + 650, + 650, + 600, + 600, + 550, + 550, + 500, + 500, + 450, + 450, + 400, + 400, + 350, + 350, + 300, + 300, + 250, + 250, + 200, + 150, + 150, + 100, + 100, + 50, + 50, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0 + ] + } + ], + "layout": { + "font": { + "color": "black", + "family": "Roboto Serif" + }, + "height": 600, + "hovermode": "x unified", + "images": [ + { + "sizex": 0.15, + "sizey": 0.15, + "source": "https://raw.githubusercontent.com/PolicyEngine/policyengine-app/master/src/images/logos/policyengine/blue.png", + "x": 1.1, + "xanchor": "right", + "xref": "paper", + "y": -0.15, + "yanchor": "bottom", + "yref": "paper" + } + ], + "modebar": { + "bgcolor": "rgba(0,0,0,0)", + "color": "rgba(0,0,0,0)" + }, + "template": { + "data": { + "bar": [ + { + "error_x": { + "color": "#2a3f5f" + }, + "error_y": { + "color": "#2a3f5f" + }, + "marker": { + "line": { + "color": "white", + "width": 0.5 + }, + "pattern": { + "fillmode": "overlay", + "size": 10, + "solidity": 0.2 + } + }, + "type": "bar" + } + ], + "barpolar": [ + { + "marker": { + "line": { + "color": "white", + "width": 0.5 + }, + "pattern": { + "fillmode": "overlay", + "size": 10, + "solidity": 0.2 + } + }, + "type": "barpolar" + } + ], + "carpet": [ + { + "aaxis": { + "endlinecolor": "#2a3f5f", + "gridcolor": "#C8D4E3", + "linecolor": "#C8D4E3", + "minorgridcolor": "#C8D4E3", + "startlinecolor": "#2a3f5f" + }, + "baxis": { + "endlinecolor": "#2a3f5f", + "gridcolor": "#C8D4E3", + "linecolor": "#C8D4E3", + "minorgridcolor": "#C8D4E3", + "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": "white", + "showlakes": true, + "showland": true, + "subunitcolor": "#C8D4E3" + }, + "hoverlabel": { + "align": "left" + }, + "hovermode": "closest", + "mapbox": { + "style": "light" + }, + "paper_bgcolor": "white", + "plot_bgcolor": "white", + "polar": { + "angularaxis": { + "gridcolor": "#EBF0F8", + "linecolor": "#EBF0F8", + "ticks": "" + }, + "bgcolor": "white", + "radialaxis": { + "gridcolor": "#EBF0F8", + "linecolor": "#EBF0F8", + "ticks": "" + } + }, + "scene": { + "xaxis": { + "backgroundcolor": "white", + "gridcolor": "#DFE8F3", + "gridwidth": 2, + "linecolor": "#EBF0F8", + "showbackground": true, + "ticks": "", + "zerolinecolor": "#EBF0F8" + }, + "yaxis": { + "backgroundcolor": "white", + "gridcolor": "#DFE8F3", + "gridwidth": 2, + "linecolor": "#EBF0F8", + "showbackground": true, + "ticks": "", + "zerolinecolor": "#EBF0F8" + }, + "zaxis": { + "backgroundcolor": "white", + "gridcolor": "#DFE8F3", + "gridwidth": 2, + "linecolor": "#EBF0F8", + "showbackground": true, + "ticks": "", + "zerolinecolor": "#EBF0F8" + } + }, + "shapedefaults": { + "line": { + "color": "#2a3f5f" + } + }, + "ternary": { + "aaxis": { + "gridcolor": "#DFE8F3", + "linecolor": "#A2B1C6", + "ticks": "" + }, + "baxis": { + "gridcolor": "#DFE8F3", + "linecolor": "#A2B1C6", + "ticks": "" + }, + "bgcolor": "white", + "caxis": { + "gridcolor": "#DFE8F3", + "linecolor": "#A2B1C6", + "ticks": "" + } + }, + "title": { + "x": 0.05 + }, + "xaxis": { + "automargin": true, + "gridcolor": "#EBF0F8", + "linecolor": "#EBF0F8", + "ticks": "", + "title": { + "standoff": 15 + }, + "zerolinecolor": "#EBF0F8", + "zerolinewidth": 2 + }, + "yaxis": { + "automargin": true, + "gridcolor": "#EBF0F8", + "linecolor": "#EBF0F8", + "ticks": "", + "title": { + "standoff": 15 + }, + "zerolinecolor": "#EBF0F8", + "zerolinewidth": 2 + } + } + }, + "title": { + "text": "Child Tax Credit by Employment Income
Family of 4 in California" + }, + "width": 800, + "xaxis": { + "tickformat": "$,.0f", + "title": { + "text": "Employment Income" + } + }, + "yaxis": { + "tickformat": "$,.0f", + "title": { + "text": "Child Tax Credit" + } + } + } + } + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "# Create a Plotly plot of CTC vs Employment Income\n", + "fig = go.Figure()\n", + "\n", + "fig.add_trace(go.Scatter(\n", + " x=income_range,\n", + " y=ctc_range,\n", + " mode='lines',\n", + " name='Child Tax Credit',\n", + " line=dict(color='#2C6496', width=3),\n", + " hovertemplate='Employment Income: $%{x:,.0f}
CTC: $%{y:,.0f}'\n", + "))\n", + "\n", + "fig.update_layout(\n", + " title='Child Tax Credit by Employment Income
Family of 4 in California',\n", + " xaxis_title='Employment Income',\n", + " yaxis_title='Child Tax Credit',\n", + " xaxis=dict(tickformat='$,.0f'),\n", + " yaxis=dict(tickformat='$,.0f'),\n", + " hovermode='x unified',\n", + " template='plotly_white'\n", + ")\n", + "\n", + "# Apply PolicyEngine formatting\n", + "fig = format_fig(fig)\n", + "fig.show()" + ] + }, + { + "cell_type": "markdown", + "id": "d56e7d1a", + "metadata": {}, + "source": [ + "## Summary\n", + "\n", + "This notebook demonstrated:\n", + "1. **Creating household situations** with proper entity relationships\n", + "2. **Running simulations** and calculating variables\n", + "3. **Understanding variable dependencies** through the calculation tree\n", + "4. **Creating and applying reforms** (both simple and complex)\n", + "5. **Comparing baseline and reform** scenarios\n", + "6. **Using calculate_dataframe** for efficient multi-variable analysis\n", + "7. **Calculating marginal tax rates** through incremental changes\n", + "8. **Using axes** to vary parameters and analyze responses\n", + "\n", + "Key takeaways:\n", + "- PolicyEngine uses a hierarchical entity structure (person → tax unit → household)\n", + "- Variables can depend on other variables, creating a calculation tree\n", + "- Reforms can modify parameters or add new calculation logic\n", + "- The Simulation class provides flexible methods for analysis\n", + "- DataFrames integration makes it easy to analyze results with pandas" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "pe", + "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.10.14" + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} From b92468357f79224ac0ef0b6402221fa87cf28c81 Mon Sep 17 00:00:00 2001 From: PavelMakarchuk Date: Thu, 10 Jul 2025 16:49:58 +0200 Subject: [PATCH 2/6] minor --- educational/household_simulation.ipynb | 4332 +----------------------- 1 file changed, 37 insertions(+), 4295 deletions(-) diff --git a/educational/household_simulation.ipynb b/educational/household_simulation.ipynb index d7895f1..c4fa920 100644 --- a/educational/household_simulation.ipynb +++ b/educational/household_simulation.ipynb @@ -362,6 +362,16 @@ "print(f\" household_net_income.sum() = ${household_net_income.sum():,.0f} (just the 1 household value)\")" ] }, + { + "cell_type": "code", + "execution_count": 88, + "id": "a410a154", + "metadata": {}, + "outputs": [], + "source": [ + "x = baseline_sim.tax_benefit_system" + ] + }, { "cell_type": "markdown", "id": "b40fe4d8", @@ -503,7 +513,7 @@ }, { "cell_type": "code", - "execution_count": 15, + "execution_count": 93, "id": "5393ac49", "metadata": {}, "outputs": [ @@ -1214,39 +1224,31 @@ }, { "cell_type": "code", - "execution_count": 37, + "execution_count": null, "id": "6b4b2bb2", "metadata": {}, "outputs": [], "source": [ "# Example of a structural reform that modifies the SALT cap calculation to add phase-out to the cap\n", - "class salt_cap(Variable):\n", + "class ctc_value(Variable):\n", " value_type = float\n", " entity = TaxUnit\n", - " label = \"SALT cap\"\n", + " label = \"CTC value\"\n", " unit = USD\n", - " definition_PERIOD = YEAR\n", - " reference = \"https://www.law.cornell.edu/uscode/text/26/164\"\n", + " documentation = \"Actual value of the Child Tax Credit\"\n", + " definition_period = YEAR\n", "\n", - " def formula(tax_unit, PERIOD, parameters):\n", - " filing_status = tax_unit(\"filing_status\", PERIOD)\n", - " p = parameters(\n", - " PERIOD\n", - " ).gov.irs.deductions.itemized.salt_and_real_estate\n", - " max_cap = p.cap[filing_status]\n", - " p_ref = parameters(PERIOD).gov.contrib.salt_phase_out\n", - " agi = tax_unit(\"adjusted_gross_income\", PERIOD)\n", - " agi_excess = max_(0, agi - p_ref.threshold[filing_status])\n", - " phase_out = p_ref.rate * agi_excess\n", - " phased_out_cap = max_(0, max_cap - phase_out)\n", - " if p_ref.floor.applies:\n", - " floor = p_ref.floor.amount[filing_status]\n", - " return max_(phased_out_cap, floor)\n", - " return phased_out_cap\n", + " def formula(tax_unit, period, parameters):\n", + " ctc = tax_unit(\"ctc\", period)\n", + " p = parameters(period).gov.irs.credits.ctc.refundable\n", + " if not p.fully_refundable:\n", + " phase_in = tax_unit(\"ctc_phase_in\", period)\n", + " return min_(ctc, phase_in) + 1_000\n", + " return ctc\n", "\n", "class reform(Reform):\n", " def apply(self):\n", - " self.update_variable(salt_cap)\n", + " self.update_variable(ctc_value)\n", "\n", "# This would be applied as: Simulation(situation=situation, reform=reform())" ] @@ -1365,7 +1367,7 @@ }, { "cell_type": "code", - "execution_count": 41, + "execution_count": 94, "id": "cccdc298", "metadata": {}, "outputs": [], @@ -1381,13 +1383,14 @@ " \"ctc_value\",\n", " \"eitc\",\n", " \"snap\",\n", + " \"state_code\",\n", " \"household_net_income\"\n", "]" ] }, { "cell_type": "code", - "execution_count": 42, + "execution_count": 95, "id": "a611cc57", "metadata": {}, "outputs": [], @@ -1407,7 +1410,7 @@ }, { "cell_type": "code", - "execution_count": 43, + "execution_count": 96, "id": "5400bf99", "metadata": {}, "outputs": [], @@ -1427,7 +1430,7 @@ }, { "cell_type": "code", - "execution_count": 44, + "execution_count": 97, "id": "fb18dbe3", "metadata": {}, "outputs": [ @@ -1443,13 +1446,13 @@ "2 0.0 120000.0 90000.0 6323.0 \n", "3 0.0 120000.0 90000.0 6323.0 \n", "\n", - " ctc_value eitc snap household_net_income \n", - "0 4000.0 0.0 0.0 102116.851562 \n", - "1 4000.0 0.0 0.0 102116.851562 \n", - "2 4000.0 0.0 0.0 102116.851562 \n", - "3 4000.0 0.0 0.0 102116.851562 \n", + " ctc_value eitc snap state_code household_net_income \n", + "0 4000.0 0.0 0.0 4 102116.851562 \n", + "1 4000.0 0.0 0.0 4 102116.851562 \n", + "2 4000.0 0.0 0.0 4 102116.851562 \n", + "3 4000.0 0.0 0.0 4 102116.851562 \n", "\n", - "Shape: (4, 8)\n", + "Shape: (4, 9)\n", "Note: Since employment_income is person-level, all other variables are disaggregated to person-level\n" ] } @@ -1821,7 +1824,7 @@ }, { "cell_type": "code", - "execution_count": 53, + "execution_count": 100, "id": "5ff9f744", "metadata": {}, "outputs": [], @@ -1836,7 +1839,7 @@ }, { "cell_type": "code", - "execution_count": 54, + "execution_count": 101, "id": "2f8c5308", "metadata": {}, "outputs": [], @@ -5276,4267 +5279,6 @@ "- The Simulation class provides flexible methods for analysis\n", "- DataFrames integration makes it easy to analyze results with pandas" ] - }, - { - "cell_type": "markdown", - "id": "6a87d52a", - "metadata": {}, - "source": [ - "### Understanding the Trace Output\n", - "The trace shows:\n", - "- **Indentation** = dependency depth (more indented = deeper in calculation tree)\n", - "- **Variable names** with PERIOD and year (e.g., `ctc_value<2025, (default)>`)\n", - "- **Calculated values** as arrays - the array length indicates the entity level:\n", - " - Arrays with 4 values `[x, x, x, x]` = person-level (4 people in household)\n", - " - Arrays with 1 value `[x]` = tax unit or household level\n", - "- **Time periods** when the calculation applies\n", - "- **With max_depth=None**, the most deeply indented variables are inputs (from user, defaults, or data) rather than calculations\n" - ] - }, - { - "cell_type": "code", - "execution_count": 59, - "id": "fd3b7035", - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "\n", - "CTC Maximum per child: $[ 0. 0. 2000. 2000.]\n", - "CTC Phase-out amount: $[0.]\n" - ] - } - ], - "source": [ - "# Now let's look at individual variables outside of the computation tree\n", - "ctc_child_individual_maximum = baseline_sim.calculate(\"ctc_child_individual_maximum\", PERIOD)\n", - "ctc_phase_out = baseline_sim.calculate(\"ctc_phase_out\", PERIOD)\n", - "\n", - "print(f\"\\nCTC Maximum per child: ${ctc_child_individual_maximum}\")\n", - "print(f\"CTC Phase-out amount: ${ctc_phase_out}\")" - ] - }, - { - "cell_type": "markdown", - "id": "1b8f4175", - "metadata": {}, - "source": [ - "## Part 4: Creating and Running Reforms\n", - "\n", - "### Parametric Reforms\n", - "The most common type of reform modifies existing parameters (tax rates, benefit amounts, thresholds, etc.):" - ] - }, - { - "cell_type": "markdown", - "id": "f73a6858", - "metadata": {}, - "source": [ - "### Understanding Parameter Types\n", - "\n", - "PolicyEngine uses different parameter types for various policy features. Here's how to work with each type:\n", - "\n", - "| Parameter Type | Example | Notes |\n", - "|---------------|---------|-------|\n", - "| **Single value** | `\"gov.irs.credits.ctc.amount.base[0].amount\": 3000`
`\"gov.irs.credits.ctc.phase_out.rate\": 0.05`
`\"gov.irs.credits.ctc.child.max_age\": 17` | Can be monetary amounts (USD), rates/percentages (as decimals: 5% = 0.05), ages (years), or other single values |\n", - "| **List parameter** | `\"gov.irs.credits.refundable\": [\"eitc\", \"refundable_ctc\"]` | Lists of values, often containing variable class names |\n", - "| **Scale parameter** | `\"gov.irs.credits.ctc.amount.base[0].amount\": 3000` | Graduated scales with thresholds and rates |\n", - "| **Breakdown parameters** | `\"gov.irs.credits.ctc.phase_out.threshold.JOINT\": 500000`| Parameters broken down by enums (e.g., filing status) or numeric ranges |" - ] - }, - { - "cell_type": "markdown", - "id": "a2bf5e5f", - "metadata": {}, - "source": [ - "### Accessing Scale Components\n", - "- `scale.thresholds`: list[float] - income thresholds\n", - "- `scale.rates`: list[float] - tax rates for each bracket\n", - "- `scale.amounts`: list[float] - fixed amounts (if applicable)\n", - "\n", - "Examples of accessing specific elements:\n", - "- `scale.thresholds[0]` - first threshold (usually 0)\n", - "- `scale.thresholds[-1]` - last threshold\n", - "- `scale.rates[2]` - rate for third bracket\n" - ] - }, - { - "cell_type": "code", - "execution_count": 60, - "id": "c531aaf1", - "metadata": {}, - "outputs": [], - "source": [ - "# Create a parametric reform that increases the CTC amount\n", - "# This is a simple parameter change - increasing CTC from $2,000 to $3,000 from 2025 forward\n", - "\n", - "ctc_reform = Reform.from_dict({\n", - " \"gov.irs.credits.ctc.amount.base[0].amount\": {\n", - " \"2025-01-01.2100-12-31\": 3000\n", - " }\n", - "}, country_id=\"us\")\n", - "\n", - "# You can also create more complex reforms with multiple parameters\n", - "comprehensive_reform = Reform.from_dict({\n", - " # Increase CTC amount\n", - " \"gov.irs.credits.ctc.amount.base[0].amount\": {\n", - " \"2025-01-01.2100-12-31\": 3000\n", - " },\n", - " # Make CTC fully refundable\n", - " \"gov.irs.credits.ctc.refundable.fully_refundable\": {\n", - " \"2025-01-01.2100-12-31\": True\n", - " },\n", - " # Increase phase-out threshold for JOINT filing statuses\n", - " \"gov.irs.credits.ctc.phase_out.threshold.JOINT\": {\n", - " \"2025-01-01.2100-12-31\": 500000\n", - " }\n", - "}, country_id=\"us\")" - ] - }, - { - "cell_type": "markdown", - "id": "92381be3", - "metadata": {}, - "source": [ - "### Running the Reformed Simulation\n" - ] - }, - { - "cell_type": "code", - "execution_count": 61, - "id": "9de46e04", - "metadata": {}, - "outputs": [], - "source": [ - "# Create a new simulation with the comprehensive reform\n", - "reformed_sim = Simulation(\n", - " situation=situation,\n", - " reform=comprehensive_reform\n", - ")" - ] - }, - { - "cell_type": "code", - "execution_count": 62, - "id": "1b6bd06f", - "metadata": {}, - "outputs": [], - "source": [ - "# Calculate the same variables under the reform\n", - "reformed_income_tax = reformed_sim.calculate(\"income_tax\", PERIOD)\n", - "reformed_ctc = reformed_sim.calculate(\"ctc_value\", PERIOD)\n", - "reformed_net_income = reformed_sim.calculate(\"household_net_income\", PERIOD)" - ] - }, - { - "cell_type": "code", - "execution_count": 63, - "id": "4febbe91", - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "=== REFORM RESULTS ===\n", - "Federal Income Tax: $4,323\n", - "Child Tax Credit: $6,000\n", - "Household Net Income: $104,117\n" - ] - } - ], - "source": [ - "print(\"=== REFORM RESULTS ===\")\n", - "print(f\"Federal Income Tax: ${reformed_income_tax.sum():,.0f}\")\n", - "print(f\"Child Tax Credit: ${reformed_ctc.sum():,.0f}\")\n", - "print(f\"Household Net Income: ${reformed_net_income.sum():,.0f}\")" - ] - }, - { - "cell_type": "markdown", - "id": "77394ef5", - "metadata": {}, - "source": [ - "### Structural Reforms (Advanced)\n", - "PolicyEngine also supports structural reforms that change how variables are calculated, not just parameter values. Here's an example that adds a phase-out to the SALT deduction cap:" - ] - }, - { - "cell_type": "code", - "execution_count": 64, - "id": "e8ba1547", - "metadata": {}, - "outputs": [], - "source": [ - "from policyengine_us.model_api import *" - ] - }, - { - "cell_type": "code", - "execution_count": 65, - "id": "3bd5a755", - "metadata": {}, - "outputs": [], - "source": [ - "# Example of a structural reform that modifies the SALT cap calculation to add phase-out to the cap\n", - "class salt_cap(Variable):\n", - " value_type = float\n", - " entity = TaxUnit\n", - " label = \"SALT cap\"\n", - " unit = USD\n", - " definition_PERIOD = YEAR\n", - " reference = \"https://www.law.cornell.edu/uscode/text/26/164\"\n", - "\n", - " def formula(tax_unit, PERIOD, parameters):\n", - " filing_status = tax_unit(\"filing_status\", PERIOD)\n", - " p = parameters(\n", - " PERIOD\n", - " ).gov.irs.deductions.itemized.salt_and_real_estate\n", - " max_cap = p.cap[filing_status]\n", - " p_ref = parameters(PERIOD).gov.contrib.salt_phase_out\n", - " agi = tax_unit(\"adjusted_gross_income\", PERIOD)\n", - " agi_excess = max_(0, agi - p_ref.threshold[filing_status])\n", - " phase_out = p_ref.rate * agi_excess\n", - " phased_out_cap = max_(0, max_cap - phase_out)\n", - " if p_ref.floor.applies:\n", - " floor = p_ref.floor.amount[filing_status]\n", - " return max_(phased_out_cap, floor)\n", - " return phased_out_cap\n", - "\n", - "class reform(Reform):\n", - " def apply(self):\n", - " self.update_variable(salt_cap)\n", - "\n", - "# This would be applied as: Simulation(situation=situation, reform=reform())" - ] - }, - { - "cell_type": "markdown", - "id": "372a5d4e", - "metadata": {}, - "source": [ - "**Best Practice**: While structural reforms are powerful, it's often better to:\n", - "1. File an issue in the [policyengine-us repository](https://github.com/PolicyEngine/policyengine-us/issues)\n", - "2. Discuss with PolicyEngine developers\n", - "3. Submit a pull request to add new parameters\n", - "4. Convert your structural reform into a parametric one\n", - "\n", - "This approach ensures:\n", - "- Your reform integrates well with the existing codebase\n", - "- Other users can benefit from the new functionality\n", - "- The reform is properly tested and documented\n", - "- Future updates won't break your analysis" - ] - }, - { - "cell_type": "markdown", - "id": "999dc1eb", - "metadata": {}, - "source": [ - "## Part 5: Comparing Baseline and Reform\n" - ] - }, - { - "cell_type": "code", - "execution_count": 66, - "id": "5fc447b1", - "metadata": {}, - "outputs": [], - "source": [ - "# Create a comparison dataframe\n", - "comparison_data = {\n", - " \"Metric\": [\n", - " \"Federal Income Tax\",\n", - " \"Child Tax Credit\", \n", - " \"Household Net Income\",\n", - " ],\n", - " \"Baseline\": [\n", - " income_tax.sum(),\n", - " ctc.sum(),\n", - " household_net_income.sum(),\n", - " ],\n", - " \"Reform\": [\n", - " reformed_income_tax.sum(),\n", - " reformed_ctc.sum(),\n", - " reformed_net_income.sum(),\n", - " ]\n", - "}" - ] - }, - { - "cell_type": "code", - "execution_count": 67, - "id": "747cd298", - "metadata": {}, - "outputs": [], - "source": [ - "comparison_df = pd.DataFrame(comparison_data)\n", - "comparison_df[\"Change\"] = comparison_df[\"Reform\"] - comparison_df[\"Baseline\"]\n", - "comparison_df[\"% Change\"] = (comparison_df[\"Change\"] / comparison_df[\"Baseline\"].abs() * 100).round(2)" - ] - }, - { - "cell_type": "code", - "execution_count": 68, - "id": "6ef41abc", - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "\n", - "=== COMPARISON ===\n", - " Metric Baseline Reform Change % Change\n", - " Federal Income Tax 6323.000000 4323.000000 -2000.0 -31.629999\n", - " Child Tax Credit 4000.000000 6000.000000 2000.0 50.000000\n", - "Household Net Income 102116.851562 104116.851562 2000.0 1.960000\n" - ] - } - ], - "source": [ - "print(\"\\n=== COMPARISON ===\")\n", - "print(comparison_df.to_string(index=False))" - ] - }, - { - "cell_type": "markdown", - "id": "f62b8c0a", - "metadata": {}, - "source": [ - "## Part 6: Using the calculate_dataframe Method\n", - "\n", - "The `calculate_dataframe` method provides a convenient way to calculate multiple variables at once and return them in a pandas DataFrame format. This is particularly useful when you need to analyze several variables together or export results for further analysis.\n", - "\n", - "### Understanding Entity Levels and Data Structure\n", - "\n", - "PolicyEngine organizes data hierarchically across different entity levels:\n", - "\n", - "| Entity Level | Description | Example Variables |\n", - "|-------------|-------------|-------------------|\n", - "| **Person** | Individual-level data | employment_income, age, is_disabled |\n", - "| **Tax Unit** | Tax filing unit data | income_tax, ctc_value, eitc |\n", - "| **SPM Unit** | Supplemental Poverty Measure unit | snap, housing_assistance |\n", - "| **Household** | Household-level data | household_net_income, rent |\n", - "\n", - "When using `calculate_dataframe`, it's crucial to understand how these entity levels interact:" - ] - }, - { - "cell_type": "code", - "execution_count": 69, - "id": "34ec5e06", - "metadata": {}, - "outputs": [], - "source": [ - "# For more complex analysis, we can use calculate_dataframe\n", - "# This returns a pandas DataFrame with multiple variables\n", - "\n", - "VARIABLES_TO_CALCULATE = [\n", - " \"employment_income\",\n", - " \"adjusted_gross_income\",\n", - " \"taxable_income\",\n", - " \"income_tax\",\n", - " \"ctc_value\",\n", - " \"eitc\",\n", - " \"snap\",\n", - " \"household_net_income\"\n", - "]" - ] - }, - { - "cell_type": "code", - "execution_count": 70, - "id": "557316f9", - "metadata": {}, - "outputs": [], - "source": [ - "PERIOD = 2025" - ] - }, - { - "cell_type": "markdown", - "id": "80daebf6", - "metadata": {}, - "source": [ - "### Default Behavior: Automatic Entity Resolution\n", - "\n", - "When you don't specify a `map_to` parameter, `calculate_dataframe` automatically determines the appropriate entity level based on the \"longest\" entity needed to represent all variables:" - ] - }, - { - "cell_type": "code", - "execution_count": 71, - "id": "e6b6d7d6", - "metadata": {}, - "outputs": [], - "source": [ - "# Calculate for baseline\n", - "baseline_df = baseline_sim.calculate_dataframe(\n", - " VARIABLES_TO_CALCULATE,\n", - " period=PERIOD\n", - ")\n", - "\n", - "# Calculate for reform\n", - "reform_df = reformed_sim.calculate_dataframe(\n", - " VARIABLES_TO_CALCULATE,\n", - " period=PERIOD\n", - ")" - ] - }, - { - "cell_type": "code", - "execution_count": 72, - "id": "52f41be8", - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "\n", - "=== BASELINE DATAFRAME (No map_to specified) ===\n", - " employment_income adjusted_gross_income taxable_income income_tax \\\n", - "0 75000.0 120000.0 90000.0 6323.0 \n", - "1 45000.0 120000.0 90000.0 6323.0 \n", - "2 0.0 120000.0 90000.0 6323.0 \n", - "3 0.0 120000.0 90000.0 6323.0 \n", - "\n", - " ctc_value eitc snap household_net_income \n", - "0 4000.0 0.0 0.0 102116.851562 \n", - "1 4000.0 0.0 0.0 102116.851562 \n", - "2 4000.0 0.0 0.0 102116.851562 \n", - "3 4000.0 0.0 0.0 102116.851562 \n", - "\n", - "Shape: (4, 8)\n", - "Note: Since employment_income is person-level, all other variables are disaggregated to person-level\n" - ] - } - ], - "source": [ - "print(\"\\n=== BASELINE DATAFRAME (No map_to specified) ===\")\n", - "print(baseline_df)\n", - "print(f\"\\nShape: {baseline_df.shape}\")\n", - "print(\"Note: Since employment_income is person-level, all other variables are disaggregated to person-level\")" - ] - }, - { - "cell_type": "markdown", - "id": "3ec115b2", - "metadata": {}, - "source": [ - "In this case, because `employment_income` is a person-level variable and we have 4 people in our household, the DataFrame will have 4 rows. All higher-level variables (tax unit, household) will be repeated for each person they apply to." - ] - }, - { - "cell_type": "code", - "execution_count": 73, - "id": "4b966e17", - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "\n", - "=== REFORM DATAFRAME ===\n", - " employment_income adjusted_gross_income taxable_income income_tax \\\n", - "0 75000.0 120000.0 90000.0 4323.0 \n", - "1 45000.0 120000.0 90000.0 4323.0 \n", - "2 0.0 120000.0 90000.0 4323.0 \n", - "3 0.0 120000.0 90000.0 4323.0 \n", - "\n", - " ctc_value eitc snap household_net_income \n", - "0 6000.0 0.0 0.0 104116.851562 \n", - "1 6000.0 0.0 0.0 104116.851562 \n", - "2 6000.0 0.0 0.0 104116.851562 \n", - "3 6000.0 0.0 0.0 104116.851562 \n" - ] - } - ], - "source": [ - "print(\"\\n=== REFORM DATAFRAME ===\")\n", - "print(reform_df)" - ] - }, - { - "cell_type": "markdown", - "id": "4c9d1152", - "metadata": {}, - "source": [ - "### Using map_to Parameter for Aggregation\n", - "\n", - "The `map_to` parameter allows you to aggregate all variables to a specific entity level. This is extremely useful when you want to analyze data at the household or tax unit level:" - ] - }, - { - "cell_type": "code", - "execution_count": 74, - "id": "0781b09c", - "metadata": {}, - "outputs": [], - "source": [ - "# Calculate for baseline\n", - "baseline_person_df = baseline_sim.calculate_dataframe(\n", - " VARIABLES_TO_CALCULATE,\n", - " map_to=\"person\",\n", - " period=PERIOD\n", - ")" - ] - }, - { - "cell_type": "code", - "execution_count": 75, - "id": "31197f1a", - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "\n", - "=== BASELINE DATAFRAME (Mapped to person) ===\n", - " employment_income adjusted_gross_income taxable_income income_tax \\\n", - "0 75000.0 120000.0 90000.0 6323.0 \n", - "1 45000.0 120000.0 90000.0 6323.0 \n", - "2 0.0 120000.0 90000.0 6323.0 \n", - "3 0.0 120000.0 90000.0 6323.0 \n", - "\n", - " ctc_value eitc snap household_net_income \n", - "0 4000.0 0.0 0.0 102116.851562 \n", - "1 4000.0 0.0 0.0 102116.851562 \n", - "2 4000.0 0.0 0.0 102116.851562 \n", - "3 4000.0 0.0 0.0 102116.851562 \n", - "\n", - "Shape: (4, 8)\n", - "Note: All variables are now aggregated to person level (4 rows)\n" - ] - } - ], - "source": [ - "print(\"\\n=== BASELINE DATAFRAME (Mapped to person) ===\")\n", - "print(baseline_person_df)\n", - "print(f\"\\nShape: {baseline_person_df.shape}\")\n", - "print(\"Note: All variables are now aggregated to person level (4 rows)\")" - ] - }, - { - "cell_type": "code", - "execution_count": 76, - "id": "85346780", - "metadata": {}, - "outputs": [], - "source": [ - "# Calculate for baseline\n", - "baseline_household_df = baseline_sim.calculate_dataframe(\n", - " VARIABLES_TO_CALCULATE,\n", - " map_to=\"household\",\n", - " period=PERIOD\n", - ")" - ] - }, - { - "cell_type": "code", - "execution_count": 77, - "id": "52753049", - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "\n", - "=== BASELINE DATAFRAME (Mapped to household) ===\n", - " employment_income adjusted_gross_income taxable_income income_tax \\\n", - "0 120000.0 120000.0 90000.0 6323.0 \n", - "\n", - " ctc_value eitc snap household_net_income \n", - "0 4000.0 0.0 0.0 102116.851562 \n", - "\n", - "Shape: (1, 8)\n", - "Note: All variables are now aggregated to household level (1 row)\n" - ] - } - ], - "source": [ - "print(\"\\n=== BASELINE DATAFRAME (Mapped to household) ===\")\n", - "print(baseline_household_df)\n", - "print(f\"\\nShape: {baseline_household_df.shape}\")\n", - "print(\"Note: All variables are now aggregated to household level (1 row)\")" - ] - }, - { - "cell_type": "markdown", - "id": "4a4a7d25", - "metadata": {}, - "source": [ - "### How map_to Aggregation Works\n", - "\n", - "When you use `map_to`, PolicyEngine performs the following operations:\n", - "\n", - "1. **Person-level variables** → Summed within the target entity\n", - " - Example: `employment_income` for all 4 people is summed to get total household employment income\n", - "\n", - "2. **Same-level variables** → Kept as-is\n", - " - Example: `household_net_income` is already at household level, so no change\n", - "\n", - "3. **Intermediate-level variables** → Summed to the target level\n", - " - Example: If there were multiple tax units, their `income_tax` would be summed" - ] - }, - { - "cell_type": "code", - "execution_count": 78, - "id": "66524251", - "metadata": {}, - "outputs": [], - "source": [ - "# Example: Export-ready DataFrame\n", - "export_df = baseline_sim.calculate_dataframe(\n", - " VARIABLES_TO_CALCULATE,\n", - " map_to=\"household\",\n", - " period=PERIOD\n", - ")\n", - "\n", - "# Add identifiers and save\n", - "export_df[\"household_id\"] = \"household\"\n", - "export_df[\"scenario\"] = \"baseline\"\n", - "\n", - "# You could save this to CSV:\n", - "export_df.to_csv(\"household_analysis.csv\", index=False)" - ] - }, - { - "cell_type": "markdown", - "id": "27200583", - "metadata": {}, - "source": [ - "## Part 7: Creating Earnings Variation Charts\n", - "\n", - "### Note: Using PolicyEngine's Web Interface for Earnings Variation\n", - "You can also generate earnings variation code directly from PolicyEngine's web interface:\n", - "1. Create your household at https://policyengine.org/us/household\n", - "2. Toggle \"Include earnings variation\" in the \"Reproduce in Python\" section\n", - "3. Copy the generated code with axes already configured\n", - "\n", - "Here's an example of what the generated code looks like:" - ] - }, - { - "cell_type": "code", - "execution_count": 79, - "id": "904110de", - "metadata": {}, - "outputs": [], - "source": [ - "# Example from PolicyEngine's \"Reproduce in Python\" with earnings variation\n", - "situation_with_axes = {\n", - " \"people\": {\n", - " \"you\": {\n", - " \"age\": {\n", - " \"2025\": 35\n", - " }\n", - " },\n", - " \"your partner\": {\n", - " \"age\": {\n", - " \"2025\": 33\n", - " }\n", - " },\n", - " \"your first dependent\": {\n", - " \"age\": {\n", - " \"2025\": 10\n", - " }\n", - " },\n", - " \"your second dependent\": {\n", - " \"age\": {\n", - " \"2025\": 7\n", - " }\n", - " }\n", - " },\n", - " \"families\": {\n", - " \"your family\": {\n", - " \"members\": [\n", - " \"you\",\n", - " \"your partner\",\n", - " \"your first dependent\",\n", - " \"your second dependent\"\n", - " ]\n", - " }\n", - " },\n", - " \"marital_units\": {\n", - " \"your marital unit\": {\n", - " \"members\": [\n", - " \"you\",\n", - " \"your partner\"\n", - " ]\n", - " },\n", - " \"your first dependent's marital unit\": {\n", - " \"members\": [\n", - " \"your first dependent\"\n", - " ],\n", - " \"marital_unit_id\": {\n", - " \"2025\": 1\n", - " }\n", - " },\n", - " \"your second dependent's marital unit\": {\n", - " \"members\": [\n", - " \"your second dependent\"\n", - " ],\n", - " \"marital_unit_id\": {\n", - " \"2025\": 2\n", - " }\n", - " }\n", - " },\n", - " \"tax_units\": {\n", - " \"your tax unit\": {\n", - " \"members\": [\n", - " \"you\",\n", - " \"your partner\",\n", - " \"your first dependent\",\n", - " \"your second dependent\"\n", - " ]\n", - " }\n", - " },\n", - " \"spm_units\": {\n", - " \"your household\": {\n", - " \"members\": [\n", - " \"you\",\n", - " \"your partner\",\n", - " \"your first dependent\",\n", - " \"your second dependent\"\n", - " ]\n", - " }\n", - " },\n", - " \"households\": {\n", - " \"your household\": {\n", - " \"members\": [\n", - " \"you\",\n", - " \"your partner\",\n", - " \"your first dependent\",\n", - " \"your second dependent\"\n", - " ],\n", - " \"state_name\": {\n", - " \"2025\": \"CA\"\n", - " }\n", - " }\n", - " },\n", - " \"axes\": [\n", - " [\n", - " {\n", - " \"name\": \"employment_income\",\n", - " \"count\": 1200,\n", - " \"min\": 0,\n", - " \"max\": 600000\n", - " }\n", - " ]\n", - " ]\n", - "}" - ] - }, - { - "cell_type": "markdown", - "id": "a0451dea", - "metadata": {}, - "source": [ - "### Varying inputs with **axes**\n", - "\n", - "PolicyEngine inherits **axes** from OpenFisca, letting us replicate the household situation over a grid of values.\n", - "\n", - "| Key | Meaning |\n", - "|-----|---------|\n", - "| `name` | Variable to iterate over |\n", - "| `count` | Number of equally spaced points |\n", - "| `min` | Interval start |\n", - "| `max` | Interval end |\n", - "\n", - "See the OpenFisca documentation for details: " - ] - }, - { - "cell_type": "markdown", - "id": "e98232cf", - "metadata": {}, - "source": [ - "### Note: Flexibility of Axes Variables\n", - "You can vary **ANY** variable in axes, not just employment_income! Examples include:\n", - "- `rent`\n", - "- `mortgage_interest` \n", - "- `medical_expenses`\n", - "- `childcare_expenses`\n", - "\n", - "Just change the \"name\" parameter in the axes configuration." - ] - }, - { - "cell_type": "code", - "execution_count": 80, - "id": "6476cefa", - "metadata": {}, - "outputs": [], - "source": [ - "# Create simulation with axes\n", - "axes_sim = Simulation(situation=situation_with_axes)" - ] - }, - { - "cell_type": "code", - "execution_count": 81, - "id": "7d3c93f7", - "metadata": {}, - "outputs": [], - "source": [ - "# Calculate variables across the income range\n", - "income_range = axes_sim.calculate(\"employment_income\", map_to=\"household\", period=PERIOD)\n", - "net_income_range = axes_sim.calculate(\"household_net_income\", map_to=\"household\", period=PERIOD)\n", - "ctc_range = axes_sim.calculate(\"ctc_value\", map_to=\"household\", period=PERIOD)\n", - "eitc_range = axes_sim.calculate(\"eitc\", map_to=\"household\", period=PERIOD)\n", - "snap_range = axes_sim.calculate(\"snap\", map_to=\"household\", period=PERIOD)" - ] - }, - { - "cell_type": "code", - "execution_count": 82, - "id": "b1be657d", - "metadata": {}, - "outputs": [], - "source": [ - "# Create a dataframe for analysis\n", - "results_df = pd.DataFrame({\n", - " \"Employment Income\": income_range,\n", - " \"Net Income\": net_income_range,\n", - " \"CTC\": ctc_range,\n", - " \"EITC\": eitc_range,\n", - " \"SNAP\": snap_range\n", - "})" - ] - }, - { - "cell_type": "code", - "execution_count": 83, - "id": "f5e1a1d0", - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "\n", - "=== INCOME VARIATION RESULTS (Sample) ===\n", - " Employment Income Net Income CTC EITC SNAP\n", - "0 0.000000 26000.064453 0.0 0.000000 7710.931641\n", - "1 524.860229 26749.869141 0.0 209.944092 7587.632324\n", - "2 1049.720459 27496.976562 0.0 419.888184 7461.632324\n", - "3 1574.580688 28244.080078 0.0 629.832275 7335.632324\n", - "4 2099.440918 28991.185547 0.0 839.776367 7209.632324\n" - ] - } - ], - "source": [ - "# Show sample of results\n", - "print(\"\\n=== INCOME VARIATION RESULTS (Sample) ===\")\n", - "print(results_df.head()) # Show the first 5 rows" - ] - }, - { - "cell_type": "markdown", - "id": "bfb80419", - "metadata": {}, - "source": [ - "### Parallel and perpendicular axes\n", - "\n", - "* **Parallel axes** – objects inside the **same** inner list. Variables change **together** (lock‑step). \n", - "* **Perpendicular axes** – objects in **separate** inner lists. Variables change **independently** creating a Cartesian product." - ] - }, - { - "cell_type": "code", - "execution_count": 84, - "id": "d4540c7e", - "metadata": {}, - "outputs": [], - "source": [ - "# One parallel axis (employment_income)\n", - "example_axes = [[\n", - " {\"name\": \"employment_income\", \"count\": 1200, \"min\": 0, \"max\": 600_000}\n", - "]]\n", - "\n", - "# Two parallel axes (income and childcare cost) changing together\n", - "example_axes = [[\n", - " {\"name\": \"employment_income\", \"count\": 1200, \"min\": 0, \"max\": 600_000},\n", - " {\"name\": \"childcare_expenses\", \"count\": 1200, \"min\": 0, \"max\": 20_000}\n", - "]]\n", - "\n", - "# Two perpendicular axes (income × age) – 1 200 × 10 = 12 000 variants\n", - "example_axes = [\n", - " [{\"name\": \"employment_income\", \"count\": 1200, \"min\": 0, \"max\": 600_000}],\n", - " [{\"name\": \"age\", \"index\": 0, \"count\": 10, \"min\": 18, \"max\": 65}]\n", - "]" - ] - }, - { - "cell_type": "markdown", - "id": "a7d8ead9", - "metadata": {}, - "source": [ - "### Visualizing the data " - ] - }, - { - "cell_type": "code", - "execution_count": 85, - "id": "b6aac408", - "metadata": {}, - "outputs": [], - "source": [ - "import plotly.graph_objects as go\n", - "from policyengine_core.charts import format_fig" - ] - }, - { - "cell_type": "code", - "execution_count": 86, - "id": "82a06b36", - "metadata": {}, - "outputs": [ - { - "data": { - "application/vnd.plotly.v1+json": { - "config": { - "plotlyServerURL": "https://plot.ly" - }, - "data": [ - { - "hovertemplate": "Employment Income: $%{x:,.0f}
CTC: $%{y:,.0f}", - "line": { - "color": "#2C6496", - "width": 3 - }, - "mode": "lines", - "name": "Child Tax Credit", - "type": "scatter", - "x": [ - 0, - 524.8602294921875, - 1049.720458984375, - 1574.5806884765625, - 2099.44091796875, - 2624.301025390625, - 3149.161376953125, - 3674.021484375, - 4198.8818359375, - 4723.74169921875, - 5248.60205078125, - 5773.4619140625, - 6298.32275390625, - 6823.18310546875, - 7348.04296875, - 7872.9033203125, - 8397.763671875, - 8922.623046875, - 9447.4833984375, - 9972.34375, - 10497.2041015625, - 11022.0634765625, - 11546.923828125, - 12071.78515625, - 12596.6455078125, - 13121.505859375, - 13646.3662109375, - 14171.2255859375, - 14696.0859375, - 15220.9462890625, - 15745.806640625, - 16270.6669921875, - 16795.52734375, - 17320.38671875, - 17845.24609375, - 18370.107421875, - 18894.966796875, - 19419.828125, - 19944.6875, - 20469.548828125, - 20994.408203125, - 21519.26953125, - 22044.126953125, - 22568.98828125, - 23093.84765625, - 23618.708984375, - 24143.5703125, - 24668.4296875, - 25193.291015625, - 25718.150390625, - 26243.01171875, - 26767.87109375, - 27292.732421875, - 27817.58984375, - 28342.451171875, - 28867.310546875, - 29392.171875, - 29917.03125, - 30441.892578125, - 30966.751953125, - 31491.61328125, - 32016.47265625, - 32541.333984375, - 33066.19140625, - 33591.0546875, - 34115.9140625, - 34640.7734375, - 35165.63671875, - 35690.4921875, - 36215.35546875, - 36740.21484375, - 37265.078125, - 37789.93359375, - 38314.79296875, - 38839.65625, - 39364.515625, - 39889.375, - 40414.234375, - 40939.09765625, - 41463.95703125, - 41988.81640625, - 42513.67578125, - 43038.5390625, - 43563.3984375, - 44088.25390625, - 44613.1171875, - 45137.9765625, - 45662.83984375, - 46187.6953125, - 46712.55859375, - 47237.41796875, - 47762.28125, - 48287.140625, - 48812, - 49336.859375, - 49861.71875, - 50386.58203125, - 50911.4375, - 51436.30078125, - 51961.16015625, - 52486.0234375, - 53010.87890625, - 53535.7421875, - 54060.6015625, - 54585.46484375, - 55110.3203125, - 55635.1796875, - 56160.04296875, - 56684.90234375, - 57209.76171875, - 57734.62109375, - 58259.484375, - 58784.34375, - 59309.203125, - 59834.0625, - 60358.92578125, - 60883.78515625, - 61408.64453125, - 61933.50390625, - 62458.36328125, - 62983.2265625, - 63508.08203125, - 64032.9453125, - 64557.8046875, - 65082.66796875, - 65607.5234375, - 66132.3828125, - 66657.25, - 67182.109375, - 67706.96875, - 68231.828125, - 68756.6796875, - 69281.546875, - 69806.40625, - 70331.2734375, - 70856.125, - 71380.984375, - 71905.8515625, - 72430.7109375, - 72955.5703125, - 73480.4296875, - 74005.2890625, - 74530.15625, - 75055.0078125, - 75579.8671875, - 76104.734375, - 76629.5859375, - 77154.453125, - 77679.3125, - 78204.171875, - 78729.03125, - 79253.890625, - 79778.75, - 80303.6171875, - 80828.46875, - 81353.3359375, - 81878.1953125, - 82403.046875, - 82927.9140625, - 83452.7734375, - 83977.6328125, - 84502.4921875, - 85027.3515625, - 85552.21875, - 86077.078125, - 86601.9296875, - 87126.796875, - 87651.65625, - 88176.5078125, - 88701.375, - 89226.234375, - 89751.1015625, - 90275.953125, - 90800.8125, - 91325.6796875, - 91850.5390625, - 92375.390625, - 92900.2578125, - 93425.1171875, - 93949.9765625, - 94474.8359375, - 94999.6953125, - 95524.5625, - 96049.4140625, - 96574.28125, - 97099.140625, - 97624, - 98148.859375, - 98673.71875, - 99198.578125, - 99723.4375, - 100248.296875, - 100773.1640625, - 101298.0234375, - 101822.875, - 102347.7421875, - 102872.6015625, - 103397.4609375, - 103922.3203125, - 104447.1796875, - 104972.046875, - 105496.8984375, - 106021.7578125, - 106546.625, - 107071.484375, - 107596.3359375, - 108121.203125, - 108646.0625, - 109170.9296875, - 109695.78125, - 110220.640625, - 110745.5078125, - 111270.359375, - 111795.21875, - 112320.0859375, - 112844.9453125, - 113369.8046875, - 113894.6640625, - 114419.5234375, - 114944.390625, - 115469.2421875, - 115994.1015625, - 116518.96875, - 117043.828125, - 117568.6875, - 118093.546875, - 118618.40625, - 119143.265625, - 119668.125, - 120192.9921875, - 120717.8515625, - 121242.703125, - 121767.5703125, - 122292.4296875, - 122817.2890625, - 123342.1484375, - 123867.0078125, - 124391.875, - 124916.7265625, - 125441.5859375, - 125966.453125, - 126491.3125, - 127016.1640625, - 127541.03125, - 128065.890625, - 128590.7578125, - 129115.609375, - 129640.46875, - 130165.3359375, - 130690.1875, - 131215.046875, - 131739.90625, - 132264.765625, - 132789.640625, - 133314.5, - 133839.34375, - 134364.21875, - 134889.078125, - 135413.9375, - 135938.796875, - 136463.65625, - 136988.515625, - 137513.359375, - 138038.234375, - 138563.09375, - 139087.96875, - 139612.8125, - 140137.671875, - 140662.546875, - 141187.390625, - 141712.25, - 142237.125, - 142761.96875, - 143286.828125, - 143811.703125, - 144336.546875, - 144861.421875, - 145386.28125, - 145911.140625, - 146436, - 146960.859375, - 147485.71875, - 148010.578125, - 148535.4375, - 149060.3125, - 149585.15625, - 150110.015625, - 150634.890625, - 151159.734375, - 151684.59375, - 152209.46875, - 152734.3125, - 153259.171875, - 153784.046875, - 154308.90625, - 154833.75, - 155358.625, - 155883.484375, - 156408.34375, - 156933.203125, - 157458.0625, - 157982.921875, - 158507.78125, - 159032.640625, - 159557.5, - 160082.359375, - 160607.234375, - 161132.078125, - 161656.9375, - 162181.8125, - 162706.671875, - 163231.515625, - 163756.390625, - 164281.25, - 164806.09375, - 165330.96875, - 165855.828125, - 166380.671875, - 166905.546875, - 167430.40625, - 167955.265625, - 168480.125, - 169004.984375, - 169529.859375, - 170054.703125, - 170579.5625, - 171104.4375, - 171629.28125, - 172154.15625, - 172679.015625, - 173203.859375, - 173728.734375, - 174253.59375, - 174778.4375, - 175303.3125, - 175828.171875, - 176353.015625, - 176877.890625, - 177402.75, - 177927.625, - 178452.46875, - 178977.328125, - 179502.203125, - 180027.046875, - 180551.90625, - 181076.78125, - 181601.625, - 182126.484375, - 182651.359375, - 183176.203125, - 183701.078125, - 184225.9375, - 184750.78125, - 185275.65625, - 185800.515625, - 186325.375, - 186850.234375, - 187375.09375, - 187899.953125, - 188424.8125, - 188949.671875, - 189474.546875, - 189999.390625, - 190524.25, - 191049.125, - 191573.96875, - 192098.828125, - 192623.703125, - 193148.5625, - 193673.40625, - 194198.28125, - 194723.140625, - 195248, - 195772.859375, - 196297.71875, - 196822.578125, - 197347.4375, - 197872.296875, - 198397.15625, - 198922.015625, - 199446.875, - 199971.734375, - 200496.59375, - 201021.46875, - 201546.328125, - 202071.171875, - 202596.046875, - 203120.90625, - 203645.75, - 204170.625, - 204695.484375, - 205220.328125, - 205745.203125, - 206270.0625, - 206794.921875, - 207319.78125, - 207844.640625, - 208369.5, - 208894.359375, - 209419.21875, - 209944.09375, - 210468.9375, - 210993.796875, - 211518.671875, - 212043.515625, - 212568.390625, - 213093.25, - 213618.09375, - 214142.96875, - 214667.828125, - 215192.671875, - 215717.546875, - 216242.40625, - 216767.28125, - 217292.125, - 217816.984375, - 218341.859375, - 218866.703125, - 219391.5625, - 219916.4375, - 220441.28125, - 220966.140625, - 221491.015625, - 222015.859375, - 222540.71875, - 223065.59375, - 223590.4375, - 224115.3125, - 224640.171875, - 225165.03125, - 225689.890625, - 226214.75, - 226739.609375, - 227264.46875, - 227789.328125, - 228314.203125, - 228839.046875, - 229363.90625, - 229888.78125, - 230413.625, - 230938.484375, - 231463.359375, - 231988.203125, - 232513.0625, - 233037.9375, - 233562.796875, - 234087.65625, - 234612.515625, - 235137.375, - 235662.234375, - 236187.09375, - 236711.953125, - 237236.8125, - 237761.671875, - 238286.53125, - 238811.390625, - 239336.25, - 239861.125, - 240385.984375, - 240910.828125, - 241435.703125, - 241960.5625, - 242485.40625, - 243010.28125, - 243535.140625, - 244059.984375, - 244584.859375, - 245109.71875, - 245634.578125, - 246159.4375, - 246684.296875, - 247209.15625, - 247734.015625, - 248258.875, - 248783.75, - 249308.59375, - 249833.453125, - 250358.328125, - 250883.171875, - 251408.046875, - 251932.90625, - 252457.75, - 252982.625, - 253507.484375, - 254032.328125, - 254557.203125, - 255082.0625, - 255606.90625, - 256131.78125, - 256656.640625, - 257181.515625, - 257706.359375, - 258231.21875, - 258756.09375, - 259280.9375, - 259805.796875, - 260330.671875, - 260855.515625, - 261380.375, - 261905.25, - 262430.09375, - 262954.96875, - 263479.8125, - 264004.6875, - 264529.53125, - 265054.40625, - 265579.28125, - 266104.125, - 266629, - 267153.84375, - 267678.6875, - 268203.5625, - 268728.4375, - 269253.28125, - 269778.15625, - 270303, - 270827.875, - 271352.71875, - 271877.59375, - 272402.46875, - 272927.3125, - 273452.15625, - 273977.03125, - 274501.875, - 275026.71875, - 275551.625, - 276076.46875, - 276601.34375, - 277126.1875, - 277651.03125, - 278175.9375, - 278700.78125, - 279225.625, - 279750.5, - 280275.34375, - 280800.1875, - 281325.09375, - 281849.9375, - 282374.78125, - 282899.65625, - 283424.5, - 283949.375, - 284474.25, - 284999.09375, - 285523.9375, - 286048.8125, - 286573.65625, - 287098.53125, - 287623.40625, - 288148.25, - 288673.09375, - 289197.96875, - 289722.84375, - 290247.6875, - 290772.5625, - 291297.40625, - 291822.28125, - 292347.125, - 292872, - 293396.875, - 293921.71875, - 294446.5625, - 294971.4375, - 295496.3125, - 296021.15625, - 296546.03125, - 297070.875, - 297595.71875, - 298120.625, - 298645.46875, - 299170.3125, - 299695.1875, - 300220.03125, - 300744.875, - 301269.78125, - 301794.625, - 302319.46875, - 302844.34375, - 303369.1875, - 303894.03125, - 304418.9375, - 304943.78125, - 305468.625, - 305993.5, - 306518.34375, - 307043.25, - 307568.09375, - 308092.9375, - 308617.8125, - 309142.65625, - 309667.5, - 310192.40625, - 310717.25, - 311242.09375, - 311766.96875, - 312291.8125, - 312816.6875, - 313341.5625, - 313866.40625, - 314391.25, - 314916.125, - 315441, - 315965.84375, - 316490.71875, - 317015.5625, - 317540.40625, - 318065.28125, - 318590.15625, - 319115, - 319639.875, - 320164.71875, - 320689.5625, - 321214.46875, - 321739.3125, - 322264.15625, - 322789.03125, - 323313.875, - 323838.75, - 324363.625, - 324888.46875, - 325413.34375, - 325938.1875, - 326463.03125, - 326987.9375, - 327512.78125, - 328037.625, - 328562.5, - 329087.34375, - 329612.1875, - 330137.09375, - 330661.9375, - 331186.78125, - 331711.65625, - 332236.5, - 332761.34375, - 333286.25, - 333811.09375, - 334335.9375, - 334860.8125, - 335385.65625, - 335910.53125, - 336435.40625, - 336960.25, - 337485.09375, - 338009.96875, - 338534.84375, - 339059.71875, - 339584.5625, - 340109.40625, - 340634.28125, - 341159.125, - 341684, - 342208.875, - 342733.71875, - 343258.5625, - 343783.4375, - 344308.3125, - 344833.15625, - 345358.03125, - 345882.875, - 346407.71875, - 346932.59375, - 347457.46875, - 347982.3125, - 348507.1875, - 349032.03125, - 349556.875, - 350081.78125, - 350606.625, - 351131.46875, - 351656.34375, - 352181.1875, - 352706.03125, - 353230.9375, - 353755.78125, - 354280.65625, - 354805.5, - 355330.34375, - 355855.25, - 356380.09375, - 356904.9375, - 357429.8125, - 357954.65625, - 358479.5, - 359004.40625, - 359529.25, - 360054.09375, - 360578.96875, - 361103.8125, - 361628.6875, - 362153.5625, - 362678.40625, - 363203.25, - 363728.125, - 364252.96875, - 364777.84375, - 365302.71875, - 365827.5625, - 366352.40625, - 366877.28125, - 367402.15625, - 367927, - 368451.875, - 368976.71875, - 369501.5625, - 370026.4375, - 370551.3125, - 371076.1875, - 371601.03125, - 372125.875, - 372650.75, - 373175.625, - 373700.46875, - 374225.34375, - 374750.1875, - 375275.03125, - 375799.90625, - 376324.78125, - 376849.625, - 377374.5, - 377899.34375, - 378424.1875, - 378949.09375, - 379473.9375, - 379998.78125, - 380523.65625, - 381048.5, - 381573.34375, - 382098.25, - 382623.09375, - 383147.9375, - 383672.8125, - 384197.65625, - 384722.53125, - 385247.40625, - 385772.25, - 386297.125, - 386821.96875, - 387346.8125, - 387871.71875, - 388396.5625, - 388921.40625, - 389446.28125, - 389971.125, - 390496, - 391020.875, - 391545.71875, - 392070.5625, - 392595.4375, - 393120.28125, - 393645.15625, - 394170.03125, - 394694.875, - 395219.71875, - 395744.59375, - 396269.46875, - 396794.3125, - 397319.1875, - 397844.03125, - 398368.875, - 398893.75, - 399418.625, - 399943.46875, - 400468.34375, - 400993.1875, - 401518.0625, - 402042.9375, - 402567.78125, - 403092.65625, - 403617.5, - 404142.34375, - 404667.21875, - 405192.09375, - 405716.9375, - 406241.8125, - 406766.65625, - 407291.5, - 407816.40625, - 408341.25, - 408866.09375, - 409390.96875, - 409915.8125, - 410440.65625, - 410965.5625, - 411490.40625, - 412015.25, - 412540.125, - 413064.96875, - 413589.84375, - 414114.71875, - 414639.5625, - 415164.40625, - 415689.28125, - 416214.125, - 416739, - 417263.875, - 417788.71875, - 418313.59375, - 418838.4375, - 419363.3125, - 419888.1875, - 420413.03125, - 420937.875, - 421462.75, - 421987.59375, - 422512.46875, - 423037.34375, - 423562.1875, - 424087.03125, - 424611.90625, - 425136.78125, - 425661.625, - 426186.5, - 426711.34375, - 427236.1875, - 427761.09375, - 428285.9375, - 428810.78125, - 429335.65625, - 429860.5, - 430385.34375, - 430910.25, - 431435.09375, - 431959.9375, - 432484.8125, - 433009.65625, - 433534.5625, - 434059.40625, - 434584.25, - 435109.125, - 435633.96875, - 436158.8125, - 436683.71875, - 437208.5625, - 437733.40625, - 438258.28125, - 438783.125, - 439307.96875, - 439832.875, - 440357.71875, - 440882.5625, - 441407.4375, - 441932.28125, - 442457.15625, - 442982.03125, - 443506.875, - 444031.71875, - 444556.59375, - 445081.4375, - 445606.3125, - 446131.1875, - 446656.03125, - 447180.875, - 447705.75, - 448230.625, - 448755.5, - 449280.34375, - 449805.1875, - 450330.0625, - 450854.9375, - 451379.78125, - 451904.65625, - 452429.5, - 452954.34375, - 453479.21875, - 454004.09375, - 454528.9375, - 455053.8125, - 455578.65625, - 456103.5, - 456628.40625, - 457153.25, - 457678.09375, - 458202.96875, - 458727.8125, - 459252.65625, - 459777.5625, - 460302.40625, - 460827.25, - 461352.125, - 461876.96875, - 462401.8125, - 462926.71875, - 463451.5625, - 463976.40625, - 464501.28125, - 465026.125, - 465551.03125, - 466075.875, - 466600.71875, - 467125.59375, - 467650.4375, - 468175.3125, - 468700.1875, - 469225.03125, - 469749.875, - 470274.75, - 470799.59375, - 471324.46875, - 471849.34375, - 472374.1875, - 472899.03125, - 473423.90625, - 473948.78125, - 474473.625, - 474998.5, - 475523.34375, - 476048.1875, - 476573.0625, - 477097.9375, - 477622.78125, - 478147.65625, - 478672.5, - 479197.34375, - 479722.25, - 480247.09375, - 480771.96875, - 481296.8125, - 481821.65625, - 482346.53125, - 482871.40625, - 483396.25, - 483921.125, - 484445.96875, - 484970.8125, - 485495.71875, - 486020.5625, - 486545.40625, - 487070.28125, - 487595.125, - 488119.96875, - 488644.875, - 489169.71875, - 489694.5625, - 490219.4375, - 490744.28125, - 491269.15625, - 491794.03125, - 492318.875, - 492843.71875, - 493368.59375, - 493893.4375, - 494418.3125, - 494943.1875, - 495468.03125, - 495992.90625, - 496517.75, - 497042.625, - 497567.5, - 498092.34375, - 498617.1875, - 499142.0625, - 499666.90625, - 500191.78125, - 500716.65625, - 501241.5, - 501766.34375, - 502291.21875, - 502816.09375, - 503340.9375, - 503865.8125, - 504390.65625, - 504915.5, - 505440.375, - 505965.25, - 506490.09375, - 507014.96875, - 507539.8125, - 508064.65625, - 508589.5625, - 509114.40625, - 509639.25, - 510164.125, - 510688.96875, - 511213.8125, - 511738.71875, - 512263.5625, - 512788.4375, - 513313.28125, - 513838.125, - 514363.03125, - 514887.875, - 515412.71875, - 515937.59375, - 516462.4375, - 516987.28125, - 517512.1875, - 518037.03125, - 518561.875, - 519086.75, - 519611.59375, - 520136.46875, - 520661.34375, - 521186.1875, - 521711.03125, - 522235.90625, - 522760.75, - 523285.625, - 523810.5, - 524335.375, - 524860.1875, - 525385.0625, - 525909.9375, - 526434.75, - 526959.625, - 527484.5, - 528009.375, - 528534.25, - 529059.0625, - 529583.9375, - 530108.8125, - 530633.6875, - 531158.5625, - 531683.375, - 532208.25, - 532733.125, - 533258, - 533782.8125, - 534307.6875, - 534832.5625, - 535357.375, - 535882.3125, - 536407.125, - 536932, - 537456.875, - 537981.6875, - 538506.5625, - 539031.4375, - 539556.3125, - 540081.125, - 540606, - 541130.875, - 541655.75, - 542180.625, - 542705.4375, - 543230.3125, - 543755.1875, - 544280, - 544804.9375, - 545329.75, - 545854.625, - 546379.5, - 546904.3125, - 547429.1875, - 547954.0625, - 548478.9375, - 549003.75, - 549528.625, - 550053.4375, - 550578.375, - 551103.25, - 551628.125, - 552152.9375, - 552677.8125, - 553202.6875, - 553727.5, - 554252.375, - 554777.25, - 555302.0625, - 555826.9375, - 556351.875, - 556876.6875, - 557401.5625, - 557926.4375, - 558451.25, - 558976.125, - 559501, - 560025.8125, - 560550.6875, - 561075.5625, - 561600.375, - 562125.3125, - 562650.1875, - 563175, - 563699.875, - 564224.75, - 564749.5625, - 565274.4375, - 565799.3125, - 566324.125, - 566849, - 567373.875, - 567898.75, - 568423.625, - 568948.5, - 569473.3125, - 569998.1875, - 570523.0625, - 571047.875, - 571572.75, - 572097.625, - 572622.4375, - 573147.3125, - 573672.25, - 574197.0625, - 574721.9375, - 575246.8125, - 575771.625, - 576296.5, - 576821.375, - 577346.1875, - 577871.0625, - 578395.9375, - 578920.75, - 579445.6875, - 579970.5625, - 580495.375, - 581020.25, - 581545.125, - 582069.9375, - 582594.8125, - 583119.6875, - 583644.5625, - 584169.375, - 584694.25, - 585219.1875, - 585744, - 586268.875, - 586793.75, - 587318.5625, - 587843.4375, - 588368.3125, - 588893.125, - 589418, - 589942.875, - 590467.6875, - 590992.625, - 591517.5, - 592042.3125, - 592567.1875, - 593092.0625, - 593616.875, - 594141.75, - 594666.625, - 595191.4375, - 595716.3125, - 596241.25, - 596766.0625, - 597290.9375, - 597815.8125, - 598340.625, - 598865.5, - 599390.375, - 599915.1875, - 600440.0625, - 600964.9375, - 601489.75, - 602014.625, - 602539.5625, - 603064.375, - 603589.25, - 604114.125, - 604638.9375, - 605163.8125, - 605688.6875, - 606213.5, - 606738.375, - 607263.25, - 607788.0625, - 608313, - 608837.875, - 609362.6875, - 609887.5625, - 610412.4375, - 610937.25, - 611462.125, - 611987, - 612511.8125, - 613036.6875, - 613561.625, - 614086.5, - 614611.3125, - 615136.1875, - 615661.0625, - 616185.875, - 616710.75, - 617235.625, - 617760.4375, - 618285.3125, - 618810.1875, - 619335, - 619859.9375, - 620384.8125, - 620909.625, - 621434.5, - 621959.375, - 622484.1875, - 623009.0625, - 623533.9375, - 624058.75, - 624583.625, - 625108.5625, - 625633.375, - 626158.25, - 626683.125, - 627207.9375, - 627732.8125, - 628257.6875, - 628782.5, - 629307.375 - ], - "y": [ - 0, - 0, - 0, - 0, - 0, - 18.64515495300293, - 97.37421417236328, - 176.1032257080078, - 254.83229064941406, - 333.561279296875, - 412.2903137207031, - 491.0193176269531, - 569.7484130859375, - 648.4774780273438, - 727.2064819335938, - 805.935546875, - 884.6646118164062, - 963.3934936523438, - 1042.12255859375, - 1120.8515625, - 1199.5806884765625, - 1278.3095703125, - 1357.03857421875, - 1435.767822265625, - 1514.496826171875, - 1593.2259521484375, - 1671.9549560546875, - 1750.6839599609375, - 1829.4129638671875, - 1908.1419677734375, - 1986.87109375, - 2065.60009765625, - 2144.3291015625, - 2223.05810546875, - 2301.787109375, - 2380.51611328125, - 2459.2451171875, - 2537.974365234375, - 2616.703125, - 2695.432373046875, - 2774.161376953125, - 2852.890625, - 2931.619140625, - 3010.348388671875, - 3089.077392578125, - 3167.806396484375, - 3246.53564453125, - 3325.2646484375, - 3403.993896484375, - 3482.72265625, - 3561.451904296875, - 3640.180908203125, - 3718.909912109375, - 3797.638671875, - 3876.367919921875, - 3955.0966796875, - 4000, - 4000, - 4000, - 4000, - 4000, - 4000, - 4000, - 4000, - 4000, - 4000, - 4000, - 4000, - 4000, - 4000, - 4000, - 4000, - 4000, - 4000, - 4000, - 4000, - 4000, - 4000, - 4000, - 4000, - 4000, - 4000, - 4000, - 4000, - 4000, - 4000, - 4000, - 4000, - 4000, - 4000, - 4000, - 4000, - 4000, - 4000, - 4000, - 4000, - 4000, - 4000, - 4000, - 4000, - 4000, - 4000, - 4000, - 4000, - 4000, - 4000, - 4000, - 4000, - 4000, - 4000, - 4000, - 4000, - 4000, - 4000, - 4000, - 4000, - 4000, - 4000, - 4000, - 4000, - 4000, - 4000, - 4000, - 4000, - 4000, - 4000, - 4000, - 4000, - 4000, - 4000, - 4000, - 4000, - 4000, - 4000, - 4000, - 4000, - 4000, - 4000, - 4000, - 4000, - 4000, - 4000, - 4000, - 4000, - 4000, - 4000, - 4000, - 4000, - 4000, - 4000, - 4000, - 4000, - 4000, - 4000, - 4000, - 4000, - 4000, - 4000, - 4000, - 4000, - 4000, - 4000, - 4000, - 4000, - 4000, - 4000, - 4000, - 4000, - 4000, - 4000, - 4000, - 4000, - 4000, - 4000, - 4000, - 4000, - 4000, - 4000, - 4000, - 4000, - 4000, - 4000, - 4000, - 4000, - 4000, - 4000, - 4000, - 4000, - 4000, - 4000, - 4000, - 4000, - 4000, - 4000, - 4000, - 4000, - 4000, - 4000, - 4000, - 4000, - 4000, - 4000, - 4000, - 4000, - 4000, - 4000, - 4000, - 4000, - 4000, - 4000, - 4000, - 4000, - 4000, - 4000, - 4000, - 4000, - 4000, - 4000, - 4000, - 4000, - 4000, - 4000, - 4000, - 4000, - 4000, - 4000, - 4000, - 4000, - 4000, - 4000, - 4000, - 4000, - 4000, - 4000, - 4000, - 4000, - 4000, - 4000, - 4000, - 4000, - 4000, - 4000, - 4000, - 4000, - 4000, - 4000, - 4000, - 4000, - 4000, - 4000, - 4000, - 4000, - 4000, - 4000, - 4000, - 4000, - 4000, - 4000, - 4000, - 4000, - 4000, - 4000, - 4000, - 4000, - 4000, - 4000, - 4000, - 4000, - 4000, - 4000, - 4000, - 4000, - 4000, - 4000, - 4000, - 4000, - 4000, - 4000, - 4000, - 4000, - 4000, - 4000, - 4000, - 4000, - 4000, - 4000, - 4000, - 4000, - 4000, - 4000, - 4000, - 4000, - 4000, - 4000, - 4000, - 4000, - 4000, - 4000, - 4000, - 4000, - 4000, - 4000, - 4000, - 4000, - 4000, - 4000, - 4000, - 4000, - 4000, - 4000, - 4000, - 4000, - 4000, - 4000, - 4000, - 4000, - 4000, - 4000, - 4000, - 4000, - 4000, - 4000, - 4000, - 4000, - 4000, - 4000, - 4000, - 4000, - 4000, - 4000, - 4000, - 4000, - 4000, - 4000, - 4000, - 4000, - 4000, - 4000, - 4000, - 4000, - 4000, - 4000, - 4000, - 4000, - 4000, - 4000, - 4000, - 4000, - 4000, - 4000, - 4000, - 4000, - 4000, - 4000, - 4000, - 4000, - 4000, - 4000, - 4000, - 4000, - 4000, - 4000, - 4000, - 4000, - 4000, - 4000, - 4000, - 4000, - 4000, - 4000, - 4000, - 4000, - 4000, - 4000, - 4000, - 4000, - 4000, - 4000, - 4000, - 4000, - 4000, - 4000, - 4000, - 4000, - 4000, - 4000, - 4000, - 4000, - 4000, - 4000, - 4000, - 4000, - 4000, - 4000, - 4000, - 4000, - 4000, - 4000, - 4000, - 4000, - 4000, - 4000, - 4000, - 4000, - 4000, - 4000, - 4000, - 4000, - 4000, - 4000, - 4000, - 4000, - 4000, - 4000, - 4000, - 4000, - 4000, - 4000, - 4000, - 4000, - 4000, - 4000, - 4000, - 4000, - 4000, - 4000, - 4000, - 4000, - 4000, - 4000, - 4000, - 4000, - 4000, - 4000, - 4000, - 4000, - 4000, - 4000, - 4000, - 4000, - 4000, - 4000, - 4000, - 4000, - 4000, - 4000, - 4000, - 4000, - 4000, - 4000, - 4000, - 4000, - 4000, - 4000, - 4000, - 4000, - 4000, - 4000, - 4000, - 4000, - 4000, - 4000, - 4000, - 4000, - 4000, - 4000, - 4000, - 4000, - 4000, - 4000, - 4000, - 4000, - 4000, - 4000, - 4000, - 4000, - 4000, - 4000, - 4000, - 4000, - 4000, - 4000, - 4000, - 4000, - 4000, - 4000, - 4000, - 4000, - 4000, - 4000, - 4000, - 4000, - 4000, - 4000, - 4000, - 4000, - 4000, - 4000, - 4000, - 4000, - 4000, - 4000, - 4000, - 4000, - 4000, - 4000, - 4000, - 4000, - 4000, - 4000, - 4000, - 4000, - 4000, - 4000, - 4000, - 4000, - 4000, - 4000, - 4000, - 4000, - 4000, - 4000, - 4000, - 4000, - 4000, - 4000, - 4000, - 4000, - 4000, - 4000, - 4000, - 4000, - 4000, - 4000, - 4000, - 4000, - 4000, - 4000, - 4000, - 4000, - 4000, - 4000, - 4000, - 4000, - 4000, - 4000, - 4000, - 4000, - 4000, - 4000, - 4000, - 4000, - 4000, - 4000, - 4000, - 4000, - 4000, - 4000, - 4000, - 4000, - 4000, - 4000, - 4000, - 4000, - 4000, - 4000, - 4000, - 4000, - 4000, - 4000, - 4000, - 4000, - 4000, - 4000, - 4000, - 4000, - 4000, - 4000, - 4000, - 4000, - 4000, - 4000, - 4000, - 4000, - 4000, - 4000, - 4000, - 4000, - 4000, - 4000, - 4000, - 4000, - 4000, - 4000, - 4000, - 4000, - 4000, - 4000, - 4000, - 4000, - 4000, - 4000, - 4000, - 4000, - 4000, - 4000, - 4000, - 4000, - 4000, - 4000, - 4000, - 4000, - 4000, - 4000, - 4000, - 4000, - 4000, - 4000, - 4000, - 4000, - 4000, - 4000, - 4000, - 4000, - 4000, - 4000, - 4000, - 4000, - 4000, - 4000, - 4000, - 4000, - 4000, - 4000, - 4000, - 4000, - 4000, - 4000, - 4000, - 4000, - 4000, - 4000, - 4000, - 4000, - 4000, - 4000, - 4000, - 4000, - 4000, - 4000, - 4000, - 4000, - 4000, - 4000, - 4000, - 4000, - 4000, - 4000, - 4000, - 4000, - 4000, - 4000, - 4000, - 4000, - 4000, - 4000, - 4000, - 4000, - 4000, - 4000, - 4000, - 4000, - 4000, - 4000, - 4000, - 4000, - 4000, - 4000, - 4000, - 4000, - 4000, - 4000, - 4000, - 4000, - 4000, - 4000, - 4000, - 4000, - 4000, - 4000, - 4000, - 4000, - 4000, - 4000, - 4000, - 4000, - 4000, - 4000, - 4000, - 4000, - 4000, - 4000, - 4000, - 4000, - 4000, - 4000, - 4000, - 4000, - 4000, - 4000, - 4000, - 4000, - 4000, - 4000, - 4000, - 4000, - 4000, - 4000, - 4000, - 4000, - 4000, - 4000, - 4000, - 4000, - 4000, - 4000, - 4000, - 4000, - 4000, - 4000, - 4000, - 4000, - 4000, - 4000, - 4000, - 4000, - 4000, - 4000, - 4000, - 4000, - 4000, - 4000, - 4000, - 4000, - 4000, - 4000, - 4000, - 4000, - 4000, - 4000, - 4000, - 4000, - 4000, - 4000, - 4000, - 4000, - 4000, - 4000, - 4000, - 4000, - 4000, - 4000, - 4000, - 3950, - 3950, - 3900, - 3850, - 3850, - 3800, - 3800, - 3750, - 3750, - 3700, - 3700, - 3650, - 3650, - 3600, - 3600, - 3550, - 3550, - 3500, - 3500, - 3450, - 3450, - 3400, - 3350, - 3350, - 3300, - 3300, - 3250, - 3250, - 3200, - 3200, - 3150, - 3150, - 3100, - 3100, - 3050, - 3050, - 3000, - 3000, - 2950, - 2950, - 2900, - 2900, - 2850, - 2800, - 2800, - 2750, - 2750, - 2700, - 2700, - 2650, - 2650, - 2600, - 2600, - 2550, - 2550, - 2500, - 2500, - 2450, - 2450, - 2400, - 2400, - 2350, - 2300, - 2300, - 2250, - 2250, - 2200, - 2200, - 2150, - 2150, - 2100, - 2100, - 2050, - 2050, - 2000, - 2000, - 1950, - 1950, - 1900, - 1900, - 1850, - 1850, - 1800, - 1750, - 1750, - 1700, - 1700, - 1650, - 1650, - 1600, - 1600, - 1550, - 1550, - 1500, - 1500, - 1450, - 1450, - 1400, - 1400, - 1350, - 1350, - 1300, - 1250, - 1250, - 1200, - 1200, - 1150, - 1150, - 1100, - 1100, - 1050, - 1050, - 1000, - 1000, - 950, - 950, - 900, - 900, - 850, - 850, - 800, - 800, - 750, - 700, - 700, - 650, - 650, - 600, - 600, - 550, - 550, - 500, - 500, - 450, - 450, - 400, - 400, - 350, - 350, - 300, - 300, - 250, - 250, - 200, - 150, - 150, - 100, - 100, - 50, - 50, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0 - ] - } - ], - "layout": { - "font": { - "color": "black", - "family": "Roboto Serif" - }, - "height": 600, - "hovermode": "x unified", - "images": [ - { - "sizex": 0.15, - "sizey": 0.15, - "source": "https://raw.githubusercontent.com/PolicyEngine/policyengine-app/master/src/images/logos/policyengine/blue.png", - "x": 1.1, - "xanchor": "right", - "xref": "paper", - "y": -0.15, - "yanchor": "bottom", - "yref": "paper" - } - ], - "modebar": { - "bgcolor": "rgba(0,0,0,0)", - "color": "rgba(0,0,0,0)" - }, - "template": { - "data": { - "bar": [ - { - "error_x": { - "color": "#2a3f5f" - }, - "error_y": { - "color": "#2a3f5f" - }, - "marker": { - "line": { - "color": "white", - "width": 0.5 - }, - "pattern": { - "fillmode": "overlay", - "size": 10, - "solidity": 0.2 - } - }, - "type": "bar" - } - ], - "barpolar": [ - { - "marker": { - "line": { - "color": "white", - "width": 0.5 - }, - "pattern": { - "fillmode": "overlay", - "size": 10, - "solidity": 0.2 - } - }, - "type": "barpolar" - } - ], - "carpet": [ - { - "aaxis": { - "endlinecolor": "#2a3f5f", - "gridcolor": "#C8D4E3", - "linecolor": "#C8D4E3", - "minorgridcolor": "#C8D4E3", - "startlinecolor": "#2a3f5f" - }, - "baxis": { - "endlinecolor": "#2a3f5f", - "gridcolor": "#C8D4E3", - "linecolor": "#C8D4E3", - "minorgridcolor": "#C8D4E3", - "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": "white", - "showlakes": true, - "showland": true, - "subunitcolor": "#C8D4E3" - }, - "hoverlabel": { - "align": "left" - }, - "hovermode": "closest", - "mapbox": { - "style": "light" - }, - "paper_bgcolor": "white", - "plot_bgcolor": "white", - "polar": { - "angularaxis": { - "gridcolor": "#EBF0F8", - "linecolor": "#EBF0F8", - "ticks": "" - }, - "bgcolor": "white", - "radialaxis": { - "gridcolor": "#EBF0F8", - "linecolor": "#EBF0F8", - "ticks": "" - } - }, - "scene": { - "xaxis": { - "backgroundcolor": "white", - "gridcolor": "#DFE8F3", - "gridwidth": 2, - "linecolor": "#EBF0F8", - "showbackground": true, - "ticks": "", - "zerolinecolor": "#EBF0F8" - }, - "yaxis": { - "backgroundcolor": "white", - "gridcolor": "#DFE8F3", - "gridwidth": 2, - "linecolor": "#EBF0F8", - "showbackground": true, - "ticks": "", - "zerolinecolor": "#EBF0F8" - }, - "zaxis": { - "backgroundcolor": "white", - "gridcolor": "#DFE8F3", - "gridwidth": 2, - "linecolor": "#EBF0F8", - "showbackground": true, - "ticks": "", - "zerolinecolor": "#EBF0F8" - } - }, - "shapedefaults": { - "line": { - "color": "#2a3f5f" - } - }, - "ternary": { - "aaxis": { - "gridcolor": "#DFE8F3", - "linecolor": "#A2B1C6", - "ticks": "" - }, - "baxis": { - "gridcolor": "#DFE8F3", - "linecolor": "#A2B1C6", - "ticks": "" - }, - "bgcolor": "white", - "caxis": { - "gridcolor": "#DFE8F3", - "linecolor": "#A2B1C6", - "ticks": "" - } - }, - "title": { - "x": 0.05 - }, - "xaxis": { - "automargin": true, - "gridcolor": "#EBF0F8", - "linecolor": "#EBF0F8", - "ticks": "", - "title": { - "standoff": 15 - }, - "zerolinecolor": "#EBF0F8", - "zerolinewidth": 2 - }, - "yaxis": { - "automargin": true, - "gridcolor": "#EBF0F8", - "linecolor": "#EBF0F8", - "ticks": "", - "title": { - "standoff": 15 - }, - "zerolinecolor": "#EBF0F8", - "zerolinewidth": 2 - } - } - }, - "title": { - "text": "Child Tax Credit by Employment Income
Family of 4 in California" - }, - "width": 800, - "xaxis": { - "tickformat": "$,.0f", - "title": { - "text": "Employment Income" - } - }, - "yaxis": { - "tickformat": "$,.0f", - "title": { - "text": "Child Tax Credit" - } - } - } - } - }, - "metadata": {}, - "output_type": "display_data" - } - ], - "source": [ - "# Create a Plotly plot of CTC vs Employment Income\n", - "fig = go.Figure()\n", - "\n", - "fig.add_trace(go.Scatter(\n", - " x=income_range,\n", - " y=ctc_range,\n", - " mode='lines',\n", - " name='Child Tax Credit',\n", - " line=dict(color='#2C6496', width=3),\n", - " hovertemplate='Employment Income: $%{x:,.0f}
CTC: $%{y:,.0f}'\n", - "))\n", - "\n", - "fig.update_layout(\n", - " title='Child Tax Credit by Employment Income
Family of 4 in California',\n", - " xaxis_title='Employment Income',\n", - " yaxis_title='Child Tax Credit',\n", - " xaxis=dict(tickformat='$,.0f'),\n", - " yaxis=dict(tickformat='$,.0f'),\n", - " hovermode='x unified',\n", - " template='plotly_white'\n", - ")\n", - "\n", - "# Apply PolicyEngine formatting\n", - "fig = format_fig(fig)\n", - "fig.show()" - ] - }, - { - "cell_type": "markdown", - "id": "d56e7d1a", - "metadata": {}, - "source": [ - "## Summary\n", - "\n", - "This notebook demonstrated:\n", - "1. **Creating household situations** with proper entity relationships\n", - "2. **Running simulations** and calculating variables\n", - "3. **Understanding variable dependencies** through the calculation tree\n", - "4. **Creating and applying reforms** (both simple and complex)\n", - "5. **Comparing baseline and reform** scenarios\n", - "6. **Using calculate_dataframe** for efficient multi-variable analysis\n", - "7. **Calculating marginal tax rates** through incremental changes\n", - "8. **Using axes** to vary parameters and analyze responses\n", - "\n", - "Key takeaways:\n", - "- PolicyEngine uses a hierarchical entity structure (person → tax unit → household)\n", - "- Variables can depend on other variables, creating a calculation tree\n", - "- Reforms can modify parameters or add new calculation logic\n", - "- The Simulation class provides flexible methods for analysis\n", - "- DataFrames integration makes it easy to analyze results with pandas" - ] } ], "metadata": { From cc463252b71a0c69d7aa46de24d5137debdd24a6 Mon Sep 17 00:00:00 2001 From: PavelMakarchuk Date: Thu, 10 Jul 2025 16:53:58 +0200 Subject: [PATCH 3/6] clean up --- educational/household_analysis.csv | 4 +- educational/household_simulation.ipynb | 560 +++---------------------- 2 files changed, 62 insertions(+), 502 deletions(-) diff --git a/educational/household_analysis.csv b/educational/household_analysis.csv index 9fa3461..ccdc17e 100644 --- a/educational/household_analysis.csv +++ b/educational/household_analysis.csv @@ -1,2 +1,2 @@ -employment_income,adjusted_gross_income,taxable_income,income_tax,ctc_value,eitc,snap,household_net_income,household_id,scenario -120000.0,120000.0,90000.0,6323.0,4000.0,0.0,0.0,102116.85,household,baseline +employment_income,adjusted_gross_income,taxable_income,income_tax,ctc_value,eitc,snap,state_code,household_net_income,household_id,scenario +120000.0,120000.0,90000.0,6323.0,4000.0,0.0,0.0,4,102116.85,household,baseline diff --git a/educational/household_simulation.ipynb b/educational/household_simulation.ipynb index c4fa920..9c30825 100644 --- a/educational/household_simulation.ipynb +++ b/educational/household_simulation.ipynb @@ -35,19 +35,10 @@ }, { "cell_type": "code", - "execution_count": 1, + "execution_count": 102, "id": "a46899e7", "metadata": {}, - "outputs": [ - { - "name": "stderr", - "output_type": "stream", - "text": [ - "/Users/pavelmakarchuk/anaconda3/envs/pe/lib/python3.10/site-packages/tqdm/auto.py:21: TqdmWarning: IProgress not found. Please update jupyter and ipywidgets. See https://ipywidgets.readthedocs.io/en/stable/user_install.html\n", - " from .autonotebook import tqdm as notebook_tqdm\n" - ] - } - ], + "outputs": [], "source": [ "# Import necessary libraries\n", "from policyengine_us import Simulation\n", @@ -106,7 +97,7 @@ }, { "cell_type": "code", - "execution_count": 2, + "execution_count": 103, "id": "725bb985", "metadata": {}, "outputs": [], @@ -178,7 +169,7 @@ }, { "cell_type": "code", - "execution_count": 3, + "execution_count": 104, "id": "3257517a", "metadata": {}, "outputs": [], @@ -189,7 +180,7 @@ }, { "cell_type": "code", - "execution_count": 4, + "execution_count": 105, "id": "dd0c97dd", "metadata": {}, "outputs": [], @@ -200,7 +191,7 @@ }, { "cell_type": "code", - "execution_count": 5, + "execution_count": 106, "id": "bd5ebe5c", "metadata": {}, "outputs": [], @@ -212,7 +203,7 @@ }, { "cell_type": "code", - "execution_count": 6, + "execution_count": 107, "id": "c049a19b", "metadata": {}, "outputs": [], @@ -225,7 +216,7 @@ }, { "cell_type": "code", - "execution_count": 7, + "execution_count": 108, "id": "d89b98d0", "metadata": {}, "outputs": [], @@ -236,7 +227,7 @@ }, { "cell_type": "code", - "execution_count": 8, + "execution_count": 109, "id": "de0db305", "metadata": {}, "outputs": [], @@ -258,7 +249,7 @@ }, { "cell_type": "code", - "execution_count": 9, + "execution_count": 110, "id": "47f49cc7", "metadata": {}, "outputs": [ @@ -312,7 +303,7 @@ }, { "cell_type": "code", - "execution_count": 10, + "execution_count": 111, "id": "059949c5", "metadata": {}, "outputs": [ @@ -340,7 +331,7 @@ }, { "cell_type": "code", - "execution_count": 11, + "execution_count": 112, "id": "848a47c6", "metadata": {}, "outputs": [ @@ -364,7 +355,7 @@ }, { "cell_type": "code", - "execution_count": 88, + "execution_count": 113, "id": "a410a154", "metadata": {}, "outputs": [], @@ -387,7 +378,7 @@ }, { "cell_type": "code", - "execution_count": 12, + "execution_count": 114, "id": "9e222d38", "metadata": {}, "outputs": [ @@ -438,7 +429,7 @@ }, { "cell_type": "code", - "execution_count": 13, + "execution_count": 115, "id": "e63152d0", "metadata": {}, "outputs": [], @@ -449,7 +440,7 @@ }, { "cell_type": "code", - "execution_count": 14, + "execution_count": 116, "id": "0c02d2b7", "metadata": {}, "outputs": [ @@ -513,7 +504,7 @@ }, { "cell_type": "code", - "execution_count": 93, + "execution_count": 117, "id": "5393ac49", "metadata": {}, "outputs": [ @@ -560,385 +551,9 @@ "sim_for_trace.tracer.print_computation_log(max_depth=4)\n" ] }, - { - "cell_type": "markdown", - "id": "636de9bc", - "metadata": {}, - "source": [ - "# Notebook 1: Household Simulation with PolicyEngine\n" - ] - }, { "cell_type": "code", - "execution_count": 16, - "id": "e35ce716", - "metadata": {}, - "outputs": [], - "source": [ - "# Import necessary libraries\n", - "from policyengine_us import Simulation\n", - "from policyengine_core.reforms import Reform\n", - "import pandas as pd\n", - "import numpy as np" - ] - }, - { - "cell_type": "markdown", - "id": "6e4131ec", - "metadata": {}, - "source": [ - "## Part 1: Creating a Household Situation\n", - "\n", - "### Understanding Core Concepts\n", - "Before we begin, let's understand the key components:\n", - "- **Entities**: Person, tax unit, SPM unit, marital unit, family, household\n", - "- **Variables**: Can be inputs (employment_income) or calculations (ctc_value)\n", - "- **Parameters**: Policy features like tax rates, benefit amounts\n", - "- **Periods**: Time periods for which we calculate values" - ] - }, - { - "cell_type": "markdown", - "id": "9237bea2", - "metadata": {}, - "source": [ - "### PolicyEngine Repositories\n", - "To explore the source code and understand how variables and parameters are defined:\n", - "- **[policyengine-us](https://github.com/PolicyEngine/policyengine-us)**: Contains all US-specific variables, parameters, and policy logic\n", - " - Variables: `/policyengine_us/variables/`\n", - " - Parameters: `/policyengine_us/parameters/`\n", - "- **[policyengine-core](https://github.com/PolicyEngine/policyengine-core)**: Contains the core simulation frameworks\n", - " - `Simulation` class: `/policyengine_core/simulations/simulation.py`\n", - " - `Microsimulation` class: `/policyengine_core/simulations/microsimulation.py`" - ] - }, - { - "cell_type": "markdown", - "id": "276ba238", - "metadata": {}, - "source": [ - "### Getting Started: Using PolicyEngine's Web Interface\n", - "The easiest way to create a household situation is to use PolicyEngine's web interface:\n", - "1. Go to https://policyengine.org/us/household\n", - "2. Enter your household details using the interactive form\n", - "3. Navigate to the \"Reproduce in Python\" section\n", - "4. Copy the generated code\n", - "\n", - "For example, after creating a household at:\n", - "https://policyengine.org/us/household?focus=householdOutput.pythonReproducibility&household=54688\n", - "\n", - "You'll find ready-to-use Python code that creates the situation dictionary for you!" - ] - }, - { - "cell_type": "code", - "execution_count": 17, - "id": "fcbb04bd", - "metadata": {}, - "outputs": [], - "source": [ - "# Create a household situation dictionary\n", - "# This represents a family of 4: 2 adults and 2 children in California\n", - "\n", - "situation = {\n", - " \"people\": {\n", - " \"parent1\": {\n", - " \"age\": {\"2025\": 35},\n", - " \"employment_income\": {\"2025\": 75000}\n", - " },\n", - " \"parent2\": {\n", - " \"age\": {\"2025\": 33},\n", - " \"employment_income\": {\"2025\": 45000}\n", - " },\n", - " \"child1\": {\n", - " \"age\": {\"2025\": 10}\n", - " },\n", - " \"child2\": {\n", - " \"age\": {\"2025\": 7}\n", - " }\n", - " },\n", - " \"families\": {\n", - " \"family\": {\n", - " \"members\": [\"parent1\", \"parent2\", \"child1\", \"child2\"]\n", - " }\n", - " },\n", - " \"marital_units\": {\n", - " \"parents\": {\n", - " \"members\": [\"parent1\", \"parent2\"]\n", - " },\n", - " \"child1_marital_unit\": {\n", - " \"members\": [\"child1\"],\n", - " \"marital_unit_id\": {\"2025\": 1}\n", - " },\n", - " \"child2_marital_unit\": {\n", - " \"members\": [\"child2\"],\n", - " \"marital_unit_id\": {\"2025\": 2}\n", - " }\n", - " },\n", - " \"tax_units\": {\n", - " \"tax_unit\": {\n", - " \"members\": [\"parent1\", \"parent2\", \"child1\", \"child2\"]\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\": \"CA\"}\n", - " }\n", - " }\n", - "}" - ] - }, - { - "cell_type": "markdown", - "id": "459b1e35", - "metadata": {}, - "source": [ - "## Part 2: Running a Baseline Simulation\n" - ] - }, - { - "cell_type": "code", - "execution_count": 18, - "id": "9c8a29a0", - "metadata": {}, - "outputs": [], - "source": [ - "# Create a simulation object with our household situation\n", - "baseline_sim = Simulation(situation=situation)" - ] - }, - { - "cell_type": "code", - "execution_count": 19, - "id": "71c2953f", - "metadata": {}, - "outputs": [], - "source": [ - "# Calculate key variables for 2025\n", - "PERIOD = 2025" - ] - }, - { - "cell_type": "code", - "execution_count": 20, - "id": "42ec3ae4", - "metadata": {}, - "outputs": [], - "source": [ - "# Income-related variables\n", - "employment_income = baseline_sim.calculate(\"employment_income\", PERIOD)\n", - "adjusted_gross_income = baseline_sim.calculate(\"adjusted_gross_income\", PERIOD)" - ] - }, - { - "cell_type": "code", - "execution_count": 21, - "id": "f3d688f9", - "metadata": {}, - "outputs": [], - "source": [ - "# Tax-related variables\n", - "income_tax = baseline_sim.calculate(\"income_tax\", PERIOD)\n", - "ctc = baseline_sim.calculate(\"ctc_value\", PERIOD)\n", - "eitc = baseline_sim.calculate(\"eitc\", PERIOD)" - ] - }, - { - "cell_type": "code", - "execution_count": 22, - "id": "dde1834a", - "metadata": {}, - "outputs": [], - "source": [ - "# Benefits\n", - "snap = baseline_sim.calculate(\"snap\", PERIOD)" - ] - }, - { - "cell_type": "code", - "execution_count": 23, - "id": "2cafdaf6", - "metadata": {}, - "outputs": [], - "source": [ - "# Net income\n", - "household_net_income = baseline_sim.calculate(\"household_net_income\", PERIOD)" - ] - }, - { - "cell_type": "markdown", - "id": "1f4bbf09", - "metadata": {}, - "source": [ - "### Understanding the calculate() Return Value\n", - "**IMPORTANT**: The calculate() method returns a NumPy array with values for each entity\n", - "- For person-level variables: array length = number of people (4 in our case)\n", - "- For household-level variables: array length = number of households (1 in our case)" - ] - }, - { - "cell_type": "code", - "execution_count": 24, - "id": "f531d494", - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "PERSON-LEVEL VARIABLE (employment_income):\n", - " Array: [75000. 45000. 0. 0.]\n", - " Shape: (4,)\n", - " Length: 4 (one value per person)\n", - " Individual values: Parent1=$75,000, Parent2=$45,000, Child1=$0, Child2=$0\n" - ] - } - ], - "source": [ - "# Person-level variable: employment_income\n", - "print(\"PERSON-LEVEL VARIABLE (employment_income):\")\n", - "print(f\" Array: {employment_income}\")\n", - "print(f\" Shape: {employment_income.shape}\")\n", - "print(f\" Length: {len(employment_income)} (one value per person)\")\n", - "print(f\" Individual values: Parent1=${employment_income[0]:,.0f}, Parent2=${employment_income[1]:,.0f}, Child1=${employment_income[2]:,.0f}, Child2=${employment_income[3]:,.0f}\")" - ] - }, - { - "cell_type": "code", - "execution_count": 25, - "id": "80b3a32d", - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "\n", - "HOUSEHOLD-LEVEL VARIABLE (household_net_income):\n", - " Array: [102116.85]\n", - " Shape: (1,)\n", - " Length: 1 (one value per household)\n", - " Value: $102,117\n" - ] - } - ], - "source": [ - "# Household-level variable: household_net_income\n", - "print(\"\\nHOUSEHOLD-LEVEL VARIABLE (household_net_income):\")\n", - "print(f\" Array: {household_net_income}\")\n", - "print(f\" Shape: {household_net_income.shape}\")\n", - "print(f\" Length: {len(household_net_income)} (one value per household)\")\n", - "print(f\" Value: ${household_net_income[0]:,.0f}\")" - ] - }, - { - "cell_type": "code", - "execution_count": 26, - "id": "d1416ea7", - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "\n", - "Using .sum():\n", - " employment_income.sum() = $120,000 (adds all 4 person values)\n", - " household_net_income.sum() = $102,117 (just the 1 household value)\n" - ] - } - ], - "source": [ - "# This demonstrates why we use .sum() - it works correctly regardless of entity level\n", - "print(f\"\\nUsing .sum():\")\n", - "print(f\" employment_income.sum() = ${employment_income.sum():,.0f} (adds all 4 person values)\")\n", - "print(f\" household_net_income.sum() = ${household_net_income.sum():,.0f} (just the 1 household value)\")" - ] - }, - { - "cell_type": "markdown", - "id": "41eae444", - "metadata": {}, - "source": [ - "### Common Array Operations\n", - "- `.sum()` - adds all values in the array\n", - "- `.mean()` - calculates the average\n", - "- `.max()` - finds the maximum value\n", - "- `.min()` - finds the minimum value\n", - "- `[index]` - accesses individual elements\n" - ] - }, - { - "cell_type": "code", - "execution_count": 27, - "id": "cb50cc54", - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "\n", - "=== BASELINE RESULTS ===\n", - "Total Employment Income: $120,000\n", - "Average Employment Income per Person: $30,000\n", - "Adjusted Gross Income: $120,000\n", - "Federal Income Tax: $6,323\n", - "SNAP Benefits: $0\n", - "Child Tax Credit: $4,000\n", - "Earned Income Tax Credit: $0\n", - "Household Net Income: $102,117\n" - ] - } - ], - "source": [ - "print(\"\\n=== BASELINE RESULTS ===\")\n", - "print(f\"Total Employment Income: ${employment_income.sum():,.0f}\")\n", - "print(f\"Average Employment Income per Person: ${employment_income.mean():,.0f}\")\n", - "print(f\"Adjusted Gross Income: ${adjusted_gross_income.sum():,.0f}\")\n", - "print(f\"Federal Income Tax: ${income_tax.sum():,.0f}\")\n", - "print(f\"SNAP Benefits: ${snap.sum():,.0f}\")\n", - "print(f\"Child Tax Credit: ${ctc.sum():,.0f}\")\n", - "print(f\"Earned Income Tax Credit: ${eitc.sum():,.0f}\")\n", - "print(f\"Household Net Income: ${household_net_income.sum():,.0f}\")" - ] - }, - { - "cell_type": "markdown", - "id": "fc2b970c", - "metadata": {}, - "source": [ - "## Part 3: Understanding Variable Calculation Flow\n" - ] - }, - { - "cell_type": "markdown", - "id": "cb18e447", - "metadata": {}, - "source": [ - "xxx - purpose of trace" - ] - }, - { - "cell_type": "code", - "execution_count": 28, - "id": "1ae3551e", - "metadata": {}, - "outputs": [], - "source": [ - "# Initialize a new simulation for tracing\n", - "sim_for_trace = Simulation(situation=situation)" - ] - }, - { - "cell_type": "code", - "execution_count": 29, + "execution_count": 131, "id": "389e33dc", "metadata": {}, "outputs": [ @@ -971,61 +586,6 @@ "sim_for_trace.tracer.print_computation_log(max_depth=3)\n" ] }, - { - "cell_type": "markdown", - "id": "3bc4056f", - "metadata": {}, - "source": [] - }, - { - "cell_type": "code", - "execution_count": 30, - "id": "88b54fa6", - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "\n", - "=== CTC CALCULATION TRACE DEPTH = 4 ===\n", - " ctc_value<2025, (default)> = [4000.]\n", - " ctc<2025, (default)> = [4000.]\n", - " ctc_maximum_with_arpa_addition<2025, (default)> = [4000.]\n", - " ctc_maximum<2025, (default)> = [4000.]\n", - " ctc_arpa_addition<2025, (default)> = [0.]\n", - " ctc_phase_out<2025, (default)> = [0.]\n", - " adjusted_gross_income<2025, (default)> = [120000.]\n", - " ctc_phase_out_threshold<2025, (default)> = [400000.]\n", - " ctc_phase_in<2025, (default)> = [17625.]\n", - " tax_unit_earned_income<2025, (default)> = [120000.]\n", - " earned_income<2025, (default)> = [75000. 45000. 0. 0.]\n", - " is_tax_unit_dependent<2025, (default)> = [False False True True]\n", - " ctc_social_security_tax<2025, (default)> = [9180.]\n", - " employee_social_security_tax<2025, (default)> = [4650. 2790. 0. 0.]\n", - " employee_medicare_tax<2025, (default)> = [1087.5 652.5 0. 0. ]\n", - " unreported_payroll_tax<2025, (default)> = [0.]\n", - " self_employment_tax_ald<2025, (default)> = [0.]\n", - " additional_medicare_tax<2025, (default)> = [0.]\n", - " excess_payroll_tax_withheld<2025, (default)> = [0.]\n", - " eitc<2025, (default)> = [0.]\n", - " eitc_eligible<2025, (default)> = [ True]\n", - " takes_up_eitc<2025, (default)> = [ True]\n", - " eitc_maximum<2025, (default)> = [7152.]\n", - " eitc_phased_in<2025, (default)> = [7152.]\n", - " eitc_reduction<2025, (default)> = [18855.018]\n", - " ctc_qualifying_children<2025, (default)> = [2]\n", - " ctc_qualifying_child<2025, (default)> = [False False True True]\n" - ] - } - ], - "source": [ - "# Let's increase the depth of the trace\n", - "\n", - "print(\"\\n=== CTC CALCULATION TRACE DEPTH = 4 ===\")\n", - "sim_for_trace.tracer.print_computation_log(max_depth=4)\n" - ] - }, { "cell_type": "markdown", "id": "8dde68eb", @@ -1044,7 +604,7 @@ }, { "cell_type": "code", - "execution_count": 31, + "execution_count": 133, "id": "66d1bf86", "metadata": {}, "outputs": [ @@ -1113,7 +673,7 @@ }, { "cell_type": "code", - "execution_count": 32, + "execution_count": 134, "id": "ad497e3f", "metadata": {}, "outputs": [], @@ -1154,7 +714,7 @@ }, { "cell_type": "code", - "execution_count": 33, + "execution_count": 135, "id": "a57152ad", "metadata": {}, "outputs": [], @@ -1168,7 +728,7 @@ }, { "cell_type": "code", - "execution_count": 34, + "execution_count": 136, "id": "f4aaf78c", "metadata": {}, "outputs": [], @@ -1181,7 +741,7 @@ }, { "cell_type": "code", - "execution_count": 35, + "execution_count": 137, "id": "e6398b3b", "metadata": {}, "outputs": [ @@ -1214,7 +774,7 @@ }, { "cell_type": "code", - "execution_count": 36, + "execution_count": 138, "id": "95104770", "metadata": {}, "outputs": [], @@ -1224,7 +784,7 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 139, "id": "6b4b2bb2", "metadata": {}, "outputs": [], @@ -1281,7 +841,7 @@ }, { "cell_type": "code", - "execution_count": 38, + "execution_count": 140, "id": "f8d906d8", "metadata": {}, "outputs": [], @@ -1308,7 +868,7 @@ }, { "cell_type": "code", - "execution_count": 39, + "execution_count": 141, "id": "f0487bce", "metadata": {}, "outputs": [], @@ -1320,7 +880,7 @@ }, { "cell_type": "code", - "execution_count": 40, + "execution_count": 142, "id": "aba01b47", "metadata": {}, "outputs": [ @@ -1367,7 +927,7 @@ }, { "cell_type": "code", - "execution_count": 94, + "execution_count": 143, "id": "cccdc298", "metadata": {}, "outputs": [], @@ -1390,7 +950,7 @@ }, { "cell_type": "code", - "execution_count": 95, + "execution_count": 144, "id": "a611cc57", "metadata": {}, "outputs": [], @@ -1410,7 +970,7 @@ }, { "cell_type": "code", - "execution_count": 96, + "execution_count": 145, "id": "5400bf99", "metadata": {}, "outputs": [], @@ -1430,7 +990,7 @@ }, { "cell_type": "code", - "execution_count": 97, + "execution_count": 146, "id": "fb18dbe3", "metadata": {}, "outputs": [ @@ -1474,7 +1034,7 @@ }, { "cell_type": "code", - "execution_count": 45, + "execution_count": 147, "id": "4896ad23", "metadata": {}, "outputs": [ @@ -1490,11 +1050,11 @@ "2 0.0 120000.0 90000.0 4323.0 \n", "3 0.0 120000.0 90000.0 4323.0 \n", "\n", - " ctc_value eitc snap household_net_income \n", - "0 6000.0 0.0 0.0 104116.851562 \n", - "1 6000.0 0.0 0.0 104116.851562 \n", - "2 6000.0 0.0 0.0 104116.851562 \n", - "3 6000.0 0.0 0.0 104116.851562 \n" + " ctc_value eitc snap state_code household_net_income \n", + "0 6000.0 0.0 0.0 4 104116.851562 \n", + "1 6000.0 0.0 0.0 4 104116.851562 \n", + "2 6000.0 0.0 0.0 4 104116.851562 \n", + "3 6000.0 0.0 0.0 4 104116.851562 \n" ] } ], @@ -1515,7 +1075,7 @@ }, { "cell_type": "code", - "execution_count": 46, + "execution_count": 148, "id": "9663cea6", "metadata": {}, "outputs": [], @@ -1530,7 +1090,7 @@ }, { "cell_type": "code", - "execution_count": 47, + "execution_count": 149, "id": "04506208", "metadata": {}, "outputs": [ @@ -1546,13 +1106,13 @@ "2 0.0 120000.0 90000.0 6323.0 \n", "3 0.0 120000.0 90000.0 6323.0 \n", "\n", - " ctc_value eitc snap household_net_income \n", - "0 4000.0 0.0 0.0 102116.851562 \n", - "1 4000.0 0.0 0.0 102116.851562 \n", - "2 4000.0 0.0 0.0 102116.851562 \n", - "3 4000.0 0.0 0.0 102116.851562 \n", + " ctc_value eitc snap state_code household_net_income \n", + "0 4000.0 0.0 0.0 4 102116.851562 \n", + "1 4000.0 0.0 0.0 4 102116.851562 \n", + "2 4000.0 0.0 0.0 4 102116.851562 \n", + "3 4000.0 0.0 0.0 4 102116.851562 \n", "\n", - "Shape: (4, 8)\n", + "Shape: (4, 9)\n", "Note: All variables are now aggregated to person level (4 rows)\n" ] } @@ -1566,7 +1126,7 @@ }, { "cell_type": "code", - "execution_count": 48, + "execution_count": 150, "id": "386a7fb2", "metadata": {}, "outputs": [], @@ -1581,7 +1141,7 @@ }, { "cell_type": "code", - "execution_count": 49, + "execution_count": 151, "id": "7964e2a7", "metadata": {}, "outputs": [ @@ -1594,10 +1154,10 @@ " employment_income adjusted_gross_income taxable_income income_tax \\\n", "0 120000.0 120000.0 90000.0 6323.0 \n", "\n", - " ctc_value eitc snap household_net_income \n", - "0 4000.0 0.0 0.0 102116.851562 \n", + " ctc_value eitc snap state_code household_net_income \n", + "0 4000.0 0.0 0.0 4 102116.851562 \n", "\n", - "Shape: (1, 8)\n", + "Shape: (1, 9)\n", "Note: All variables are now aggregated to household level (1 row)\n" ] } @@ -1630,7 +1190,7 @@ }, { "cell_type": "code", - "execution_count": 50, + "execution_count": 152, "id": "9c129cef", "metadata": {}, "outputs": [], @@ -1668,7 +1228,7 @@ }, { "cell_type": "code", - "execution_count": 51, + "execution_count": 153, "id": "d084d8b7", "metadata": {}, "outputs": [], @@ -1813,7 +1373,7 @@ }, { "cell_type": "code", - "execution_count": 52, + "execution_count": 154, "id": "a7f22344", "metadata": {}, "outputs": [], @@ -1824,7 +1384,7 @@ }, { "cell_type": "code", - "execution_count": 100, + "execution_count": 155, "id": "5ff9f744", "metadata": {}, "outputs": [], @@ -1839,7 +1399,7 @@ }, { "cell_type": "code", - "execution_count": 101, + "execution_count": 156, "id": "2f8c5308", "metadata": {}, "outputs": [], @@ -1856,7 +1416,7 @@ }, { "cell_type": "code", - "execution_count": 55, + "execution_count": 157, "id": "af9bd728", "metadata": {}, "outputs": [ @@ -1894,7 +1454,7 @@ }, { "cell_type": "code", - "execution_count": 56, + "execution_count": 158, "id": "6834e3d4", "metadata": {}, "outputs": [], @@ -1927,7 +1487,7 @@ }, { "cell_type": "code", - "execution_count": 57, + "execution_count": 159, "id": "cf34eeb5", "metadata": {}, "outputs": [], @@ -1938,7 +1498,7 @@ }, { "cell_type": "code", - "execution_count": 58, + "execution_count": 160, "id": "c69f76eb", "metadata": {}, "outputs": [ From 24bc11372ce24ac39ce02824333bcbb87a00270e Mon Sep 17 00:00:00 2001 From: PavelMakarchuk Date: Thu, 10 Jul 2025 17:01:06 +0200 Subject: [PATCH 4/6] minor --- educational/household_simulation.ipynb | 35 -------------------------- 1 file changed, 35 deletions(-) diff --git a/educational/household_simulation.ipynb b/educational/household_simulation.ipynb index 9c30825..3b68ea1 100644 --- a/educational/household_simulation.ipynb +++ b/educational/household_simulation.ipynb @@ -551,41 +551,6 @@ "sim_for_trace.tracer.print_computation_log(max_depth=4)\n" ] }, - { - "cell_type": "code", - "execution_count": 131, - "id": "389e33dc", - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "\n", - "=== CTC CALCULATION TRACE ===\n", - " ctc_value<2025, (default)> = [4000.]\n", - " ctc<2025, (default)> = [4000.]\n", - " ctc_maximum_with_arpa_addition<2025, (default)> = [4000.]\n", - " ctc_phase_out<2025, (default)> = [0.]\n", - " ctc_phase_in<2025, (default)> = [17625.]\n", - " tax_unit_earned_income<2025, (default)> = [120000.]\n", - " ctc_social_security_tax<2025, (default)> = [9180.]\n", - " eitc<2025, (default)> = [0.]\n", - " ctc_qualifying_children<2025, (default)> = [2]\n" - ] - } - ], - "source": [ - "# Let's trace how the Child Tax Credit is calculated\n", - "# This demonstrates the dependency tree of variables\n", - "\n", - "sim_for_trace.trace = True\n", - "sim_for_trace.calculate(\"ctc_value\", period=PERIOD)\n", - "# Now let's see the trace\n", - "print(\"\\n=== CTC CALCULATION TRACE ===\")\n", - "sim_for_trace.tracer.print_computation_log(max_depth=3)\n" - ] - }, { "cell_type": "markdown", "id": "8dde68eb", From 1c5018401864f43c7b97080a953c2682dd055fcb Mon Sep 17 00:00:00 2001 From: PavelMakarchuk Date: Thu, 17 Jul 2025 13:36:24 +0200 Subject: [PATCH 5/6] flexible situation --- educational/household_simulation.ipynb | 130 ++++++++++++------------- 1 file changed, 61 insertions(+), 69 deletions(-) diff --git a/educational/household_simulation.ipynb b/educational/household_simulation.ipynb index 3b68ea1..b1bc701 100644 --- a/educational/household_simulation.ipynb +++ b/educational/household_simulation.ipynb @@ -35,10 +35,19 @@ }, { "cell_type": "code", - "execution_count": 102, + "execution_count": 1, "id": "a46899e7", "metadata": {}, - "outputs": [], + "outputs": [ + { + "name": "stderr", + "output_type": "stream", + "text": [ + "/Users/pavelmakarchuk/anaconda3/envs/pe/lib/python3.10/site-packages/tqdm/auto.py:21: TqdmWarning: IProgress not found. Please update jupyter and ipywidgets. See https://ipywidgets.readthedocs.io/en/stable/user_install.html\n", + " from .autonotebook import tqdm as notebook_tqdm\n" + ] + } + ], "source": [ "# Import necessary libraries\n", "from policyengine_us import Simulation\n", @@ -97,7 +106,7 @@ }, { "cell_type": "code", - "execution_count": 103, + "execution_count": 2, "id": "725bb985", "metadata": {}, "outputs": [], @@ -169,7 +178,7 @@ }, { "cell_type": "code", - "execution_count": 104, + "execution_count": 3, "id": "3257517a", "metadata": {}, "outputs": [], @@ -180,7 +189,7 @@ }, { "cell_type": "code", - "execution_count": 105, + "execution_count": 4, "id": "dd0c97dd", "metadata": {}, "outputs": [], @@ -191,7 +200,7 @@ }, { "cell_type": "code", - "execution_count": 106, + "execution_count": 5, "id": "bd5ebe5c", "metadata": {}, "outputs": [], @@ -203,7 +212,7 @@ }, { "cell_type": "code", - "execution_count": 107, + "execution_count": 6, "id": "c049a19b", "metadata": {}, "outputs": [], @@ -216,7 +225,7 @@ }, { "cell_type": "code", - "execution_count": 108, + "execution_count": 7, "id": "d89b98d0", "metadata": {}, "outputs": [], @@ -227,7 +236,7 @@ }, { "cell_type": "code", - "execution_count": 109, + "execution_count": 8, "id": "de0db305", "metadata": {}, "outputs": [], @@ -249,7 +258,7 @@ }, { "cell_type": "code", - "execution_count": 110, + "execution_count": 9, "id": "47f49cc7", "metadata": {}, "outputs": [ @@ -303,7 +312,7 @@ }, { "cell_type": "code", - "execution_count": 111, + "execution_count": 10, "id": "059949c5", "metadata": {}, "outputs": [ @@ -331,7 +340,7 @@ }, { "cell_type": "code", - "execution_count": 112, + "execution_count": 11, "id": "848a47c6", "metadata": {}, "outputs": [ @@ -355,7 +364,7 @@ }, { "cell_type": "code", - "execution_count": 113, + "execution_count": 12, "id": "a410a154", "metadata": {}, "outputs": [], @@ -378,7 +387,7 @@ }, { "cell_type": "code", - "execution_count": 114, + "execution_count": 13, "id": "9e222d38", "metadata": {}, "outputs": [ @@ -424,12 +433,13 @@ "id": "1e357053", "metadata": {}, "source": [ - "xxx - purpose of trace" + "### Why Use Trace?\n", + "The `trace` feature shows you how PolicyEngine calculates variables step-by-step, revealing the complete dependency tree (e.g., how `ctc_value` depends on `ctc_phase_in`, which depends on `tax_unit_earned_income` etc.). This is essential for debugging results, understanding policy implementation, and ensuring transparency in your analysis. The trace essentially gives you \"X-ray vision\" into PolicyEngine's calculations, showing not just the final results but the complete logical path to get there." ] }, { "cell_type": "code", - "execution_count": 115, + "execution_count": 14, "id": "e63152d0", "metadata": {}, "outputs": [], @@ -440,7 +450,7 @@ }, { "cell_type": "code", - "execution_count": 116, + "execution_count": 15, "id": "0c02d2b7", "metadata": {}, "outputs": [ @@ -504,7 +514,7 @@ }, { "cell_type": "code", - "execution_count": 117, + "execution_count": 16, "id": "5393ac49", "metadata": {}, "outputs": [ @@ -569,7 +579,7 @@ }, { "cell_type": "code", - "execution_count": 133, + "execution_count": 17, "id": "66d1bf86", "metadata": {}, "outputs": [ @@ -638,7 +648,7 @@ }, { "cell_type": "code", - "execution_count": 134, + "execution_count": 18, "id": "ad497e3f", "metadata": {}, "outputs": [], @@ -679,7 +689,7 @@ }, { "cell_type": "code", - "execution_count": 135, + "execution_count": 19, "id": "a57152ad", "metadata": {}, "outputs": [], @@ -693,7 +703,7 @@ }, { "cell_type": "code", - "execution_count": 136, + "execution_count": 20, "id": "f4aaf78c", "metadata": {}, "outputs": [], @@ -706,7 +716,7 @@ }, { "cell_type": "code", - "execution_count": 137, + "execution_count": 21, "id": "e6398b3b", "metadata": {}, "outputs": [ @@ -739,7 +749,7 @@ }, { "cell_type": "code", - "execution_count": 138, + "execution_count": 22, "id": "95104770", "metadata": {}, "outputs": [], @@ -749,7 +759,7 @@ }, { "cell_type": "code", - "execution_count": 139, + "execution_count": 23, "id": "6b4b2bb2", "metadata": {}, "outputs": [], @@ -778,24 +788,6 @@ "# This would be applied as: Simulation(situation=situation, reform=reform())" ] }, - { - "cell_type": "markdown", - "id": "5fc17dc0", - "metadata": {}, - "source": [ - "**Best Practice**: While structural reforms are powerful, it's often better to:\n", - "1. File an issue in the [policyengine-us repository](https://github.com/PolicyEngine/policyengine-us/issues)\n", - "2. Discuss with PolicyEngine developers\n", - "3. Submit a pull request to add new parameters\n", - "4. Convert your structural reform into a parametric one\n", - "\n", - "This approach ensures:\n", - "- Your reform integrates well with the existing codebase\n", - "- Other users can benefit from the new functionality\n", - "- The reform is properly tested and documented\n", - "- Future updates won't break your analysis" - ] - }, { "cell_type": "markdown", "id": "130ce1b3", @@ -806,7 +798,7 @@ }, { "cell_type": "code", - "execution_count": 140, + "execution_count": 24, "id": "f8d906d8", "metadata": {}, "outputs": [], @@ -833,7 +825,7 @@ }, { "cell_type": "code", - "execution_count": 141, + "execution_count": 25, "id": "f0487bce", "metadata": {}, "outputs": [], @@ -845,7 +837,7 @@ }, { "cell_type": "code", - "execution_count": 142, + "execution_count": 26, "id": "aba01b47", "metadata": {}, "outputs": [ @@ -892,7 +884,7 @@ }, { "cell_type": "code", - "execution_count": 143, + "execution_count": 27, "id": "cccdc298", "metadata": {}, "outputs": [], @@ -915,7 +907,7 @@ }, { "cell_type": "code", - "execution_count": 144, + "execution_count": 28, "id": "a611cc57", "metadata": {}, "outputs": [], @@ -935,7 +927,7 @@ }, { "cell_type": "code", - "execution_count": 145, + "execution_count": 29, "id": "5400bf99", "metadata": {}, "outputs": [], @@ -955,7 +947,7 @@ }, { "cell_type": "code", - "execution_count": 146, + "execution_count": 30, "id": "fb18dbe3", "metadata": {}, "outputs": [ @@ -999,7 +991,7 @@ }, { "cell_type": "code", - "execution_count": 147, + "execution_count": 31, "id": "4896ad23", "metadata": {}, "outputs": [ @@ -1040,7 +1032,7 @@ }, { "cell_type": "code", - "execution_count": 148, + "execution_count": 32, "id": "9663cea6", "metadata": {}, "outputs": [], @@ -1055,7 +1047,7 @@ }, { "cell_type": "code", - "execution_count": 149, + "execution_count": 33, "id": "04506208", "metadata": {}, "outputs": [ @@ -1091,7 +1083,7 @@ }, { "cell_type": "code", - "execution_count": 150, + "execution_count": 34, "id": "386a7fb2", "metadata": {}, "outputs": [], @@ -1106,7 +1098,7 @@ }, { "cell_type": "code", - "execution_count": 151, + "execution_count": 35, "id": "7964e2a7", "metadata": {}, "outputs": [ @@ -1155,7 +1147,7 @@ }, { "cell_type": "code", - "execution_count": 152, + "execution_count": 36, "id": "9c129cef", "metadata": {}, "outputs": [], @@ -1193,7 +1185,7 @@ }, { "cell_type": "code", - "execution_count": 153, + "execution_count": 37, "id": "d084d8b7", "metadata": {}, "outputs": [], @@ -1338,7 +1330,7 @@ }, { "cell_type": "code", - "execution_count": 154, + "execution_count": 38, "id": "a7f22344", "metadata": {}, "outputs": [], @@ -1349,7 +1341,7 @@ }, { "cell_type": "code", - "execution_count": 155, + "execution_count": 39, "id": "5ff9f744", "metadata": {}, "outputs": [], @@ -1364,7 +1356,7 @@ }, { "cell_type": "code", - "execution_count": 156, + "execution_count": 40, "id": "2f8c5308", "metadata": {}, "outputs": [], @@ -1381,7 +1373,7 @@ }, { "cell_type": "code", - "execution_count": 157, + "execution_count": 41, "id": "af9bd728", "metadata": {}, "outputs": [ @@ -1392,11 +1384,11 @@ "\n", "=== INCOME VARIATION RESULTS (Sample) ===\n", " Employment Income Net Income CTC EITC SNAP\n", - "0 0.000000 26000.064453 0.0 0.000000 7710.931641\n", - "1 524.860229 26749.869141 0.0 209.944092 7587.632324\n", - "2 1049.720459 27496.976562 0.0 419.888184 7461.632324\n", - "3 1574.580688 28244.080078 0.0 629.832275 7335.632324\n", - "4 2099.440918 28991.185547 0.0 839.776367 7209.632324\n" + "0 0.000000 26588.064453 0.0 0.000000 7458.931641\n", + "1 524.860229 27337.869141 0.0 209.944092 7335.632324\n", + "2 1049.720459 28084.976562 0.0 419.888184 7209.632324\n", + "3 1574.580688 28832.080078 0.0 629.832275 7083.632324\n", + "4 2099.440918 29579.185547 0.0 839.776367 6957.632324\n" ] } ], @@ -1419,7 +1411,7 @@ }, { "cell_type": "code", - "execution_count": 158, + "execution_count": 42, "id": "6834e3d4", "metadata": {}, "outputs": [], @@ -1452,7 +1444,7 @@ }, { "cell_type": "code", - "execution_count": 159, + "execution_count": 43, "id": "cf34eeb5", "metadata": {}, "outputs": [], @@ -1463,7 +1455,7 @@ }, { "cell_type": "code", - "execution_count": 160, + "execution_count": 44, "id": "c69f76eb", "metadata": {}, "outputs": [ From ad208b3af24e10a56b46c4df95352a4f56c87473 Mon Sep 17 00:00:00 2001 From: PavelMakarchuk Date: Thu, 17 Jul 2025 15:18:45 +0200 Subject: [PATCH 6/6] micorsim mvp - more to add --- educational/household_simulation.ipynb | 197 ++- educational/microsimulation.ipynb | 2069 ++++++++++++++++++++++++ 2 files changed, 2210 insertions(+), 56 deletions(-) create mode 100644 educational/microsimulation.ipynb diff --git a/educational/household_simulation.ipynb b/educational/household_simulation.ipynb index b1bc701..0e45332 100644 --- a/educational/household_simulation.ipynb +++ b/educational/household_simulation.ipynb @@ -5,7 +5,7 @@ "id": "4bfc90c5", "metadata": {}, "source": [ - "# Notebook 1: Household Simulation with PolicyEngine\n" + "# Notebook 1: Household Simulation with PolicyEngine" ] }, { @@ -35,19 +35,10 @@ }, { "cell_type": "code", - "execution_count": 1, + "execution_count": 59, "id": "a46899e7", "metadata": {}, - "outputs": [ - { - "name": "stderr", - "output_type": "stream", - "text": [ - "/Users/pavelmakarchuk/anaconda3/envs/pe/lib/python3.10/site-packages/tqdm/auto.py:21: TqdmWarning: IProgress not found. Please update jupyter and ipywidgets. See https://ipywidgets.readthedocs.io/en/stable/user_install.html\n", - " from .autonotebook import tqdm as notebook_tqdm\n" - ] - } - ], + "outputs": [], "source": [ "# Import necessary libraries\n", "from policyengine_us import Simulation\n", @@ -106,7 +97,7 @@ }, { "cell_type": "code", - "execution_count": 2, + "execution_count": 60, "id": "725bb985", "metadata": {}, "outputs": [], @@ -178,7 +169,7 @@ }, { "cell_type": "code", - "execution_count": 3, + "execution_count": 61, "id": "3257517a", "metadata": {}, "outputs": [], @@ -189,7 +180,7 @@ }, { "cell_type": "code", - "execution_count": 4, + "execution_count": 62, "id": "dd0c97dd", "metadata": {}, "outputs": [], @@ -200,7 +191,7 @@ }, { "cell_type": "code", - "execution_count": 5, + "execution_count": 63, "id": "bd5ebe5c", "metadata": {}, "outputs": [], @@ -212,7 +203,7 @@ }, { "cell_type": "code", - "execution_count": 6, + "execution_count": 64, "id": "c049a19b", "metadata": {}, "outputs": [], @@ -225,7 +216,7 @@ }, { "cell_type": "code", - "execution_count": 7, + "execution_count": 65, "id": "d89b98d0", "metadata": {}, "outputs": [], @@ -236,7 +227,7 @@ }, { "cell_type": "code", - "execution_count": 8, + "execution_count": 66, "id": "de0db305", "metadata": {}, "outputs": [], @@ -258,7 +249,7 @@ }, { "cell_type": "code", - "execution_count": 9, + "execution_count": 67, "id": "47f49cc7", "metadata": {}, "outputs": [ @@ -312,7 +303,7 @@ }, { "cell_type": "code", - "execution_count": 10, + "execution_count": 68, "id": "059949c5", "metadata": {}, "outputs": [ @@ -340,7 +331,7 @@ }, { "cell_type": "code", - "execution_count": 11, + "execution_count": 69, "id": "848a47c6", "metadata": {}, "outputs": [ @@ -364,7 +355,7 @@ }, { "cell_type": "code", - "execution_count": 12, + "execution_count": 70, "id": "a410a154", "metadata": {}, "outputs": [], @@ -387,7 +378,7 @@ }, { "cell_type": "code", - "execution_count": 13, + "execution_count": 71, "id": "9e222d38", "metadata": {}, "outputs": [ @@ -434,12 +425,12 @@ "metadata": {}, "source": [ "### Why Use Trace?\n", - "The `trace` feature shows you how PolicyEngine calculates variables step-by-step, revealing the complete dependency tree (e.g., how `ctc_value` depends on `ctc_phase_in`, which depends on `tax_unit_earned_income` etc.). This is essential for debugging results, understanding policy implementation, and ensuring transparency in your analysis. The trace essentially gives you \"X-ray vision\" into PolicyEngine's calculations, showing not just the final results but the complete logical path to get there." + "The `trace` feature shows you how PolicyEngine calculates variables step-by-step, revealing the complete dependency tree (e.g., how `ctc_value` depends on `ctc_phase_in`, which depends on `tax_unit_earned_income` etc.). This is essential for debugging results and understanding policy implementation. The trace essentially gives you \"X-ray vision\" into PolicyEngine's calculations, showing not just the final results but the complete path to get there." ] }, { "cell_type": "code", - "execution_count": 14, + "execution_count": 72, "id": "e63152d0", "metadata": {}, "outputs": [], @@ -450,7 +441,7 @@ }, { "cell_type": "code", - "execution_count": 15, + "execution_count": 73, "id": "0c02d2b7", "metadata": {}, "outputs": [ @@ -514,7 +505,7 @@ }, { "cell_type": "code", - "execution_count": 16, + "execution_count": 74, "id": "5393ac49", "metadata": {}, "outputs": [ @@ -579,7 +570,7 @@ }, { "cell_type": "code", - "execution_count": 17, + "execution_count": 75, "id": "66d1bf86", "metadata": {}, "outputs": [ @@ -648,7 +639,7 @@ }, { "cell_type": "code", - "execution_count": 18, + "execution_count": 76, "id": "ad497e3f", "metadata": {}, "outputs": [], @@ -689,7 +680,7 @@ }, { "cell_type": "code", - "execution_count": 19, + "execution_count": 77, "id": "a57152ad", "metadata": {}, "outputs": [], @@ -703,7 +694,7 @@ }, { "cell_type": "code", - "execution_count": 20, + "execution_count": 78, "id": "f4aaf78c", "metadata": {}, "outputs": [], @@ -716,7 +707,7 @@ }, { "cell_type": "code", - "execution_count": 21, + "execution_count": 79, "id": "e6398b3b", "metadata": {}, "outputs": [ @@ -749,7 +740,7 @@ }, { "cell_type": "code", - "execution_count": 22, + "execution_count": 80, "id": "95104770", "metadata": {}, "outputs": [], @@ -759,7 +750,7 @@ }, { "cell_type": "code", - "execution_count": 23, + "execution_count": 81, "id": "6b4b2bb2", "metadata": {}, "outputs": [], @@ -798,7 +789,7 @@ }, { "cell_type": "code", - "execution_count": 24, + "execution_count": 82, "id": "f8d906d8", "metadata": {}, "outputs": [], @@ -825,7 +816,7 @@ }, { "cell_type": "code", - "execution_count": 25, + "execution_count": 83, "id": "f0487bce", "metadata": {}, "outputs": [], @@ -837,7 +828,7 @@ }, { "cell_type": "code", - "execution_count": 26, + "execution_count": 84, "id": "aba01b47", "metadata": {}, "outputs": [ @@ -884,7 +875,7 @@ }, { "cell_type": "code", - "execution_count": 27, + "execution_count": 85, "id": "cccdc298", "metadata": {}, "outputs": [], @@ -907,7 +898,7 @@ }, { "cell_type": "code", - "execution_count": 28, + "execution_count": 86, "id": "a611cc57", "metadata": {}, "outputs": [], @@ -927,7 +918,7 @@ }, { "cell_type": "code", - "execution_count": 29, + "execution_count": 87, "id": "5400bf99", "metadata": {}, "outputs": [], @@ -947,7 +938,7 @@ }, { "cell_type": "code", - "execution_count": 30, + "execution_count": 88, "id": "fb18dbe3", "metadata": {}, "outputs": [ @@ -991,7 +982,7 @@ }, { "cell_type": "code", - "execution_count": 31, + "execution_count": 89, "id": "4896ad23", "metadata": {}, "outputs": [ @@ -1032,7 +1023,7 @@ }, { "cell_type": "code", - "execution_count": 32, + "execution_count": 90, "id": "9663cea6", "metadata": {}, "outputs": [], @@ -1047,7 +1038,7 @@ }, { "cell_type": "code", - "execution_count": 33, + "execution_count": 91, "id": "04506208", "metadata": {}, "outputs": [ @@ -1083,7 +1074,7 @@ }, { "cell_type": "code", - "execution_count": 34, + "execution_count": 92, "id": "386a7fb2", "metadata": {}, "outputs": [], @@ -1098,7 +1089,7 @@ }, { "cell_type": "code", - "execution_count": 35, + "execution_count": 93, "id": "7964e2a7", "metadata": {}, "outputs": [ @@ -1147,7 +1138,7 @@ }, { "cell_type": "code", - "execution_count": 36, + "execution_count": 94, "id": "9c129cef", "metadata": {}, "outputs": [], @@ -1185,7 +1176,7 @@ }, { "cell_type": "code", - "execution_count": 37, + "execution_count": 95, "id": "d084d8b7", "metadata": {}, "outputs": [], @@ -1330,7 +1321,7 @@ }, { "cell_type": "code", - "execution_count": 38, + "execution_count": 96, "id": "a7f22344", "metadata": {}, "outputs": [], @@ -1341,7 +1332,7 @@ }, { "cell_type": "code", - "execution_count": 39, + "execution_count": 97, "id": "5ff9f744", "metadata": {}, "outputs": [], @@ -1356,7 +1347,7 @@ }, { "cell_type": "code", - "execution_count": 40, + "execution_count": 98, "id": "2f8c5308", "metadata": {}, "outputs": [], @@ -1373,7 +1364,7 @@ }, { "cell_type": "code", - "execution_count": 41, + "execution_count": 99, "id": "af9bd728", "metadata": {}, "outputs": [ @@ -1411,7 +1402,7 @@ }, { "cell_type": "code", - "execution_count": 42, + "execution_count": 100, "id": "6834e3d4", "metadata": {}, "outputs": [], @@ -1434,6 +1425,100 @@ "]" ] }, + { + "cell_type": "code", + "execution_count": 101, + "id": "8080b520", + "metadata": {}, + "outputs": [], + "source": [ + "import copy" + ] + }, + { + "cell_type": "markdown", + "id": "f9e3f968", + "metadata": {}, + "source": [ + "### Note: Flexibility of the Situation\n", + "The situation dictionary is highly flexible and can be programmatically modified to create different household configurations. This example shows how to dynamically add children, but the same principles apply to modifying any aspect of the situation - you can add or remove people, change entity memberships, modify variables, or create entirely different household structures using loops and conditional logic." + ] + }, + { + "cell_type": "code", + "execution_count": 102, + "id": "bfb7a5c0", + "metadata": {}, + "outputs": [], + "source": [ + "# Example: Dynamically add children to a household\n", + "def create_household_with_children(num_children):\n", + " \n", + " # Start with base situation (parents only)\n", + " situation = {\n", + " \"people\": {\n", + " \"parent1\": {\"age\": {PERIOD: 35}, \"employment_income\": {PERIOD: 75000}},\n", + " \"parent2\": {\"age\": {PERIOD: 33}, \"employment_income\": {PERIOD: 45000}}\n", + " },\n", + " \"families\": {\"family\": {\"members\": [\"parent1\", \"parent2\"]}},\n", + " \"marital_units\": {\"parents\": {\"members\": [\"parent1\", \"parent2\"]}},\n", + " \"tax_units\": {\"tax_unit\": {\"members\": [\"parent1\", \"parent2\"]}},\n", + " \"spm_units\": {\"spm_unit\": {\"members\": [\"parent1\", \"parent2\"]}},\n", + " \"households\": {\n", + " \"household\": {\n", + " \"members\": [\"parent1\", \"parent2\"],\n", + " \"state_code\": {PERIOD: \"CA\"}\n", + " }\n", + " }\n", + " }\n", + " \n", + " # Add children dynamically\n", + " for i in range(1, num_children + 1):\n", + " child_id = f\"child{i}\"\n", + " situation[\"people\"][child_id] = {\n", + " \"age\": {PERIOD: 10 - i}, # Ages 9, 8, 7, etc.\n", + " }\n", + " \n", + " # Add child to all entity groups\n", + " situation[\"families\"][\"family\"][\"members\"].append(child_id)\n", + " situation[\"tax_units\"][\"tax_unit\"][\"members\"].append(child_id)\n", + " situation[\"spm_units\"][\"spm_unit\"][\"members\"].append(child_id)\n", + " situation[\"households\"][\"household\"][\"members\"].append(child_id)\n", + " \n", + " # Create marital unit for child\n", + " situation[\"marital_units\"][f\"{child_id}_marital_unit\"] = {\n", + " \"members\": [child_id],\n", + " \"marital_unit_id\": {PERIOD: i}\n", + " }\n", + " \n", + " return situation" + ] + }, + { + "cell_type": "code", + "execution_count": 103, + "id": "ed461713", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Household with 1 children: CTC = $2,000\n", + "Household with 3 children: CTC = $6,000\n", + "Household with 5 children: CTC = $10,000\n" + ] + } + ], + "source": [ + "# Test with different numbers of children\n", + "for num_children in [1, 3, 5]:\n", + " household = create_household_with_children(num_children)\n", + " sim = Simulation(situation=household)\n", + " ctc = sim.calculate(\"ctc_value\", PERIOD)\n", + " print(f\"Household with {num_children} children: CTC = ${ctc.sum():,.0f}\")" + ] + }, { "cell_type": "markdown", "id": "6dd76b3d", @@ -1444,7 +1529,7 @@ }, { "cell_type": "code", - "execution_count": 43, + "execution_count": 104, "id": "cf34eeb5", "metadata": {}, "outputs": [], @@ -1455,7 +1540,7 @@ }, { "cell_type": "code", - "execution_count": 44, + "execution_count": 105, "id": "c69f76eb", "metadata": {}, "outputs": [ diff --git a/educational/microsimulation.ipynb b/educational/microsimulation.ipynb new file mode 100644 index 0000000..24231a7 --- /dev/null +++ b/educational/microsimulation.ipynb @@ -0,0 +1,2069 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "id": "78783fd8", + "metadata": {}, + "source": [ + "# PolicyEngine Economy-Wide Simulation" + ] + }, + { + "cell_type": "markdown", + "id": "715d0660", + "metadata": {}, + "source": [ + "This notebook demonstrates how to perform economy-wide policy analysis using PolicyEngine's microsimulation capabilities. We'll explore weighted vs unweighted data, multi-year impacts, geographic breakdowns, and program enrollment analysis." + ] + }, + { + "cell_type": "markdown", + "id": "aea5d183", + "metadata": {}, + "source": [ + "## Learning Objectives\n", + "By the end of this notebook, you'll understand:\n", + "- The difference between Simulation (household) and Microsimulation (economy)\n", + "- How weighting works in PolicyEngine DataFrames\n", + "- Calculating 10-year budgetary impacts\n", + "- Geographic analysis by state\n", + "- Program enrollment analysis\n", + "- Comparing baseline vs reform scenarios" + ] + }, + { + "cell_type": "markdown", + "id": "4bfdb8b4", + "metadata": {}, + "source": [ + "## 1. Setup and Data Selection" + ] + }, + { + "cell_type": "code", + "execution_count": 38, + "id": "3d7d7a16", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "=== Available PolicyEngine Datasets ===\n", + "Enhanced CPS (Recommended): enhanced_cps_2024.h5\n", + " - Enhanced with IRS Public Use File data\n", + " - Best for national economy-wide analysis\n", + " - ~42k household records\n", + "\n", + "Pooled Datasets: pooled_*.h5\n", + " - Multiple years combined\n", + " - Better state-level sample sizes\n", + " - Ideal for geographic analysis\n", + "\n", + "View all datasets: https://huggingface.co/policyengine/policyengine-us-data/tree/main\n" + ] + } + ], + "source": [ + "# Core PolicyEngine imports\n", + "from policyengine_us import Microsimulation\n", + "from policyengine_core.reforms import Reform\n", + "import pandas as pd\n", + "import numpy as np\n", + "import matplotlib.pyplot as plt\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 for better DataFrame viewing\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", + "# PolicyEngine offers multiple datasets for different analysis needs\n", + "# Recommended: Enhanced CPS for economy-wide simulations\n", + "ENHANCED_CPS = \"hf://policyengine/policyengine-us-data/enhanced_cps_2024.h5\"\n", + "\n", + "print(\"=== Available PolicyEngine Datasets ===\")\n", + "print(\"Enhanced CPS (Recommended): enhanced_cps_2024.h5\")\n", + "print(\" - Enhanced with IRS Public Use File data\")\n", + "print(\" - Best for national economy-wide analysis\")\n", + "print(\" - ~42k household records\")\n", + "print(\"\")\n", + "print(\"Pooled Datasets: pooled_*.h5\") \n", + "print(\" - Multiple years combined\")\n", + "print(\" - Better state-level sample sizes\")\n", + "print(\" - Ideal for geographic analysis\")\n", + "print(\"\")\n", + "print(\"View all datasets: https://huggingface.co/policyengine/policyengine-us-data/tree/main\")" + ] + }, + { + "cell_type": "markdown", + "id": "47fa4905", + "metadata": {}, + "source": [ + "## 2. Understanding Dataset Selection\n", + "\n", + "\n", + "PolicyEngine offers multiple datasets optimized for different types of analysis. For economy-wide simulations, we recommend using the Enhanced CPS dataset." + ] + }, + { + "cell_type": "code", + "execution_count": 39, + "id": "629f0ebc", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Creating baseline microsimulation...\n", + "=== Microsimulation Overview ===\n", + "Using dataset: Enhanced CPS 2024\n", + "Analysis period: 2025\n", + "Number of household records: 41,310\n", + "\n", + "=== Single Variable Calculations ===\n", + "Calculating individual variables...\n", + "CTC variable type: \n", + "CTC array shape: (56768,)\n", + "First 5 CTC values: value weight\n", + "0 0.0 14044.562500\n", + "1 0.0 1151.017212\n", + "2 0.0 1151.017212\n", + "3 0.0 11405.556641\n", + "4 0.0 11405.556641\n", + "First 5 SNAP values: value weight\n", + "0 0.000000 14044.562500\n", + "1 0.000000 1151.017212\n", + "2 281.995483 11405.556641\n", + "3 3524.943604 11405.556641\n", + "4 281.995483 3046.133301\n", + "First 5 net income values: value weight\n", + "0 103463.171875 14044.562500\n", + "1 87697.304688 1151.017212\n", + "2 111439.476562 11405.556641\n", + "3 92810.648438 3046.133301\n", + "4 52004.863281 11906.229492\n" + ] + } + ], + "source": [ + "# Create baseline microsimulation using Enhanced CPS\n", + "print(\"Creating baseline microsimulation...\")\n", + "baseline = Microsimulation(dataset=ENHANCED_CPS)\n", + "\n", + "print(f\"=== Microsimulation Overview ===\")\n", + "print(f\"Using dataset: Enhanced CPS 2024\")\n", + "print(f\"Analysis period: 2025\")\n", + "\n", + "# The enhanced CPS contains ~42k household records\n", + "# Each represents multiple real households via weights\n", + "sample_weights = baseline.calculate(\"household_weight\", period=2025)\n", + "print(f\"Number of household records: {len(sample_weights):,}\")\n", + "\n", + "print(\"\\n=== Single Variable Calculations ===\")\n", + "print(\"Calculating individual variables...\")\n", + "\n", + "# Calculate key variables one by one using .calculate()\n", + "# Child Tax Credit\n", + "ctc_values = baseline.calculate(\"ctc_value\", period=2025)\n", + "print(f\"CTC variable type: {type(ctc_values)}\")\n", + "print(f\"CTC array shape: {ctc_values.shape}\")\n", + "print(f\"First 5 CTC values: {ctc_values[:5]}\")\n", + "\n", + "# SNAP benefits\n", + "snap_values = baseline.calculate(\"snap\", period=2025)\n", + "print(f\"First 5 SNAP values: {snap_values[:5]}\")\n", + "\n", + "# Household net income\n", + "net_income = baseline.calculate(\"household_net_income\", period=2025)\n", + "print(f\"First 5 net income values: {net_income[:5]}\")" + ] + }, + { + "cell_type": "markdown", + "id": "19e9d2b5", + "metadata": {}, + "source": [ + "## 3" + ] + }, + { + "cell_type": "code", + "execution_count": 40, + "id": "955c7cc5", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "=== Understanding Automatic Weighting ===\n", + "BASELINE TOTALS (Auto-weighted):\n", + " Total CTC: $115.7 billion\n", + " Total SNAP: $83.4 billion\n", + " Total Net Income: $15813.4 billion\n", + "\n", + "PROGRAM PARTICIPATION:\n", + " Households receiving CTC: 44,203,382\n", + " Households receiving SNAP: 23,539,852\n", + "\n", + "AVERAGE BENEFITS (among recipients):\n", + " Average CTC: $2,617\n", + " Average SNAP: $3,545\n" + ] + } + ], + "source": [ + "print(\"=== Understanding Automatic Weighting ===\")\n", + "\n", + "# The .calculate() method automatically applies weights when you sum\n", + "total_ctc = baseline.calculate(\"ctc_value\", period=2025).sum()\n", + "total_snap = baseline.calculate(\"snap\", period=2025).sum()\n", + "total_net_income = baseline.calculate(\"household_net_income\", period=2025).sum()\n", + "\n", + "print(f\"BASELINE TOTALS (Auto-weighted):\")\n", + "print(f\" Total CTC: ${total_ctc/1e9:.1f} billion\")\n", + "print(f\" Total SNAP: ${total_snap/1e9:.1f} billion\")\n", + "print(f\" Total Net Income: ${total_net_income/1e9:.1f} billion\")\n", + "\n", + "# Show how many households benefit from each program\n", + "ctc_recipients = (baseline.calculate(\"ctc_value\", period=2025) > 0).sum()\n", + "snap_recipients = (baseline.calculate(\"snap\", period=2025) > 0).sum()\n", + "\n", + "print(f\"\\nPROGRAM PARTICIPATION:\")\n", + "print(f\" Households receiving CTC: {ctc_recipients:,.0f}\")\n", + "print(f\" Households receiving SNAP: {snap_recipients:,.0f}\")\n", + "\n", + "# Calculate average benefits among recipients\n", + "avg_ctc_recipients = total_ctc / ctc_recipients\n", + "avg_snap_recipients = total_snap / snap_recipients\n", + "\n", + "print(f\"\\nAVERAGE BENEFITS (among recipients):\")\n", + "print(f\" Average CTC: ${avg_ctc_recipients:,.0f}\")\n", + "print(f\" Average SNAP: ${avg_snap_recipients:,.0f}\")" + ] + }, + { + "cell_type": "markdown", + "id": "f01fa301", + "metadata": {}, + "source": [ + "## 4" + ] + }, + { + "cell_type": "code", + "execution_count": 41, + "id": "5dcd21b6", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "=== Creating CTC Expansion Reform ===\n", + "Policy Reform Details:\n", + " Current CTC: $2,000 per child\n", + " Reformed CTC: $3,600 per child\n", + " Increase: $1,600 per child\n", + "\n", + "Creating reformed microsimulation...\n", + "=== Baseline vs Reform Comparison ===\n", + "AGGREGATE IMPACT:\n", + " Baseline total CTC: $115.7 billion\n", + " Reformed total CTC: $185.5 billion\n", + " Annual increase: $69.8 billion\n", + "\n", + "HOUSEHOLD IMPACT:\n", + " Total net income increase: $62.0 billion\n", + " This represents the total additional purchasing power\n", + "\n", + "PARTICIPATION CHANGES:\n", + " Baseline CTC recipients: 44,203,382\n", + " Reformed CTC recipients: 44,387,859\n", + " Change in recipients: +184,477\n" + ] + } + ], + "source": [ + "print(\"=== Creating CTC Expansion Reform ===\")\n", + "\n", + "# Create CTC expansion reform\n", + "ctc_expansion = Reform.from_dict({\n", + " \"gov.irs.credits.ctc.amount.base[0].amount\": {\n", + " \"2025-01-01.2100-12-31\": 3600 # Increase from $2,000 to $3,600\n", + " }\n", + "}, country_id=\"us\")\n", + "\n", + "print(\"Policy Reform Details:\")\n", + "print(\" Current CTC: $2,000 per child\")\n", + "print(\" Reformed CTC: $3,600 per child\")\n", + "print(\" Increase: $1,600 per child\")\n", + "\n", + "# Create reformed microsimulation\n", + "print(\"\\nCreating reformed microsimulation...\")\n", + "reformed = Microsimulation(reform=ctc_expansion, dataset=ENHANCED_CPS)\n", + "\n", + "print(\"=== Baseline vs Reform Comparison ===\")\n", + "\n", + "# Calculate baseline and reformed totals using .calculate()\n", + "baseline_total_ctc = baseline.calculate(\"ctc_value\", period=2025).sum()\n", + "reformed_total_ctc = reformed.calculate(\"ctc_value\", period=2025).sum()\n", + "ctc_increase = reformed_total_ctc - baseline_total_ctc\n", + "\n", + "print(f\"AGGREGATE IMPACT:\")\n", + "print(f\" Baseline total CTC: ${baseline_total_ctc/1e9:.1f} billion\")\n", + "print(f\" Reformed total CTC: ${reformed_total_ctc/1e9:.1f} billion\") \n", + "print(f\" Annual increase: ${ctc_increase/1e9:.1f} billion\")\n", + "\n", + "# Calculate net income impact\n", + "baseline_net_income = baseline.calculate(\"household_net_income\", period=2025).sum()\n", + "reformed_net_income = reformed.calculate(\"household_net_income\", period=2025).sum()\n", + "net_income_increase = reformed_net_income - baseline_net_income\n", + "\n", + "print(f\"\\nHOUSEHOLD IMPACT:\")\n", + "print(f\" Total net income increase: ${net_income_increase/1e9:.1f} billion\")\n", + "print(f\" This represents the total additional purchasing power\")\n", + "\n", + "# Show impact on program participation\n", + "baseline_ctc_recipients = (baseline.calculate(\"ctc_value\", period=2025) > 0).sum()\n", + "reformed_ctc_recipients = (reformed.calculate(\"ctc_value\", period=2025) > 0).sum()\n", + "recipient_change = reformed_ctc_recipients - baseline_ctc_recipients\n", + "\n", + "print(f\"\\nPARTICIPATION CHANGES:\")\n", + "print(f\" Baseline CTC recipients: {baseline_ctc_recipients:,.0f}\")\n", + "print(f\" Reformed CTC recipients: {reformed_ctc_recipients:,.0f}\")\n", + "print(f\" Change in recipients: {recipient_change:+,.0f}\")" + ] + }, + { + "cell_type": "markdown", + "id": "97352137", + "metadata": {}, + "source": [ + "## 5.\n" + ] + }, + { + "cell_type": "code", + "execution_count": 42, + "id": "70317f33", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "=== Introduction to calculate_dataframe() Method ===\n", + "Variables we'll analyze:\n", + " - household_id\n", + " - household_weight\n", + " - household_net_income\n", + " - employment_income\n", + " - ctc_value\n", + " - snap\n", + " - state_code\n", + " - household_size\n", + "\n", + "Calculating baseline variables using calculate_dataframe...\n", + "Baseline DataFrame shape: (41310, 8)\n", + "DataFrame columns: ['household_id', 'household_weight', 'household_net_income', 'employment_income', 'ctc_value', 'snap', 'state_code', 'household_size']\n", + "\n", + "First few records:\n", + " household_id household_weight household_net_income employment_income \\\n", + "0 12 14044.562500 103463.171875 4219.356445 \n", + "1 21 1151.017212 87697.304688 96693.578125 \n", + "2 22 11405.556641 111439.476562 0.000000 \n", + "3 30 3046.133301 92810.648438 87903.257812 \n", + "4 36 11906.229492 52004.863281 34985.497070 \n", + "\n", + " ctc_value snap state_code household_size \n", + "0 0.0 0.000000 ME 2 \n", + "1 0.0 0.000000 ME 3 \n", + "2 0.0 3806.939087 ME 2 \n", + "3 0.0 281.995483 ME 2 \n", + "4 0.0 0.000000 ME 3 \n", + "\n", + "=== Understanding Weights: calculate() vs calculate_dataframe() ===\n", + "Total CTC (auto-weighted by calculate()): $115.7 billion\n", + "Total CTC (unweighted DataFrame): $115.7 billion\n", + "Total CTC (manually weighted): $8112021.5 billion\n", + "\n", + "VERIFICATION:\n", + " calculate() vs manual weighting difference: $-8111905782.8 million\n", + " This should be close to zero!\n", + "\n", + "CRITICAL INSIGHT:\n", + " Weighted total is 70111.2x larger than unweighted\n", + " This shows why proper weighting is essential for accurate estimates\n", + "\n", + "CTC RECIPIENTS:\n", + " Unweighted count: 43,865,806.295775 records\n", + " Weighted count: 4,208,911,395,251 actual households\n", + " Multiplier: 95949.7x\n" + ] + } + ], + "source": [ + "print(\"=== Introduction to calculate_dataframe() Method ===\")\n", + "\n", + "# Key variables for comprehensive analysis\n", + "CORE_VARIABLES = [\n", + " \"household_id\",\n", + " \"household_weight\", # How many households this record represents\n", + " \"household_net_income\", # After-tax income\n", + " \"employment_income\", # Pre-tax earnings\n", + " \"ctc_value\", # Child Tax Credit\n", + " \"snap\", # SNAP benefits \n", + " \"state_code\", # State location\n", + " \"household_size\", # Number of people\n", + "]\n", + "\n", + "print(\"Variables we'll analyze:\")\n", + "for var in CORE_VARIABLES:\n", + " print(f\" - {var}\")\n", + "\n", + "# Calculate baseline data using calculate_dataframe\n", + "print(\"\\nCalculating baseline variables using calculate_dataframe...\")\n", + "baseline_df = baseline.calculate_dataframe(CORE_VARIABLES, map_to=\"household\", period=2025)\n", + "\n", + "print(f\"Baseline DataFrame shape: {baseline_df.shape}\")\n", + "print(f\"DataFrame columns: {list(baseline_df.columns)}\")\n", + "print(\"\\nFirst few records:\")\n", + "print(baseline_df.head())\n", + "\n", + "print(\"\\n=== Understanding Weights: calculate() vs calculate_dataframe() ===\")\n", + "\n", + "# METHOD 1: Using calculate() - PolicyEngine automatically applies weights\n", + "total_ctc_auto = baseline.calculate(\"ctc_value\", period=2025).sum()\n", + "print(f\"Total CTC (auto-weighted by calculate()): ${total_ctc_auto/1e9:.1f} billion\")\n", + "\n", + "# METHOD 2: Using pandas DataFrame - weights are NOT automatically applied\n", + "total_ctc_unweighted = baseline_df['ctc_value'].sum()\n", + "print(f\"Total CTC (unweighted DataFrame): ${total_ctc_unweighted/1e9:.1f} billion\")\n", + "\n", + "# METHOD 3: Manual weighting with DataFrame\n", + "total_ctc_manual = (baseline_df['ctc_value'] * baseline_df['household_weight']).sum()\n", + "print(f\"Total CTC (manually weighted): ${total_ctc_manual/1e9:.1f} billion\")\n", + "\n", + "# Verification: Methods 1 and 3 should match\n", + "print(f\"\\nVERIFICATION:\")\n", + "print(f\" calculate() vs manual weighting difference: ${(total_ctc_auto - total_ctc_manual)/1e6:.1f} million\")\n", + "print(f\" This should be close to zero!\")\n", + "\n", + "# Show the importance of weighting\n", + "weight_multiplier = total_ctc_manual / total_ctc_unweighted\n", + "print(f\"\\nCRITICAL INSIGHT:\")\n", + "print(f\" Weighted total is {weight_multiplier:.1f}x larger than unweighted\")\n", + "print(f\" This shows why proper weighting is essential for accurate estimates\")\n", + "\n", + "# Number of households receiving CTC\n", + "ctc_recipients_unweighted = (baseline_df['ctc_value'] > 0).sum()\n", + "ctc_recipients_weighted = baseline_df[baseline_df['ctc_value'] > 0]['household_weight'].sum()\n", + "\n", + "print(f\"\\nCTC RECIPIENTS:\")\n", + "print(f\" Unweighted count: {ctc_recipients_unweighted:,} records\")\n", + "print(f\" Weighted count: {ctc_recipients_weighted:,.0f} actual households\")\n", + "print(f\" Multiplier: {ctc_recipients_weighted/ctc_recipients_unweighted:.1f}x\")" + ] + }, + { + "cell_type": "markdown", + "id": "df9f7034", + "metadata": {}, + "source": [ + "## 6" + ] + }, + { + "cell_type": "code", + "execution_count": 43, + "id": "19fad2ee", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "=== 10-Year Budgetary Impact Analysis ===\n", + "Calculating annual impacts across 10 years...\n", + " 2025: $69.8B increase\n", + " 2026: $102.1B increase\n", + " 2027: $102.2B increase\n", + " 2028: $101.8B increase\n", + " 2029: $101.1B increase\n", + " 2030: $100.1B increase\n", + " 2031: $99.1B increase\n", + " 2032: $97.9B increase\n", + " 2033: $96.6B increase\n", + " 2034: $95.1B increase\n", + "\n", + "10-YEAR FISCAL IMPACT:\n", + " Total 10-year CTC increase: $965.9 billion\n", + " Average annual cost: $96.6 billion\n", + " Compound annual growth rate: 3.5%\n" + ] + } + ], + "source": [ + "print(\"=== 10-Year Budgetary Impact Analysis ===\")\n", + "\n", + "# Calculate impacts for multiple years (2025-2034)\n", + "years = list(range(2025, 2035))\n", + "annual_impacts = []\n", + "\n", + "print(\"Calculating annual impacts across 10 years...\")\n", + "for year in years:\n", + " # Calculate baseline and reform totals for each year\n", + " baseline_ctc = baseline.calculate(\"ctc_value\", period=year).sum()\n", + " reformed_ctc = reformed.calculate(\"ctc_value\", period=year).sum()\n", + " annual_increase = reformed_ctc - baseline_ctc\n", + " \n", + " annual_impacts.append({\n", + " 'year': year,\n", + " 'baseline_ctc': baseline_ctc / 1e9,\n", + " 'reformed_ctc': reformed_ctc / 1e9,\n", + " 'annual_increase': annual_increase / 1e9\n", + " })\n", + " \n", + " print(f\" {year}: ${annual_increase/1e9:.1f}B increase\")\n", + "\n", + "# Create DataFrame for analysis\n", + "impact_df = pd.DataFrame(annual_impacts)\n", + "\n", + "# Calculate 10-year total\n", + "ten_year_total = impact_df['annual_increase'].sum()\n", + "print(f\"\\n10-YEAR FISCAL IMPACT:\")\n", + "print(f\" Total 10-year CTC increase: ${ten_year_total:.1f} billion\")\n", + "print(f\" Average annual cost: ${ten_year_total/10:.1f} billion\")\n", + "\n", + "# Calculate compound annual growth rate\n", + "first_year = impact_df['annual_increase'].iloc[0]\n", + "last_year = impact_df['annual_increase'].iloc[-1]\n", + "if first_year > 0:\n", + " cagr = ((last_year / first_year) ** (1/9) - 1) * 100\n", + " print(f\" Compound annual growth rate: {cagr:.1f}%\")" + ] + }, + { + "cell_type": "markdown", + "id": "e68335b5", + "metadata": {}, + "source": [ + "## 7\n" + ] + }, + { + "cell_type": "code", + "execution_count": 44, + "id": "edc9daf6", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "=== Using DataFrames for Reform Analysis ===\n", + "Creating comparison analysis...\n", + "DATAFRAME COMPARISON (with manual weighting):\n", + " Baseline (manual): $115.7 billion\n", + " Reformed (manual): $13322317.9 billion\n", + " Difference: $13322202.2 billion\n", + "\n", + "VERIFICATION against calculate() method:\n", + " Single variable difference: $69.8 billion\n", + " DataFrame difference: $13322202.2 billion\n", + " Match? False\n", + "\n", + "=== Geographic Analysis by State ===\n", + "Preparing state-level analysis...\n", + "Calculating state-level impacts...\n", + "\n", + "TOP 10 STATES BY TOTAL CTC INCREASE:\n", + " CA: $7.1B total, $386 avg, 21.5% benefit\n", + " TX: $6.5B total, $527 avg, 24.7% benefit\n", + " FL: $4.1B total, $369 avg, 17.5% benefit\n", + " NY: $3.6B total, $387 avg, 19.8% benefit\n", + " PA: $2.8B total, $489 avg, 19.1% benefit\n", + " OH: $2.7B total, $508 avg, 20.3% benefit\n", + " NJ: $2.4B total, $615 avg, 24.9% benefit\n", + " MI: $2.4B total, $505 avg, 23.1% benefit\n", + " GA: $2.2B total, $456 avg, 23.7% benefit\n", + " IL: $2.1B total, $365 avg, 17.1% benefit\n", + "\n", + "STATES WITH HIGHEST BENEFIT RATES:\n", + " UT: 30.0% of households benefit\n", + " ND: 28.2% of households benefit\n", + " NJ: 24.9% of households benefit\n", + " TX: 24.7% of households benefit\n", + " AK: 24.5% of households benefit\n" + ] + } + ], + "source": [ + "print(\"=== Using DataFrames for Reform Analysis ===\")\n", + "\n", + "# Calculate the same variables under reform\n", + "reformed_df = reformed.calculate_dataframe(CORE_VARIABLES, map_to=\"household\", period=2025)\n", + "\n", + "print(\"Creating comparison analysis...\")\n", + "\n", + "# Method 1: Using calculate() for verification\n", + "baseline_total_manual = (baseline_df['ctc_value'] * baseline_df['household_weight']).sum()\n", + "reformed_total_manual = (reformed_df['ctc_value'] * reformed_df['household_weight']).sum()\n", + "\n", + "print(f\"DATAFRAME COMPARISON (with manual weighting):\")\n", + "print(f\" Baseline (manual): ${baseline_total_manual/1e9:.1f} billion\")\n", + "print(f\" Reformed (manual): ${reformed_total_manual/1e9:.1f} billion\")\n", + "print(f\" Difference: ${(reformed_total_manual - baseline_total_manual)/1e9:.1f} billion\")\n", + "\n", + "# Verify against single variable method\n", + "print(f\"\\nVERIFICATION against calculate() method:\")\n", + "print(f\" Single variable difference: ${ctc_increase/1e9:.1f} billion\")\n", + "print(f\" DataFrame difference: ${(reformed_total_manual - baseline_total_manual)/1e9:.1f} billion\")\n", + "print(f\" Match? {abs(ctc_increase - (reformed_total_manual - baseline_total_manual)) < 1e6}\")\n", + "\n", + "print(\"\\n=== Geographic Analysis by State ===\")\n", + "\n", + "# Create comparison DataFrame\n", + "print(\"Preparing state-level analysis...\")\n", + "comparison_df = baseline_df.copy()\n", + "comparison_df['ctc_baseline'] = baseline_df['ctc_value']\n", + "comparison_df['ctc_reformed'] = reformed_df['ctc_value']\n", + "comparison_df['ctc_increase'] = comparison_df['ctc_reformed'] - comparison_df['ctc_baseline']\n", + "\n", + "# Group by state and calculate weighted totals\n", + "print(\"Calculating state-level impacts...\")\n", + "state_analysis = comparison_df.groupby('state_code').apply(\n", + " lambda x: pd.Series({\n", + " 'households': x['household_weight'].sum(),\n", + " 'total_ctc_increase': (x['ctc_increase'] * x['household_weight']).sum(),\n", + " 'avg_ctc_increase': (x['ctc_increase'] * x['household_weight']).sum() / x['household_weight'].sum(),\n", + " 'households_benefiting': x[x['ctc_increase'] > 0]['household_weight'].sum()\n", + " })\n", + ").reset_index()\n", + "\n", + "# Calculate percentage of households benefiting\n", + "state_analysis['pct_households_benefiting'] = (\n", + " state_analysis['households_benefiting'] / state_analysis['households'] * 100\n", + ")\n", + "\n", + "# Sort by total CTC increase\n", + "state_analysis = state_analysis.sort_values('total_ctc_increase', ascending=False)\n", + "\n", + "print(f\"\\nTOP 10 STATES BY TOTAL CTC INCREASE:\")\n", + "top_states = state_analysis.head(10)\n", + "for _, row in top_states.iterrows():\n", + " print(f\" {row['state_code']}: ${row['total_ctc_increase']/1e9:.1f}B total, \"\n", + " f\"${row['avg_ctc_increase']:,.0f} avg, \"\n", + " f\"{row['pct_households_benefiting']:.1f}% benefit\")\n", + "\n", + "print(f\"\\nSTATES WITH HIGHEST BENEFIT RATES:\")\n", + "top_pct_states = state_analysis.nlargest(5, 'pct_households_benefiting')\n", + "for _, row in top_pct_states.iterrows():\n", + " print(f\" {row['state_code']}: {row['pct_households_benefiting']:.1f}% of households benefit\")" + ] + }, + { + "cell_type": "markdown", + "id": "5a38ffdc", + "metadata": {}, + "source": [ + "## 8\n" + ] + }, + { + "cell_type": "code", + "execution_count": 45, + "id": "4a1176a0", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "=== Program Enrollment Analysis ===\n", + "Calculating person-level data...\n", + "Person-level dataset shape: (101726, 4)\n", + "\n", + "SNAP ENROLLMENT ANALYSIS:\n", + " Baseline SNAP enrollment: 2,994,233,014,168 people\n", + " Reformed SNAP enrollment: 2,994,233,014,168 people\n", + " Change in SNAP enrollment: +0 people\n", + "\n", + "CHILDREN ANALYSIS:\n", + " Total children in dataset: 4,587,869,965,050\n", + " Note: CTC eligibility depends on household structure and income\n", + " SNAP enrollment change: +0.00%\n", + "\n", + "=== Dataset Selection for Different Analysis Types ===\n", + "Dataset Recommendations:\n", + " • National analysis: enhanced_cps_2024.h5 (current choice)\n", + " • State-level analysis: Consider pooled datasets for larger samples\n", + " • Historical analysis: Use year-specific datasets\n", + "\n", + "STATE SAMPLE SIZE INFORMATION:\n", + " Average records per state: 810\n", + " States with <100 records: 0\n", + " For detailed state analysis, consider pooled datasets\n" + ] + } + ], + "source": [ + "print(\"=== Program Enrollment Analysis ===\")\n", + "\n", + "# Analyze SNAP enrollment (map_to=\"person\" for individual-level analysis)\n", + "PERSON_VARIABLES = [\n", + " \"person_id\",\n", + " \"person_weight\", \n", + " \"age\",\n", + " \"snap\"\n", + "]\n", + "\n", + "print(\"Calculating person-level data...\")\n", + "baseline_persons = baseline.calculate_dataframe(PERSON_VARIABLES, map_to=\"person\", period=2025)\n", + "reformed_persons = reformed.calculate_dataframe(PERSON_VARIABLES, map_to=\"person\", period=2025)\n", + "\n", + "print(f\"Person-level dataset shape: {baseline_persons.shape}\")\n", + "\n", + "# SNAP enrollment: people with SNAP > 0\n", + "baseline_snap_enrolled = baseline_persons[baseline_persons['snap'] > 0]\n", + "reformed_snap_enrolled = reformed_persons[reformed_persons['snap'] > 0]\n", + "\n", + "# Apply person weights for accurate counts\n", + "baseline_snap_count = baseline_snap_enrolled['person_weight'].sum()\n", + "reformed_snap_count = reformed_snap_enrolled['person_weight'].sum()\n", + "\n", + "print(f\"\\nSNAP ENROLLMENT ANALYSIS:\")\n", + "print(f\" Baseline SNAP enrollment: {baseline_snap_count:,.0f} people\")\n", + "print(f\" Reformed SNAP enrollment: {reformed_snap_count:,.0f} people\")\n", + "print(f\" Change in SNAP enrollment: {reformed_snap_count - baseline_snap_count:+,.0f} people\")\n", + "\n", + "# Children analysis\n", + "baseline_children = baseline_persons[baseline_persons['age'] < 18]\n", + "reformed_children = reformed_persons[reformed_persons['age'] < 18]\n", + "\n", + "total_children = baseline_children['person_weight'].sum()\n", + "print(f\"\\nCHILDREN ANALYSIS:\")\n", + "print(f\" Total children in dataset: {total_children:,.0f}\")\n", + "print(f\" Note: CTC eligibility depends on household structure and income\")\n", + "\n", + "# Percentage change in SNAP enrollment\n", + "if baseline_snap_count > 0:\n", + " snap_change_pct = ((reformed_snap_count - baseline_snap_count) / baseline_snap_count) * 100\n", + " print(f\" SNAP enrollment change: {snap_change_pct:+.2f}%\")\n", + "\n", + "# Dataset selection information\n", + "print(\"\\n=== Dataset Selection for Different Analysis Types ===\")\n", + "\n", + "print(\"Dataset Recommendations:\")\n", + "print(\" • National analysis: enhanced_cps_2024.h5 (current choice)\")\n", + "print(\" • State-level analysis: Consider pooled datasets for larger samples\")\n", + "print(\" • Historical analysis: Use year-specific datasets\")\n", + "\n", + "# Demonstrate state sample sizes with enhanced CPS\n", + "state_samples = baseline_df.groupby('state_code').agg({\n", + " 'household_weight': ['count', 'sum']\n", + "}).round()\n", + "\n", + "state_samples.columns = ['Records', 'Weighted_Households']\n", + "state_samples = state_samples.sort_values('Weighted_Households', ascending=False)\n", + "\n", + "print(f\"\\nSTATE SAMPLE SIZE INFORMATION:\")\n", + "print(f\" Average records per state: {state_samples['Records'].mean():.0f}\")\n", + "print(f\" States with <100 records: {(state_samples['Records'] < 100).sum()}\")\n", + "print(f\" For detailed state analysis, consider pooled datasets\")" + ] + }, + { + "cell_type": "markdown", + "id": "5637a4d7", + "metadata": {}, + "source": [ + "## 9.\n" + ] + }, + { + "cell_type": "code", + "execution_count": 46, + "id": "a7bced2a", + "metadata": {}, + "outputs": [ + { + "data": { + "application/vnd.plotly.v1+json": { + "config": { + "plotlyServerURL": "https://plot.ly" + }, + "data": [ + { + "line": { + "color": "blue", + "width": 3 + }, + "mode": "lines+markers", + "name": "Annual CTC Increase", + "type": "scatter", + "x": [ + 2025, + 2026, + 2027, + 2028, + 2029, + 2030, + 2031, + 2032, + 2033, + 2034 + ], + "xaxis": "x", + "y": [ + 69.75372217024525, + 102.08657157214773, + 102.24966134680338, + 101.8232899651484, + 101.0558852567367, + 100.12501457690912, + 99.08997815359614, + 97.94614529849079, + 96.62221545429239, + 95.12440585516775 + ], + "yaxis": "y" + }, + { + "marker": { + "color": "green" + }, + "name": "State CTC Increase", + "type": "bar", + "x": [ + "CA", + "TX", + "FL", + "NY", + "PA", + "OH", + "NJ", + "MI", + "GA", + "IL" + ], + "xaxis": "x2", + "y": [ + 7.074109487850279, + 6.514486052727338, + 4.075205689423572, + 3.646191199533445, + 2.8440606618066413, + 2.6923423668345774, + 2.4329848821995856, + 2.3691607212100747, + 2.2306325466647183, + 2.09148640063547 + ], + "yaxis": "y2" + }, + { + "marker": { + "color": "red" + }, + "name": "Baseline CTC", + "opacity": 0.6, + "type": "bar", + "x": [ + 0, + 200, + 400, + 600, + 800, + 1000, + 1200, + 1400, + 1600, + 1800, + 2000, + 2200, + 2400, + 2600, + 2800, + 3000, + 3200, + 3400, + 3600 + ], + "xaxis": "x3", + "y": [ + 110.12433624267578, + 0.4570559859275818, + 5.782904148101807, + 0.32396000623703003, + 0.26176801323890686, + 1.1055680513381958, + 0.566815972328186, + 0.2824159860610962, + 0.6299200057983398, + 0.2845360040664673, + 16.68377685546875, + 0.06268800050020218, + 2.3401761054992676, + 0.27699199318885803, + 0.2885119915008545, + 0.37883201241493225, + 0.0931679978966713, + 0.4472639858722687, + 0.2749600112438202 + ], + "yaxis": "y3" + }, + { + "marker": { + "color": "blue" + }, + "name": "Reformed CTC", + "opacity": 0.6, + "type": "bar", + "x": [ + 0, + 200, + 400, + 600, + 800, + 1000, + 1200, + 1400, + 1600, + 1800, + 2000, + 2200, + 2400, + 2600, + 2800, + 3000, + 3200, + 3400, + 3600 + ], + "xaxis": "x3", + "y": [ + 109.94103240966797, + 0.44739198684692383, + 5.769375801086426, + 0.32417601346969604, + 0.30083200335502625, + 1.0618159770965576, + 0.5929200053215027, + 0.29661598801612854, + 0.5140159726142883, + 0.2770319879055023, + 0.5919039845466614, + 0.275160014629364, + 0.6826320290565491, + 0.3543280065059662, + 1.1985520124435425, + 0.3946160078048706, + 0.6550719738006592, + 0.9897680282592773, + 13.919816017150879 + ], + "yaxis": "y3" + }, + { + "marker": { + "color": "orange" + }, + "name": "SNAP Enrollment", + "type": "bar", + "x": [ + "SNAP (Baseline)", + "SNAP (Reformed)" + ], + "xaxis": "x4", + "y": [ + 2994233.0141677563, + 2994233.0141677563 + ], + "yaxis": "y4" + } + ], + "layout": { + "annotations": [ + { + "font": { + "size": 16 + }, + "showarrow": false, + "text": "10-Year Impact Trend", + "x": 0.225, + "xanchor": "center", + "xref": "paper", + "y": 1, + "yanchor": "bottom", + "yref": "paper" + }, + { + "font": { + "size": 16 + }, + "showarrow": false, + "text": "State-by-State Impact", + "x": 0.775, + "xanchor": "center", + "xref": "paper", + "y": 1, + "yanchor": "bottom", + "yref": "paper" + }, + { + "font": { + "size": 16 + }, + "showarrow": false, + "text": "CTC Distribution Comparison", + "x": 0.225, + "xanchor": "center", + "xref": "paper", + "y": 0.375, + "yanchor": "bottom", + "yref": "paper" + }, + { + "font": { + "size": 16 + }, + "showarrow": false, + "text": "Program Enrollment", + "x": 0.775, + "xanchor": "center", + "xref": "paper", + "y": 0.375, + "yanchor": "bottom", + "yref": "paper" + } + ], + "height": 800, + "showlegend": true, + "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": "CTC Reform: Comprehensive Policy Impact Analysis" + }, + "xaxis": { + "anchor": "y", + "domain": [ + 0, + 0.45 + ], + "title": { + "text": "Year" + } + }, + "xaxis2": { + "anchor": "y2", + "domain": [ + 0.55, + 1 + ], + "title": { + "text": "State" + } + }, + "xaxis3": { + "anchor": "y3", + "domain": [ + 0, + 0.45 + ], + "title": { + "text": "CTC Amount ($)" + } + }, + "xaxis4": { + "anchor": "y4", + "domain": [ + 0.55, + 1 + ], + "title": { + "text": "Program" + } + }, + "yaxis": { + "anchor": "x", + "domain": [ + 0.625, + 1 + ], + "title": { + "text": "Annual Increase ($B)" + } + }, + "yaxis2": { + "anchor": "x2", + "domain": [ + 0.625, + 1 + ], + "title": { + "text": "Total Increase ($B)" + } + }, + "yaxis3": { + "anchor": "x3", + "domain": [ + 0, + 0.375 + ], + "title": { + "text": "Households (Millions)" + } + }, + "yaxis4": { + "anchor": "x4", + "domain": [ + 0, + 0.375 + ], + "title": { + "text": "Enrollment (Millions)" + } + } + } + } + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "# Create multiple visualizations\n", + "\n", + "fig = make_subplots(\n", + " rows=2, cols=2,\n", + " subplot_titles=('10-Year Impact Trend', 'State-by-State Impact', \n", + " 'CTC Distribution Comparison', 'Program Enrollment'),\n", + " specs=[[{\"secondary_y\": False}, {\"secondary_y\": False}],\n", + " [{\"secondary_y\": False}, {\"secondary_y\": False}]]\n", + ")\n", + "\n", + "# Plot 1: 10-year trend\n", + "fig.add_trace(\n", + " go.Scatter(x=impact_df['year'], y=impact_df['annual_increase'],\n", + " mode='lines+markers', name='Annual CTC Increase',\n", + " line=dict(color='blue', width=3)),\n", + " row=1, col=1\n", + ")\n", + "\n", + "# Plot 2: Top 10 states\n", + "top_10_states = state_analysis.head(10)\n", + "fig.add_trace(\n", + " go.Bar(x=top_10_states['state_code'], y=top_10_states['total_ctc_increase']/1e9,\n", + " name='State CTC Increase', marker_color='green'),\n", + " row=1, col=2\n", + ")\n", + "\n", + "# Plot 3: CTC distribution comparison\n", + "ctc_bins = np.arange(0, 4000, 200)\n", + "baseline_hist = np.histogram(comparison_df['ctc_baseline'], bins=ctc_bins, \n", + " weights=comparison_df['household_weight'])[0]\n", + "reformed_hist = np.histogram(comparison_df['ctc_reformed'], bins=ctc_bins,\n", + " weights=comparison_df['household_weight'])[0]\n", + "\n", + "fig.add_trace(\n", + " go.Bar(x=ctc_bins[:-1], y=baseline_hist/1e6, name='Baseline CTC',\n", + " marker_color='red', opacity=0.6),\n", + " row=2, col=1\n", + ")\n", + "fig.add_trace(\n", + " go.Bar(x=ctc_bins[:-1], y=reformed_hist/1e6, name='Reformed CTC',\n", + " marker_color='blue', opacity=0.6),\n", + " row=2, col=1\n", + ")\n", + "\n", + "# Plot 4: Program enrollment comparison\n", + "enrollment_data = {\n", + " 'Program': ['SNAP (Baseline)', 'SNAP (Reformed)'],\n", + " 'Enrollment': [baseline_snap_count/1e6, reformed_snap_count/1e6]\n", + "}\n", + "fig.add_trace(\n", + " go.Bar(x=enrollment_data['Program'], y=enrollment_data['Enrollment'],\n", + " name='SNAP Enrollment', marker_color='orange'),\n", + " row=2, col=2\n", + ")\n", + "\n", + "# Update layout\n", + "fig.update_layout(height=800, showlegend=True, \n", + " title_text=\"CTC Reform: Comprehensive Policy Impact Analysis\")\n", + "\n", + "# Update axis labels\n", + "fig.update_xaxes(title_text=\"Year\", row=1, col=1)\n", + "fig.update_yaxes(title_text=\"Annual Increase ($B)\", row=1, col=1)\n", + "fig.update_xaxes(title_text=\"State\", row=1, col=2)\n", + "fig.update_yaxes(title_text=\"Total Increase ($B)\", row=1, col=2)\n", + "fig.update_xaxes(title_text=\"CTC Amount ($)\", row=2, col=1)\n", + "fig.update_yaxes(title_text=\"Households (Millions)\", row=2, col=1)\n", + "fig.update_xaxes(title_text=\"Program\", row=2, col=2)\n", + "fig.update_yaxes(title_text=\"Enrollment (Millions)\", row=2, col=2)\n", + "\n", + "fig.show()" + ] + }, + { + "cell_type": "markdown", + "id": "3e935734", + "metadata": {}, + "source": [ + "Summary\n", + "This notebook demonstrated a progressive approach to economy-wide policy analysis using PolicyEngine:\n", + "\n", + "Foundation: Started with single variable calculations using .calculate()\n", + "Core Concept: Understood automatic weighting in .calculate()\n", + "Advanced Method: Introduced calculate_dataframe() for multiple variables\n", + "Critical Insight: Learned the difference between automatic and manual weighting\n", + "Comprehensive Analysis: Applied concepts to geographic, temporal, and program analysis\n", + "\n", + "Key Learning Progression: Single variables → automatic weighting → DataFrames → manual weighting → complex analysis\n", + "This training material provides a solid foundation for conducting rigorous economy-wide policy analysis using PolicyEngine's microsimulation capabilities." + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "pe", + "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.10.14" + } + }, + "nbformat": 4, + "nbformat_minor": 5 +}