Skip to article frontmatterSkip to article content

Household Impacts

This chapter analyzes how each of the eight Social Security taxation reform options affects individual households across different income levels. The analysis shows both the change in net income and comparative baseline vs. reform scenarios for each policy option.

Household Example

The household impact analysis uses a representative household with the following characteristics:

  • Single elderly tax filer

  • Age 65+

  • Social Security benefits: $30,000 per year

  • Wages and salaries varying from 0to0 to 200,000 in $500 increments

  • Located in Florida (no state income tax)

  • Standard deduction

For each income level and policy option, we calculate:

  1. Change in Net Income: The difference in household net income under the reform versus baseline

  2. Baseline vs Reform Comparison: Total net income under current law versus under each policy reform

Source
# Import necessary libraries for visualization
from IPython.display import display, HTML
import plotly.express as px
import plotly.graph_objects as go
import pandas as pd
import warnings
warnings.filterwarnings('ignore')

# Define PolicyEngine's color palette
BLACK = "#000000"
BLUE_LIGHT = "#D8E6F3"
BLUE_PRIMARY = "#2C6496"
DARK_BLUE_HOVER = "#1d3e5e"
DARK_GRAY = "#616161"
DARKEST_BLUE = "#0C1A27"
GRAY = "#808080"
LIGHT_GRAY = "#F2F2F2"
MEDIUM_DARK_GRAY = "#D2D2D2"
MEDIUM_LIGHT_GRAY = "#BDBDBD"
WHITE = "#FFFFFF"
Source
# Load the pre-generated household impact data
combined_df = pd.read_csv("../data/household_impacts.csv")
Source
# Define visualization functions
def create_animated_change_graph(df, reform_name):
    """Create an animated graph showing change in net income across years with enhanced hover data"""

    # Create custom hover data
    fig = go.Figure()

    # Get unique years for animation frames
    years = sorted(df['year'].unique())

    # Calculate the overall min and max for y-axis across all years
    y_min = df['change_in_net_income'].min()
    y_max = df['change_in_net_income'].max()
    # Add 5% padding to make the chart look better
    y_range_padding = (y_max - y_min) * 0.05
    y_range = [y_min - y_range_padding, y_max + y_range_padding]

    # Create frames for animation
    frames = []
    for year in years:
        year_data = df[df['year'] == year]
        frame = go.Frame(
            data=[go.Scatter(
                x=year_data['employment_income'],
                y=year_data['change_in_net_income'],
                mode='lines',
                line=dict(color=BLUE_PRIMARY, width=2),
                customdata=year_data[['baseline_net_income', 'reform_net_income']],
                hovertemplate='<b>Wages and Salaries:</b> $%{x:,.0f}<br>' +
                             '<b>Change in Net Income:</b> $%{y:,.0f}<br>' +
                             '<b>Baseline Net Income:</b> $%{customdata[0]:,.0f}<br>' +
                             '<b>Reform Net Income:</b> $%{customdata[1]:,.0f}<br>' +
                             '<extra></extra>'
            )],
            name=str(year)
        )
        frames.append(frame)

    # Add initial data for first year
    initial_year = years[0]
    initial_data = df[df['year'] == initial_year]

    fig.add_trace(go.Scatter(
        x=initial_data['employment_income'],
        y=initial_data['change_in_net_income'],
        mode='lines',
        line=dict(color=BLUE_PRIMARY, width=2),
        customdata=initial_data[['baseline_net_income', 'reform_net_income']],
        hovertemplate='<b>Wages and Salaries:</b> $%{x:,.0f}<br>' +
                     '<b>Change in Net Income:</b> $%{y:,.0f}<br>' +
                     '<b>Baseline Net Income:</b> $%{customdata[0]:,.0f}<br>' +
                     '<b>Reform Net Income:</b> $%{customdata[1]:,.0f}<br>' +
                     '<extra></extra>',
        name='Impact'
    ))

    fig.frames = frames

    # Add only the slider for year selection (no play/pause buttons)
    fig.update_layout(
        sliders=[{
            "active": 0,
            "steps": [
                {
                    "label": str(year),
                    "method": "animate",
                    "args": [[str(year)], {
                        "frame": {"duration": 0, "redraw": True},
                        "mode": "immediate",
                        "transition": {"duration": 0}
                    }]
                } for year in years
            ],
            "y": 0,
            "len": 0.9,
            "x": 0.05
        }]
    )

    fig.update_layout(
        title={
            'text': f"Change in Net Income - {reform_name}<br><sub>Single Elderly Filer with $30,000 in Social Security Benefits</sub>",
            'x': 0.5,
            'xanchor': 'center'
        },
        xaxis_title="Wages and Salaries",
        yaxis_title="Change in Net Income",
        yaxis=dict(range=y_range),  # Set fixed y-axis range for all frames
        font=dict(family="Roboto Serif"),
        xaxis_tickformat="$,",
        yaxis_tickformat="$,",
        font_color=BLACK,
        margin={"l": 50, "r": 50, "b": 100, "t": 100, "pad": 4},
        showlegend=False,
        annotations=[
            {
                "x": 1,
                "y": -0.25,
                "xref": "paper",
                "yref": "paper",
                "text": "Source: PolicyEngine US",
                "showarrow": False,
                "font": {"family": "Roboto Serif", "size": 10, "color": DARK_GRAY},
            }
        ],
    )

    return fig

