diff --git a/us/states/mn/hf1938/marriage_penalty_analysis.ipynb b/us/states/mn/hf1938/marriage_penalty_analysis.ipynb
new file mode 100644
index 0000000..f4f66d7
--- /dev/null
+++ b/us/states/mn/hf1938/marriage_penalty_analysis.ipynb
@@ -0,0 +1,981 @@
+{
+ "cells": [
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "# Marriage Penalty/Bonus Analysis\n",
+ "\n",
+ "Compare `income_tax + state_income_tax` between:\n",
+ "- **Factual**: actual marital status\n",
+ "- **Counterfactual**: flipped marital status\n",
+ "\n",
+ "Transformations:\n",
+ "- **Married → Single**: Spouse income combined into primary earner\n",
+ "- **Single → Married**: Add spouse with $0 income (tests bracket benefit)"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 1,
+ "metadata": {},
+ "outputs": [
+ {
+ "name": "stderr",
+ "output_type": "stream",
+ "text": [
+ "/Library/Frameworks/Python.framework/Versions/3.10/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 pandas as pd\n",
+ "import numpy as np\n",
+ "from policyengine_us import Simulation\n",
+ "\n",
+ "YEAR = 2025"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 2,
+ "metadata": {},
+ "outputs": [
+ {
+ "name": "stdout",
+ "output_type": "stream",
+ "text": [
+ "Total households: 112,502\n",
+ "Marital status: {1: 56604, 2: 55898}\n"
+ ]
+ }
+ ],
+ "source": [
+ "# Load CPS data\n",
+ "df = pd.read_csv('/Users/pavelmakarchuk/analysis-notebooks/cps_households.csv')\n",
+ "df['total_wages'] = df['pwages'] + df['swages']\n",
+ "\n",
+ "print(f\"Total households: {len(df):,}\")\n",
+ "print(f\"Marital status: {df['mstat'].value_counts().to_dict()}\")"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 3,
+ "metadata": {},
+ "outputs": [
+ {
+ "name": "stdout",
+ "output_type": "stream",
+ "text": [
+ "Subset: 50 households with $30k-$150k wages\n",
+ "Married (mstat=2): 32\n",
+ "Single (mstat=1): 18\n"
+ ]
+ }
+ ],
+ "source": [
+ "# Filter to households with meaningful earnings ($30k-$150k)\n",
+ "subset = df[(df['total_wages'] > 30000) & (df['total_wages'] < 150000)].head(50).copy()\n",
+ "\n",
+ "print(f\"Subset: {len(subset)} households with $30k-$150k wages\")\n",
+ "print(f\"Married (mstat=2): {(subset['mstat']==2).sum()}\")\n",
+ "print(f\"Single (mstat=1): {(subset['mstat']==1).sum()}\")"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 4,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "def create_situation_from_row(row, flip_marital_status=False):\n",
+ " \"\"\"Create a PolicyEngine situation from a TAXSIM row.\n",
+ " \n",
+ " If flip_marital_status=True:\n",
+ " - Married (mstat=2) becomes single: remove spouse, combine ALL incomes\n",
+ " - Single (mstat=1) becomes married: add spouse with $0 income\n",
+ " \"\"\"\n",
+ " is_married = row['mstat'] == 2\n",
+ " \n",
+ " # Determine effective marital status\n",
+ " if flip_marital_status:\n",
+ " effective_married = not is_married\n",
+ " else:\n",
+ " effective_married = is_married\n",
+ " \n",
+ " # Helper to safely get numeric values\n",
+ " def get_val(col):\n",
+ " return float(row.get(col, 0) or 0)\n",
+ " \n",
+ " # Primary earner income\n",
+ " primary_wages = get_val('pwages')\n",
+ " primary_self_emp = get_val('psemp')\n",
+ " \n",
+ " # Spouse income (only if originally married)\n",
+ " spouse_wages = get_val('swages') if is_married else 0\n",
+ " spouse_self_emp = get_val('ssemp') if is_married else 0\n",
+ " \n",
+ " # Shared income types (allocated to primary, will combine spouse if flipping)\n",
+ " dividends = get_val('dividends')\n",
+ " interest = get_val('intrec')\n",
+ " stcg = get_val('stcg')\n",
+ " ltcg = get_val('ltcg')\n",
+ " pensions = get_val('pensions')\n",
+ " social_security = get_val('gssi')\n",
+ " \n",
+ " # Build people\n",
+ " people = {\n",
+ " \"adult\": {\n",
+ " \"age\": {YEAR: int(row['page'])},\n",
+ " \"employment_income\": {YEAR: primary_wages},\n",
+ " \"self_employment_income\": {YEAR: primary_self_emp},\n",
+ " \"dividend_income\": {YEAR: dividends},\n",
+ " \"interest_income\": {YEAR: interest},\n",
+ " \"short_term_capital_gains\": {YEAR: stcg},\n",
+ " \"long_term_capital_gains\": {YEAR: ltcg},\n",
+ " \"pension_income\": {YEAR: pensions},\n",
+ " \"social_security\": {YEAR: social_security},\n",
+ " }\n",
+ " }\n",
+ " members = [\"adult\"]\n",
+ " marital_members = [\"adult\"]\n",
+ " \n",
+ " # Handle spouse\n",
+ " if effective_married:\n",
+ " if is_married and not flip_marital_status:\n",
+ " # Original married, staying married - spouse keeps their income\n",
+ " people[\"spouse\"] = {\n",
+ " \"age\": {YEAR: int(row['sage']) if row['sage'] > 0 else int(row['page'])},\n",
+ " \"employment_income\": {YEAR: spouse_wages},\n",
+ " \"self_employment_income\": {YEAR: spouse_self_emp},\n",
+ " }\n",
+ " else:\n",
+ " # Single becoming married - add spouse with $0 income\n",
+ " people[\"spouse\"] = {\n",
+ " \"age\": {YEAR: int(row['page'])},\n",
+ " \"employment_income\": {YEAR: 0},\n",
+ " }\n",
+ " members.append(\"spouse\")\n",
+ " marital_members.append(\"spouse\")\n",
+ " elif is_married and flip_marital_status:\n",
+ " # Married becoming single - combine ALL spouse income into adult\n",
+ " people[\"adult\"][\"employment_income\"][YEAR] += spouse_wages\n",
+ " people[\"adult\"][\"self_employment_income\"][YEAR] += spouse_self_emp\n",
+ " \n",
+ " # Add dependents\n",
+ " dep_ages = [row[f'age{i}'] for i in range(1, 12) if f'age{i}' in row and pd.notna(row[f'age{i}']) and row[f'age{i}'] > 0]\n",
+ " for i, age in enumerate(dep_ages):\n",
+ " dep_name = f\"child_{i}\"\n",
+ " people[dep_name] = {\"age\": {YEAR: int(age)}}\n",
+ " members.append(dep_name)\n",
+ " \n",
+ " situation = {\n",
+ " \"people\": people,\n",
+ " \"families\": {\"family\": {\"members\": members}},\n",
+ " \"marital_units\": {\"marital_unit\": {\"members\": marital_members}},\n",
+ " \"tax_units\": {\"tax_unit\": {\"members\": members}},\n",
+ " \"households\": {\n",
+ " \"household\": {\n",
+ " \"members\": members,\n",
+ " \"state_name\": {YEAR: \"MN\"},\n",
+ " }\n",
+ " },\n",
+ " }\n",
+ " \n",
+ " return situation"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 5,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "def calculate_taxes(situation):\n",
+ " \"\"\"Calculate income_tax + state_income_tax for a situation.\"\"\"\n",
+ " sim = Simulation(situation=situation)\n",
+ " income_tax = sim.calculate(\"income_tax\", YEAR)\n",
+ " state_income_tax = sim.calculate(\"state_income_tax\", YEAR)\n",
+ " return float(income_tax.sum() + state_income_tax.sum())"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 6,
+ "metadata": {},
+ "outputs": [
+ {
+ "name": "stdout",
+ "output_type": "stream",
+ "text": [
+ "Processed 50 households\n"
+ ]
+ }
+ ],
+ "source": [
+ "# Calculate factual and counterfactual taxes for each household\n",
+ "results = []\n",
+ "\n",
+ "for idx, row in subset.iterrows():\n",
+ " try:\n",
+ " # Factual (actual marital status)\n",
+ " factual_situation = create_situation_from_row(row, flip_marital_status=False)\n",
+ " factual_tax = calculate_taxes(factual_situation)\n",
+ " \n",
+ " # Counterfactual (flipped marital status)\n",
+ " counterfactual_situation = create_situation_from_row(row, flip_marital_status=True)\n",
+ " counterfactual_tax = calculate_taxes(counterfactual_situation)\n",
+ " \n",
+ " results.append({\n",
+ " 'taxsimid': row['taxsimid'],\n",
+ " 'original_mstat': 'married' if row['mstat'] == 2 else 'single',\n",
+ " 'pwages': row['pwages'],\n",
+ " 'swages': row['swages'],\n",
+ " 'depx': row['depx'],\n",
+ " 'factual_tax': factual_tax,\n",
+ " 'counterfactual_tax': counterfactual_tax,\n",
+ " 'marriage_penalty': counterfactual_tax - factual_tax if row['mstat'] == 1 else factual_tax - counterfactual_tax,\n",
+ " })\n",
+ " except Exception as e:\n",
+ " print(f\"Error on row {idx}: {e}\")\n",
+ "\n",
+ "results_df = pd.DataFrame(results)\n",
+ "print(f\"Processed {len(results_df)} households\")"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 7,
+ "metadata": {},
+ "outputs": [
+ {
+ "data": {
+ "text/html": [
+ "
\n",
+ "\n",
+ "
\n",
+ " \n",
+ " \n",
+ " | \n",
+ " taxsimid | \n",
+ " original_mstat | \n",
+ " pwages | \n",
+ " swages | \n",
+ " depx | \n",
+ " factual_tax | \n",
+ " counterfactual_tax | \n",
+ " marriage_penalty | \n",
+ "
\n",
+ " \n",
+ " \n",
+ " \n",
+ " | 0 | \n",
+ " 2.0 | \n",
+ " married | \n",
+ " 0.000000 | \n",
+ " 41904.761905 | \n",
+ " 0.0 | \n",
+ " 1840.755859 | \n",
+ " 4437.500977 | \n",
+ " -2596.745117 | \n",
+ "
\n",
+ " \n",
+ " | 1 | \n",
+ " 8.0 | \n",
+ " married | \n",
+ " 0.000000 | \n",
+ " 64668.476190 | \n",
+ " 1.0 | \n",
+ " 4775.831055 | \n",
+ " 5551.355957 | \n",
+ " -775.524902 | \n",
+ "
\n",
+ " \n",
+ " | 2 | \n",
+ " 12.0 | \n",
+ " married | \n",
+ " 45047.619048 | \n",
+ " 26190.476190 | \n",
+ " 0.0 | \n",
+ " 6691.184570 | \n",
+ " 10651.116211 | \n",
+ " -3959.931641 | \n",
+ "
\n",
+ " \n",
+ " | 3 | \n",
+ " 17.0 | \n",
+ " single | \n",
+ " 83809.523810 | \n",
+ " 0.000000 | \n",
+ " 0.0 | \n",
+ " 14271.687500 | \n",
+ " 8969.470703 | \n",
+ " -5302.216797 | \n",
+ "
\n",
+ " \n",
+ " | 4 | \n",
+ " 25.0 | \n",
+ " single | \n",
+ " 75428.571429 | \n",
+ " 0.000000 | \n",
+ " 2.0 | \n",
+ " 4332.105957 | \n",
+ " 2867.181641 | \n",
+ " -1464.924316 | \n",
+ "
\n",
+ " \n",
+ " | 5 | \n",
+ " 26.0 | \n",
+ " married | \n",
+ " 55824.476190 | \n",
+ " 0.000000 | \n",
+ " 0.0 | \n",
+ " 4016.921875 | \n",
+ " 6977.046387 | \n",
+ " -2960.124512 | \n",
+ "
\n",
+ " \n",
+ " | 6 | \n",
+ " 28.0 | \n",
+ " single | \n",
+ " 44000.000000 | \n",
+ " 0.000000 | \n",
+ " 0.0 | \n",
+ " 4801.024902 | \n",
+ " 2162.375000 | \n",
+ " -2638.649902 | \n",
+ "
\n",
+ " \n",
+ " | 7 | \n",
+ " 29.0 | \n",
+ " single | \n",
+ " 31428.571429 | \n",
+ " 0.000000 | \n",
+ " 1.0 | \n",
+ " -6190.179688 | \n",
+ " -8171.620117 | \n",
+ " -1981.440430 | \n",
+ "
\n",
+ " \n",
+ " | 8 | \n",
+ " 30.0 | \n",
+ " married | \n",
+ " 77523.809524 | \n",
+ " 28809.523810 | \n",
+ " 0.0 | \n",
+ " 13203.947266 | \n",
+ " 20758.546875 | \n",
+ " -7554.599609 | \n",
+ "
\n",
+ " \n",
+ " | 9 | \n",
+ " 36.0 | \n",
+ " married | \n",
+ " 41904.761905 | \n",
+ " 18857.142857 | \n",
+ " 0.0 | \n",
+ " 7874.586426 | \n",
+ " 13305.869141 | \n",
+ " -5431.282715 | \n",
+ "
\n",
+ " \n",
+ " | 10 | \n",
+ " 38.0 | \n",
+ " single | \n",
+ " 52380.952381 | \n",
+ " 0.000000 | \n",
+ " 0.0 | \n",
+ " 6329.664062 | \n",
+ " 3448.851074 | \n",
+ " -2880.812988 | \n",
+ "
\n",
+ " \n",
+ " | 11 | \n",
+ " 39.0 | \n",
+ " single | \n",
+ " 68095.238095 | \n",
+ " 0.000000 | \n",
+ " 0.0 | \n",
+ " 9745.972656 | \n",
+ " 6145.898438 | \n",
+ " -3600.074219 | \n",
+ "
\n",
+ " \n",
+ " | 12 | \n",
+ " 44.0 | \n",
+ " married | \n",
+ " 23329.428571 | \n",
+ " 54476.190476 | \n",
+ " 0.0 | \n",
+ " 7943.892578 | \n",
+ " 12700.588867 | \n",
+ " -4756.696289 | \n",
+ "
\n",
+ " \n",
+ " | 13 | \n",
+ " 45.0 | \n",
+ " married | \n",
+ " 41904.761905 | \n",
+ " 0.000000 | \n",
+ " 0.0 | \n",
+ " 4705.386719 | \n",
+ " 9933.289062 | \n",
+ " -5227.902344 | \n",
+ "
\n",
+ " \n",
+ " | 14 | \n",
+ " 48.0 | \n",
+ " married | \n",
+ " 83809.523810 | \n",
+ " 29483.142857 | \n",
+ " 2.0 | \n",
+ " 9811.901367 | \n",
+ " 14044.822266 | \n",
+ " -4232.920898 | \n",
+ "
\n",
+ " \n",
+ " | 15 | \n",
+ " 56.0 | \n",
+ " married | \n",
+ " 0.000000 | \n",
+ " 104761.904762 | \n",
+ " 1.0 | \n",
+ " 22060.537109 | \n",
+ " 28406.324219 | \n",
+ " -6345.787109 | \n",
+ "
\n",
+ " \n",
+ " | 16 | \n",
+ " 57.0 | \n",
+ " single | \n",
+ " 31428.571429 | \n",
+ " 0.000000 | \n",
+ " 0.0 | \n",
+ " 4989.263184 | \n",
+ " 1377.149658 | \n",
+ " -3612.113525 | \n",
+ "
\n",
+ " \n",
+ " | 17 | \n",
+ " 61.0 | \n",
+ " married | \n",
+ " 0.000000 | \n",
+ " 61809.523810 | \n",
+ " 1.0 | \n",
+ " 2780.753906 | \n",
+ " 4213.653809 | \n",
+ " -1432.899902 | \n",
+ "
\n",
+ " \n",
+ " | 18 | \n",
+ " 62.0 | \n",
+ " single | \n",
+ " 41904.761905 | \n",
+ " 0.000000 | \n",
+ " 1.0 | \n",
+ " -1602.751709 | \n",
+ " -4629.322754 | \n",
+ " -3026.571045 | \n",
+ "
\n",
+ " \n",
+ " | 19 | \n",
+ " 63.0 | \n",
+ " married | \n",
+ " 41904.761905 | \n",
+ " 0.000000 | \n",
+ " 1.0 | \n",
+ " 3358.932617 | \n",
+ " 6225.772461 | \n",
+ " -2866.839844 | \n",
+ "
\n",
+ " \n",
+ "
\n",
+ "
"
+ ],
+ "text/plain": [
+ " taxsimid original_mstat pwages swages depx factual_tax \\\n",
+ "0 2.0 married 0.000000 41904.761905 0.0 1840.755859 \n",
+ "1 8.0 married 0.000000 64668.476190 1.0 4775.831055 \n",
+ "2 12.0 married 45047.619048 26190.476190 0.0 6691.184570 \n",
+ "3 17.0 single 83809.523810 0.000000 0.0 14271.687500 \n",
+ "4 25.0 single 75428.571429 0.000000 2.0 4332.105957 \n",
+ "5 26.0 married 55824.476190 0.000000 0.0 4016.921875 \n",
+ "6 28.0 single 44000.000000 0.000000 0.0 4801.024902 \n",
+ "7 29.0 single 31428.571429 0.000000 1.0 -6190.179688 \n",
+ "8 30.0 married 77523.809524 28809.523810 0.0 13203.947266 \n",
+ "9 36.0 married 41904.761905 18857.142857 0.0 7874.586426 \n",
+ "10 38.0 single 52380.952381 0.000000 0.0 6329.664062 \n",
+ "11 39.0 single 68095.238095 0.000000 0.0 9745.972656 \n",
+ "12 44.0 married 23329.428571 54476.190476 0.0 7943.892578 \n",
+ "13 45.0 married 41904.761905 0.000000 0.0 4705.386719 \n",
+ "14 48.0 married 83809.523810 29483.142857 2.0 9811.901367 \n",
+ "15 56.0 married 0.000000 104761.904762 1.0 22060.537109 \n",
+ "16 57.0 single 31428.571429 0.000000 0.0 4989.263184 \n",
+ "17 61.0 married 0.000000 61809.523810 1.0 2780.753906 \n",
+ "18 62.0 single 41904.761905 0.000000 1.0 -1602.751709 \n",
+ "19 63.0 married 41904.761905 0.000000 1.0 3358.932617 \n",
+ "\n",
+ " counterfactual_tax marriage_penalty \n",
+ "0 4437.500977 -2596.745117 \n",
+ "1 5551.355957 -775.524902 \n",
+ "2 10651.116211 -3959.931641 \n",
+ "3 8969.470703 -5302.216797 \n",
+ "4 2867.181641 -1464.924316 \n",
+ "5 6977.046387 -2960.124512 \n",
+ "6 2162.375000 -2638.649902 \n",
+ "7 -8171.620117 -1981.440430 \n",
+ "8 20758.546875 -7554.599609 \n",
+ "9 13305.869141 -5431.282715 \n",
+ "10 3448.851074 -2880.812988 \n",
+ "11 6145.898438 -3600.074219 \n",
+ "12 12700.588867 -4756.696289 \n",
+ "13 9933.289062 -5227.902344 \n",
+ "14 14044.822266 -4232.920898 \n",
+ "15 28406.324219 -6345.787109 \n",
+ "16 1377.149658 -3612.113525 \n",
+ "17 4213.653809 -1432.899902 \n",
+ "18 -4629.322754 -3026.571045 \n",
+ "19 6225.772461 -2866.839844 "
+ ]
+ },
+ "execution_count": 7,
+ "metadata": {},
+ "output_type": "execute_result"
+ }
+ ],
+ "source": [
+ "# View results\n",
+ "results_df.head(20)"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 8,
+ "metadata": {},
+ "outputs": [
+ {
+ "name": "stdout",
+ "output_type": "stream",
+ "text": [
+ "=== Marriage Penalty/Bonus Summary ===\n",
+ "\n",
+ "Positive = marriage PENALTY (pay MORE when married)\n",
+ "Negative = marriage BONUS (pay LESS when married)\n",
+ "\n",
+ "Mean effect: $-4,017\n",
+ "Median effect: $-3,534\n",
+ "\n",
+ "Households with marriage penalty: 0\n",
+ "Households with marriage bonus: 50\n",
+ "Households with no change: 0\n",
+ "\n",
+ "=== Results Table ===\n"
+ ]
+ },
+ {
+ "data": {
+ "text/html": [
+ "\n",
+ "\n",
+ "
\n",
+ " \n",
+ " \n",
+ " | \n",
+ " taxsimid | \n",
+ " original_mstat | \n",
+ " pwages | \n",
+ " swages | \n",
+ " depx | \n",
+ " factual_tax | \n",
+ " counterfactual_tax | \n",
+ " marriage_penalty | \n",
+ "
\n",
+ " \n",
+ " \n",
+ " \n",
+ " | 0 | \n",
+ " 2.0 | \n",
+ " married | \n",
+ " 0.000000 | \n",
+ " 41904.761905 | \n",
+ " 0.0 | \n",
+ " 1840.755859 | \n",
+ " 4437.500977 | \n",
+ " -2596.745117 | \n",
+ "
\n",
+ " \n",
+ " | 1 | \n",
+ " 8.0 | \n",
+ " married | \n",
+ " 0.000000 | \n",
+ " 64668.476190 | \n",
+ " 1.0 | \n",
+ " 4775.831055 | \n",
+ " 5551.355957 | \n",
+ " -775.524902 | \n",
+ "
\n",
+ " \n",
+ " | 2 | \n",
+ " 12.0 | \n",
+ " married | \n",
+ " 45047.619048 | \n",
+ " 26190.476190 | \n",
+ " 0.0 | \n",
+ " 6691.184570 | \n",
+ " 10651.116211 | \n",
+ " -3959.931641 | \n",
+ "
\n",
+ " \n",
+ " | 3 | \n",
+ " 17.0 | \n",
+ " single | \n",
+ " 83809.523810 | \n",
+ " 0.000000 | \n",
+ " 0.0 | \n",
+ " 14271.687500 | \n",
+ " 8969.470703 | \n",
+ " -5302.216797 | \n",
+ "
\n",
+ " \n",
+ " | 4 | \n",
+ " 25.0 | \n",
+ " single | \n",
+ " 75428.571429 | \n",
+ " 0.000000 | \n",
+ " 2.0 | \n",
+ " 4332.105957 | \n",
+ " 2867.181641 | \n",
+ " -1464.924316 | \n",
+ "
\n",
+ " \n",
+ " | 5 | \n",
+ " 26.0 | \n",
+ " married | \n",
+ " 55824.476190 | \n",
+ " 0.000000 | \n",
+ " 0.0 | \n",
+ " 4016.921875 | \n",
+ " 6977.046387 | \n",
+ " -2960.124512 | \n",
+ "
\n",
+ " \n",
+ " | 6 | \n",
+ " 28.0 | \n",
+ " single | \n",
+ " 44000.000000 | \n",
+ " 0.000000 | \n",
+ " 0.0 | \n",
+ " 4801.024902 | \n",
+ " 2162.375000 | \n",
+ " -2638.649902 | \n",
+ "
\n",
+ " \n",
+ " | 7 | \n",
+ " 29.0 | \n",
+ " single | \n",
+ " 31428.571429 | \n",
+ " 0.000000 | \n",
+ " 1.0 | \n",
+ " -6190.179688 | \n",
+ " -8171.620117 | \n",
+ " -1981.440430 | \n",
+ "
\n",
+ " \n",
+ " | 8 | \n",
+ " 30.0 | \n",
+ " married | \n",
+ " 77523.809524 | \n",
+ " 28809.523810 | \n",
+ " 0.0 | \n",
+ " 13203.947266 | \n",
+ " 20758.546875 | \n",
+ " -7554.599609 | \n",
+ "
\n",
+ " \n",
+ " | 9 | \n",
+ " 36.0 | \n",
+ " married | \n",
+ " 41904.761905 | \n",
+ " 18857.142857 | \n",
+ " 0.0 | \n",
+ " 7874.586426 | \n",
+ " 13305.869141 | \n",
+ " -5431.282715 | \n",
+ "
\n",
+ " \n",
+ " | 10 | \n",
+ " 38.0 | \n",
+ " single | \n",
+ " 52380.952381 | \n",
+ " 0.000000 | \n",
+ " 0.0 | \n",
+ " 6329.664062 | \n",
+ " 3448.851074 | \n",
+ " -2880.812988 | \n",
+ "
\n",
+ " \n",
+ " | 11 | \n",
+ " 39.0 | \n",
+ " single | \n",
+ " 68095.238095 | \n",
+ " 0.000000 | \n",
+ " 0.0 | \n",
+ " 9745.972656 | \n",
+ " 6145.898438 | \n",
+ " -3600.074219 | \n",
+ "
\n",
+ " \n",
+ " | 12 | \n",
+ " 44.0 | \n",
+ " married | \n",
+ " 23329.428571 | \n",
+ " 54476.190476 | \n",
+ " 0.0 | \n",
+ " 7943.892578 | \n",
+ " 12700.588867 | \n",
+ " -4756.696289 | \n",
+ "
\n",
+ " \n",
+ " | 13 | \n",
+ " 45.0 | \n",
+ " married | \n",
+ " 41904.761905 | \n",
+ " 0.000000 | \n",
+ " 0.0 | \n",
+ " 4705.386719 | \n",
+ " 9933.289062 | \n",
+ " -5227.902344 | \n",
+ "
\n",
+ " \n",
+ " | 14 | \n",
+ " 48.0 | \n",
+ " married | \n",
+ " 83809.523810 | \n",
+ " 29483.142857 | \n",
+ " 2.0 | \n",
+ " 9811.901367 | \n",
+ " 14044.822266 | \n",
+ " -4232.920898 | \n",
+ "
\n",
+ " \n",
+ " | 15 | \n",
+ " 56.0 | \n",
+ " married | \n",
+ " 0.000000 | \n",
+ " 104761.904762 | \n",
+ " 1.0 | \n",
+ " 22060.537109 | \n",
+ " 28406.324219 | \n",
+ " -6345.787109 | \n",
+ "
\n",
+ " \n",
+ " | 16 | \n",
+ " 57.0 | \n",
+ " single | \n",
+ " 31428.571429 | \n",
+ " 0.000000 | \n",
+ " 0.0 | \n",
+ " 4989.263184 | \n",
+ " 1377.149658 | \n",
+ " -3612.113525 | \n",
+ "
\n",
+ " \n",
+ " | 17 | \n",
+ " 61.0 | \n",
+ " married | \n",
+ " 0.000000 | \n",
+ " 61809.523810 | \n",
+ " 1.0 | \n",
+ " 2780.753906 | \n",
+ " 4213.653809 | \n",
+ " -1432.899902 | \n",
+ "
\n",
+ " \n",
+ " | 18 | \n",
+ " 62.0 | \n",
+ " single | \n",
+ " 41904.761905 | \n",
+ " 0.000000 | \n",
+ " 1.0 | \n",
+ " -1602.751709 | \n",
+ " -4629.322754 | \n",
+ " -3026.571045 | \n",
+ "
\n",
+ " \n",
+ " | 19 | \n",
+ " 63.0 | \n",
+ " married | \n",
+ " 41904.761905 | \n",
+ " 0.000000 | \n",
+ " 1.0 | \n",
+ " 3358.932617 | \n",
+ " 6225.772461 | \n",
+ " -2866.839844 | \n",
+ "
\n",
+ " \n",
+ "
\n",
+ "
"
+ ],
+ "text/plain": [
+ " taxsimid original_mstat pwages swages depx factual_tax \\\n",
+ "0 2.0 married 0.000000 41904.761905 0.0 1840.755859 \n",
+ "1 8.0 married 0.000000 64668.476190 1.0 4775.831055 \n",
+ "2 12.0 married 45047.619048 26190.476190 0.0 6691.184570 \n",
+ "3 17.0 single 83809.523810 0.000000 0.0 14271.687500 \n",
+ "4 25.0 single 75428.571429 0.000000 2.0 4332.105957 \n",
+ "5 26.0 married 55824.476190 0.000000 0.0 4016.921875 \n",
+ "6 28.0 single 44000.000000 0.000000 0.0 4801.024902 \n",
+ "7 29.0 single 31428.571429 0.000000 1.0 -6190.179688 \n",
+ "8 30.0 married 77523.809524 28809.523810 0.0 13203.947266 \n",
+ "9 36.0 married 41904.761905 18857.142857 0.0 7874.586426 \n",
+ "10 38.0 single 52380.952381 0.000000 0.0 6329.664062 \n",
+ "11 39.0 single 68095.238095 0.000000 0.0 9745.972656 \n",
+ "12 44.0 married 23329.428571 54476.190476 0.0 7943.892578 \n",
+ "13 45.0 married 41904.761905 0.000000 0.0 4705.386719 \n",
+ "14 48.0 married 83809.523810 29483.142857 2.0 9811.901367 \n",
+ "15 56.0 married 0.000000 104761.904762 1.0 22060.537109 \n",
+ "16 57.0 single 31428.571429 0.000000 0.0 4989.263184 \n",
+ "17 61.0 married 0.000000 61809.523810 1.0 2780.753906 \n",
+ "18 62.0 single 41904.761905 0.000000 1.0 -1602.751709 \n",
+ "19 63.0 married 41904.761905 0.000000 1.0 3358.932617 \n",
+ "\n",
+ " counterfactual_tax marriage_penalty \n",
+ "0 4437.500977 -2596.745117 \n",
+ "1 5551.355957 -775.524902 \n",
+ "2 10651.116211 -3959.931641 \n",
+ "3 8969.470703 -5302.216797 \n",
+ "4 2867.181641 -1464.924316 \n",
+ "5 6977.046387 -2960.124512 \n",
+ "6 2162.375000 -2638.649902 \n",
+ "7 -8171.620117 -1981.440430 \n",
+ "8 20758.546875 -7554.599609 \n",
+ "9 13305.869141 -5431.282715 \n",
+ "10 3448.851074 -2880.812988 \n",
+ "11 6145.898438 -3600.074219 \n",
+ "12 12700.588867 -4756.696289 \n",
+ "13 9933.289062 -5227.902344 \n",
+ "14 14044.822266 -4232.920898 \n",
+ "15 28406.324219 -6345.787109 \n",
+ "16 1377.149658 -3612.113525 \n",
+ "17 4213.653809 -1432.899902 \n",
+ "18 -4629.322754 -3026.571045 \n",
+ "19 6225.772461 -2866.839844 "
+ ]
+ },
+ "execution_count": 8,
+ "metadata": {},
+ "output_type": "execute_result"
+ }
+ ],
+ "source": [
+ "# Summary statistics\n",
+ "print(\"=== Marriage Penalty/Bonus Summary ===\")\n",
+ "print(f\"\\nPositive = marriage PENALTY (pay MORE when married)\")\n",
+ "print(f\"Negative = marriage BONUS (pay LESS when married)\")\n",
+ "print(f\"\\nMean effect: ${results_df['marriage_penalty'].mean():,.0f}\")\n",
+ "print(f\"Median effect: ${results_df['marriage_penalty'].median():,.0f}\")\n",
+ "print(f\"\\nHouseholds with marriage penalty: {(results_df['marriage_penalty'] > 0).sum()}\")\n",
+ "print(f\"Households with marriage bonus: {(results_df['marriage_penalty'] < 0).sum()}\")\n",
+ "print(f\"Households with no change: {(results_df['marriage_penalty'] == 0).sum()}\")\n",
+ "\n",
+ "# Summary table\n",
+ "print(\"\\n=== Results Table ===\")\n",
+ "display_cols = ['taxsimid', 'original_mstat', 'pwages', 'swages', 'depx', 'factual_tax', 'counterfactual_tax', 'marriage_penalty']\n",
+ "results_df[display_cols].head(20)"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 9,
+ "metadata": {},
+ "outputs": [
+ {
+ "name": "stdout",
+ "output_type": "stream",
+ "text": [
+ "=== By Original Marital Status ===\n",
+ "\n",
+ "SINGLE households (n=18):\n",
+ " Mean marriage effect: $-3,355\n",
+ " Avg factual tax: $5,900\n",
+ " Avg counterfactual tax: $2,545\n",
+ "\n",
+ "MARRIED households (n=32):\n",
+ " Mean marriage effect: $-4,389\n",
+ " Avg factual tax: $7,781\n",
+ " Avg counterfactual tax: $12,171\n"
+ ]
+ }
+ ],
+ "source": [
+ "# Breakdown by original marital status\n",
+ "print(\"=== By Original Marital Status ===\")\n",
+ "for status in ['single', 'married']:\n",
+ " subset_status = results_df[results_df['original_mstat'] == status]\n",
+ " if len(subset_status) > 0:\n",
+ " print(f\"\\n{status.upper()} households (n={len(subset_status)}):\")\n",
+ " print(f\" Mean marriage effect: ${subset_status['marriage_penalty'].mean():,.0f}\")\n",
+ " print(f\" Avg factual tax: ${subset_status['factual_tax'].mean():,.0f}\")\n",
+ " print(f\" Avg counterfactual tax: ${subset_status['counterfactual_tax'].mean():,.0f}\")"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 10,
+ "metadata": {},
+ "outputs": [
+ {
+ "name": "stdout",
+ "output_type": "stream",
+ "text": [
+ "Saved 50 rows to marriage_penalty_results.csv\n"
+ ]
+ }
+ ],
+ "source": [
+ "# Export results to CSV\n",
+ "results_df.to_csv('marriage_penalty_results.csv', index=False)\n",
+ "print(f\"Saved {len(results_df)} rows to marriage_penalty_results.csv\")"
+ ]
+ }
+ ],
+ "metadata": {
+ "kernelspec": {
+ "display_name": "Python 3",
+ "language": "python",
+ "name": "python3"
+ },
+ "language_info": {
+ "name": "python",
+ "version": "3.10.11"
+ }
+ },
+ "nbformat": 4,
+ "nbformat_minor": 4
+}
diff --git a/us/states/mn/hf1938/marriage_penalty_results.csv b/us/states/mn/hf1938/marriage_penalty_results.csv
new file mode 100644
index 0000000..ec9a3d8
--- /dev/null
+++ b/us/states/mn/hf1938/marriage_penalty_results.csv
@@ -0,0 +1,51 @@
+taxsimid,original_mstat,pwages,swages,depx,factual_tax,counterfactual_tax,marriage_penalty
+2.0,married,0.0,41904.76190476191,0.0,1840.755859375,4437.5009765625,-2596.7451171875
+8.0,married,0.0,64668.47619047619,1.0,4775.8310546875,5551.35595703125,-775.52490234375
+12.0,married,45047.61904761905,26190.47619047619,0.0,6691.1845703125,10651.1162109375,-3959.931640625
+17.0,single,83809.52380952382,0.0,0.0,14271.6875,8969.470703125,-5302.216796875
+25.0,single,75428.57142857143,0.0,2.0,4332.10595703125,2867.181640625,-1464.92431640625
+26.0,married,55824.47619047619,0.0,0.0,4016.921875,6977.04638671875,-2960.12451171875
+28.0,single,44000.0,0.0,0.0,4801.02490234375,2162.375,-2638.64990234375
+29.0,single,31428.57142857143,0.0,1.0,-6190.1796875,-8171.6201171875,-1981.4404296875
+30.0,married,77523.80952380953,28809.52380952381,0.0,13203.947265625,20758.546875,-7554.599609375
+36.0,married,41904.76190476191,18857.14285714286,0.0,7874.58642578125,13305.869140625,-5431.28271484375
+38.0,single,52380.95238095238,0.0,0.0,6329.6640625,3448.85107421875,-2880.81298828125
+39.0,single,68095.23809523809,0.0,0.0,9745.97265625,6145.8984375,-3600.07421875
+44.0,married,23329.428571428572,54476.19047619048,0.0,7943.892578125,12700.5888671875,-4756.6962890625
+45.0,married,41904.76190476191,0.0,0.0,4705.38671875,9933.2890625,-5227.90234375
+48.0,married,83809.52380952382,29483.14285714286,2.0,9811.9013671875,14044.822265625,-4232.9208984375
+56.0,married,0.0,104761.90476190476,1.0,22060.537109375,28406.32421875,-6345.787109375
+57.0,single,31428.57142857143,0.0,0.0,4989.26318359375,1377.149658203125,-3612.113525390625
+61.0,married,0.0,61809.52380952381,1.0,2780.75390625,4213.65380859375,-1432.89990234375
+62.0,single,41904.76190476191,0.0,1.0,-1602.751708984375,-4629.32275390625,-3026.571044921875
+63.0,married,41904.76190476191,0.0,1.0,3358.9326171875,6225.7724609375,-2866.83984375
+66.0,married,36666.66666666667,0.0,1.0,-6873.388671875,-3920.512939453125,-2952.875732421875
+67.0,married,69142.85714285714,0.0,0.0,7381.5712890625,11643.890625,-4262.3193359375
+68.0,single,69142.85714285714,0.0,0.0,18552.736328125,10816.99609375,-7735.740234375
+70.0,single,73333.33333333334,0.0,1.0,6288.40234375,4779.18408203125,-1509.21826171875
+71.0,single,73333.33333333334,0.0,0.0,11254.5458984375,7054.708984375,-4199.8369140625
+83.0,married,64952.380952380954,20549.04761904762,0.0,9287.548828125,14758.95703125,-5471.408203125
+85.0,married,0.0,73333.33333333334,0.0,6779.7841796875,11254.5458984375,-4474.76171875
+86.0,married,0.0,47142.857142857145,0.0,8136.34375,13272.732421875,-5136.388671875
+91.0,married,62857.14285714286,57619.04761904762,0.0,18063.30859375,29094.29296875,-11030.984375
+93.0,married,52380.95238095238,0.0,1.0,-89.98928833007812,2576.84521484375,-2666.834503173828
+95.0,married,69142.85714285714,50285.71428571429,1.0,13063.919921875,18163.48046875,-5099.560546875
+96.0,married,40857.14285714286,27238.09523809524,1.0,3898.633544921875,5337.41796875,-1438.784423828125
+97.0,single,39809.52380952381,0.0,0.0,3729.65234375,974.6915893554688,-2754.9607543945312
+100.0,married,0.0,31428.57142857143,4.0,1505.756591796875,2281.282958984375,-775.5263671875
+101.0,married,0.0,31428.57142857143,0.0,-78.52880859375,3240.96923828125,-3319.498046875
+102.0,married,91142.85714285714,58666.66666666667,2.0,22355.76953125,28786.798828125,-6431.029296875
+104.0,single,59714.28571428572,0.0,0.0,8426.734375,4878.43505859375,-3548.29931640625
+106.0,married,0.0,73333.33333333334,0.0,9001.6728515625,15007.22265625,-6005.5498046875
+109.0,married,0.0,104761.90476190476,0.0,13705.78515625,22592.529296875,-8886.744140625
+110.0,single,86952.38095238096,0.0,0.0,15176.83203125,9560.328125,-5616.50390625
+112.0,single,31428.57142857143,0.0,0.0,2283.7109375,-144.13914489746094,-2427.850082397461
+120.0,married,36666.66666666667,39809.52380952381,2.0,4548.943359375,5324.46875,-775.525390625
+121.0,single,64952.380952380954,0.0,0.0,8840.83203125,5600.61328125,-3240.21875
+122.0,married,53266.19047619048,45047.61904761905,0.0,11475.8388671875,18448.923828125,-6973.0849609375
+124.0,single,59714.28571428572,0.0,0.0,7708.3310546875,4691.80322265625,-3016.52783203125
+125.0,married,36666.66666666667,13619.04761904762,0.0,6155.8740234375,9676.244140625,-3520.3701171875
+131.0,married,7899.047619047619,57619.04761904762,1.0,6104.87890625,6880.40380859375,-775.52490234375
+132.0,married,39809.52380952381,52380.95238095238,2.0,5775.373046875,7967.392578125,-2192.01953125
+133.0,single,31428.57142857143,0.0,2.0,-12733.966796875,-14566.9248046875,-1832.9580078125
+134.0,married,0.0,136190.47619047618,0.0,19741.134765625,29864.6796875,-10123.544921875