Income sources#

This chart, mirroring Figure 12 of the UK’s Households Below Average Income poverty publication, shows the breakdown of income sources for each income decile.

from policyengine_us import Microsimulation
import pandas as pd
import numpy as np

sim = Microsimulation()

household_benefits = sim.calculate("household_benefits")
household_pensions = sim.calculate("pension_income", map_to="household")
household_investment_income = sim.calculate(
    "net_investment_income", map_to="household"
)
household_earnings = sim.calculate(
    "employment_income", map_to="household"
) + sim.calculate("self_employment_income", map_to="household")
total_income = (
    household_benefits
    + household_pensions
    + household_investment_income
    + household_earnings
)

equivalised_income = sim.calculate("equiv_household_net_income")
household_count_people = sim.calculate("people", map_to="household")
equivalised_income.weights *= household_count_people.values
household_income_percentile = equivalised_income.percentile_rank()

income_source_decodes = {
    "Earnings": household_earnings,
    "Pensions": household_pensions,
    "Investment": household_investment_income,
    "State support": household_benefits,
}

percentiles = []
values = []
income_sources = []

for percentile in range(1, 101):
    in_decile = household_income_percentile == percentile
    cumulative_income = 0
    for income_source in income_source_decodes:
        percentiles.append(percentile)
        income_sources.append(income_source)
        income_source_values = income_source_decodes[income_source]
        values.append(
            np.maximum(income_source_values[in_decile].sum(), 0)
            / total_income[in_decile].sum()
        )
        cumulative_income += np.maximum(
            income_source_values[in_decile].sum(), 0
        )
    # Add 'other income'
    percentiles.append(percentile)
    income_sources.append("Other")
    values.append(1 - cumulative_income / total_income[in_decile].sum())

df = pd.DataFrame(
    {
        "Percentile": percentiles,
        "Income source": income_sources,
        "Value": values,
    }
)

# Order by state support, other income, pensions, investment, earnings
df["Income source"] = pd.Categorical(
    df["Income source"],
    ["State support", "Other", "Pensions", "Investment", "Earnings"],
)
df = df.sort_values(["Percentile", "Income source"], ascending=[True, False])

import plotly.express as px
from policyengine_core.charts import format_fig

fig = px.bar(
    df,
    x="Percentile",
    y="Value",
    color="Income source",
).update_layout(
    height=600,
    width=800,
    # No bar gap
    bargap=0,
    # No space between bars
    bargroupgap=0,
    yaxis=dict(
        tickformat=".0%",
        title="Percentage of income",
        tickvals=[0, 0.1, 0.2, 0.3, 0.4, 0.5, 0.6, 0.7, 0.8, 0.9, 1],
    ),
    xaxis=dict(
        title="Income percentile",
        tickvals=list(range(1, 101, 10)),
    ),
)

fig = format_fig(fig).update_layout(
    title="Sources of income for pensioner households",
)
fig