Option 1: Full Repeal of Social Security Benefits Taxation

This option completely eliminates the taxation of Social Security benefits, returning to the pre-1984 system where benefits were not subject to income tax.

Source
# Option 1: Full Repeal - Visualization
option1_df = combined_df[combined_df['reform'] == "Full Repeal of Social Security Benefits Taxation"].copy()

# Create and display animated change in net income graph with enhanced hover data
fig1_change = create_animated_change_graph(option1_df, "Option 1: Full Repeal")
fig1_change.show()
Loading...

Option 2: Taxation of 85% of Social Security Benefits

This option taxes 85% of Social Security benefits for all recipients, regardless of income level, eliminating the current threshold system.

Source
# Option 2: Taxation of 85% - Visualization
option2_df = combined_df[combined_df['reform'] == "Taxation of 85% of Social Security Benefits"].copy()

# Create and display animated change in net income graph with enhanced hover data
fig2_change = create_animated_change_graph(option2_df, "Option 2: Taxation of 85%")
fig2_change.show()
Loading...

Option 3: 85% Taxation with Permanent Senior Deduction Extension

This option combines taxation of 85% of benefits with a permanent extension of the senior deduction that would otherwise expire in 2028.

Source
# Option 3: 85% with Senior Deduction - Visualization
option3_df = combined_df[combined_df['reform'] == "85% Taxation with Permanent Senior Deduction Extension"].copy()

# Create and display animated change in net income graph with enhanced hover data
fig3_change = create_animated_change_graph(option3_df, "Option 3: 85% with Senior Deduction")
fig3_change.show()
Loading...

Option 4: Social Security Tax Credit System ($500 Credit)

This option replaces the senior deduction with a $500 nonrefundable tax credit while taxing 85% of benefits.

Source
# Option 4: Tax Credit System - Visualization
option4_df = combined_df[combined_df['reform'] == "Social Security Tax Credit System ($500)"].copy()

# Create and display animated change in net income graph with enhanced hover data
fig4_change = create_animated_change_graph(option4_df, "Option 4: $500 Tax Credit")
fig4_change.show()
Loading...

Option 5: Roth-Style Swap

This option includes all employer payroll contributions in taxable income and excludes all Social Security benefits from taxable income.

Source
# Option 5: Roth-Style Swap - Visualization
option5_df = combined_df[combined_df['reform'] == "Roth-Style Swap"].copy()

# Create and display animated change in net income graph with enhanced hover data
fig5_change = create_animated_change_graph(option5_df, "Option 5: Roth-Style Swap")
fig5_change.show()
Loading...

Option 6: Phased Roth-Style Swap

This option phases in all employer payroll contributions by 1 percentage point per year until the full 7.65 percent employer contribution is taxable. The option also phases down the current formula for income taxation of Social Security benefits 5 percentage points per year (e.g., 2028: 50/85; 2029: 45/80; 2030: 40/75…, 2038: 0/35; 2039: 0/30…, 2045: 0/0)

Source
# Option 6: Phased Roth-Style - Visualization
option6_df = combined_df[combined_df['reform'] == "Phased Roth-Style Swap"].copy()

# Create and display animated change in net income graph with enhanced hover data
fig6_change = create_animated_change_graph(option6_df, "Option 6: Phased Roth-Style")
fig6_change.show()
Loading...

Option 7: Eliminate Bonus Senior Deduction

This option eliminates the 6,000bonusseniordeductionfromtheOneBigBeautifulBillthatincludesa66,000 bonus senior deduction from the One Big Beautiful Bill that includes a 6% phase-out beginning at 75k for single filers and $150k for joint filers.

Source
# Option 7: Eliminate Bonus Senior Deduction - Visualization
option7_df = combined_df[combined_df['reform'] == "Eliminate Bonus Senior Deduction"].copy()

# Create and display animated change in net income graph with enhanced hover data
fig7_change = create_animated_change_graph(option7_df, "Option 7: Eliminate Bonus Senior Deduction")
fig7_change.show()
Loading...

Option 8: Full Taxation of Social Security Benefits

This option taxes 100% of Social Security benefits for all recipients, regardless of income level. This is the most comprehensive expansion of Social Security benefit taxation, treating benefits identically to wages and other ordinary income.

Source
# Option 8: Full Taxation - Visualization
option8_df = combined_df[combined_df['reform'] == "Full Taxation of Social Security Benefits"].copy()

# Create and display animated change in net income graph with enhanced hover data
fig8_change = create_animated_change_graph(option8_df, "Option 8: 100% Taxation")
fig8_change.show()
Loading...