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 plotly.express as px
from policyengine_core.charts import format_fig

sim = Microsimulation()
period = 2026

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

household_income_decile = sim.calc(
    "equiv_household_net_income", period=period
).decile_rank()

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

deciles = []
values = []
income_sources = []

for decile in range(1, 11):
    in_decile = household_income_decile == decile
    decile_total_income = total_income[in_decile].sum()
    cumulative_income = 0

    for income_source, income_source_values in income_source_decodes.items():
        deciles.append(decile)
        income_sources.append(income_source)
        income_source_total = max(income_source_values[in_decile].sum(), 0)
        values.append(income_source_total / decile_total_income)
        cumulative_income += income_source_total

    deciles.append(decile)
    income_sources.append("Other")
    values.append(1 - cumulative_income / decile_total_income)

df = pd.DataFrame(
    {
        "Decile": deciles,
        "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(["Decile", "Income source"], ascending=[True, False])

fig = px.bar(
    df,
    x="Decile",
    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 decile",
        tickvals=list(range(1, 11)),
    ),
)

fig = format_fig(fig).update_layout(
    title="Sources of income by household income decile",
)
fig