1 in 5 Americans with disabilities is in poverty. Even small basic incomes would help.

By Nate Golden and Max Ghenis, 2020-07-31

As the Americans with Disabilities Act reaches its 30th anniversary, we wanted to look beyond the workplace to poverty, safety net programs, and basic income. Today, more than 1 in 5 Americans with disabilities live below the poverty threshold, nearly twice the rate of Americans without disabilities.

While America provides some income support to its citizens with disabilities through Supplemental Security Income (SSI) and Social Security Disability Insurance (SSDI), these programs inevitably leave many out. Today, 22% of households with people with disabilities do not receive any federal government assistance. Further, just 14% of households of families where someone is disabled collected SSI, a program “designed to help aged, blind, and disabled people, who have little or no income.” Unemployment insurance was the only program in our analysis that was more likely to go to families without people with disabilities

### LOAD PACKAGES ####

import pandas as pd
import plotly.express as px
import microdf as mdf
import numpy as np

### COLORS ###

# Colors from https://material.io/design/color/the-color-system.html
BLUE = '#1976D2'
DARK_BLUE = '#0D47A1'
BARELY_BLUE = '#E3F2FD'
GRAY = '#BDBDBD'


### LOAD DATA ###

person_raw = pd.read_csv('https://github.com/MaxGhenis/datarepo/raw/master/pppub19.csv.gz',
                         usecols=['MARSUPWT', 'SPM_ID', 'SPM_RESOURCES',
                                  'SPM_POVTHRESHOLD', 'A_AGE', 'PEDISDRS',
                                  'PEDISEAR', 'PEDISEYE', 'PEDISOUT',
                                  'PEDISPHY', 'PEDISREM', 'SPM_NUMPER',
                                  'SS_VAL', 'SSI_VAL', 'SPM_SNAPSUB',
                                  'PAW_TYP', 'SPM_CAPHOUSESUB', 'UC_VAL'])

### PREPROCESS ###

person = person_raw.copy(deep = True)
person.columns = person.columns.str.lower()
person['weight'] = person.marsupwt / 100
# Create a new dataframe that only includes adults.
adults = person[person['a_age'] > 17].copy(deep=True)

# Create a column that shows if people have any disabilities.
adults['any_disability'] = ((adults.pedisdrs == 1) | 
                            (adults.pedisear == 1) | 
                            (adults.pediseye == 1) |
                            (adults.pedisout == 1) | 
                            (adults.pedisphy == 1) |
                            (adults.pedisrem == 1))

# Map the ASEC codes to disability types.
PWD_MAP = ({'pedisdrs': 'Self-care',
            'pedisear': 'Hearing',
            'pediseye': 'Vision',
            'pedisout': 'Mobility',
            'pedisphy': 'Physical',
            'pedisrem': 'Cognitive',
            'any_disability': 'Any'
           })

# group individual questions by spm group
adults['got_tanf'] = np.where(adults.paw_typ == 1, 1, 0)
PERSON_SPM_COLS = ['ss_val', 'ssi_val', 'uc_val', 'got_tanf']
groups = adults.groupby('spm_id')[PERSON_SPM_COLS].sum()
groups.columns = ['spm_' + i for i in PERSON_SPM_COLS]
adults2 = adults.merge(groups, left_on='spm_id', right_index=True)
# Also look into OI_OFF column: Value 3 = AFDC

# All PWD records.
adults_pwd = adults2[adults2.any_disability]

# Note these columns don't represent anything consistent
# except that >0 values indicate that the SPM unit received it.

# create a columns map
BEN_MAP = ({'spm_ss_val': 'Social Security', 
            'spm_ssi_val': 'Supplemental Security Income',
            'spm_got_tanf': 'TANF',
            'spm_uc_val': 'Unemployment',
            'spm_snapsub': 'SNAP',
            'spm_caphousesub': 'Housing Assistance',
            'spm_any_ben': 'Any'
            })

# Sum up all columns except the last (which is any, itself).
adults2['spm_any_ben'] = adults2[list(BEN_MAP.keys())[:-1]].sum(axis=1)


# Create a function that calculates percent of people with disabilities
# receiving each benefit.
def percent_bens(ben_column, has_disability):
    target_adults = adults2[adults2.any_disability == has_disability]
    total_w_ben = ((target_adults[ben_column] > 0) * target_adults.weight)
    return ((total_w_ben.sum() / target_adults.weight.sum()) * 100).round(1)

def percent_bens_row(row):
    return percent_bens(row.ben_column, row.has_disability)

benefits = mdf.cartesian_product({'ben_column': BEN_MAP.keys(),
                                  'has_disability': [True, False]
                                  })

benefits['percentage'] = benefits.apply(percent_bens_row, axis=1)
benefits['benefit'] = benefits.ben_column.map(BEN_MAP)
benefits = (benefits.sort_values(['percentage','benefit'],
                               ascending=(True, True)))
benefits['disability_text'] = np.where(benefits.has_disability,
                                       'Has disability', 'No disability')

fig = px.bar(benefits, x='percentage', y='benefit', color='disability_text',
             labels={'percentage': 'Percent receiving benefit',
                      'benefit': 'Benefit'},
             title='Share of Americans in families receiving benefits',
             text='percentage',
             orientation='h',
             barmode='group',
             color_discrete_map={'Has disability': BLUE, 'No disability': GRAY}
)

fig.update_layout(barmode='group',
                  font=dict(family='Roboto'),
                  plot_bgcolor='white',
                  yaxis_title='',
                  legend_title='',
                  xaxis_ticksuffix='%',
                  legend={'traceorder': 'reversed'}
                 )

fig.update_traces(texttemplate='%{text}%')

fig.show(config={'displayModeBar': False})