Today we will study the charts in the article The Rich Cut Their Spending. That Has Hurt All the Workers Who Count on It. These charts tell us something very important about how the spending has been cut differently across different classes.

consumer spending small businesses low wage workers

Note: The vertical lines correspond to the following dates -

  • First stimulus checks - April 17
  • States in the process of reopening - May 1

The data for this analysis is taken from Opportunity Labs where they publish their data in this dashboard.

What's important about this data is best summed up by -

One of the things this crisis has made salient is how interdependent our health was, said Michael Stepner, an economist at the University of Toronto. We’re seeing the mirror of that on the economic side.

#hide_output
import pandas as pd
import altair as alt
alt.renderers.set_embed_options(actions=False)

Drop in consumer spending

The rich drive more of the economy than they did 50 years ago. And more workers depend on them.

For the highest-income quarter, spending has recovered much more slowly, after falling by 36 percent at the lowest point.

Important: We will use data till July only, so the uri used for the data is for the commit of a particular day. If you want to use the latest data then replace the spending_uri with this - ’https://raw.githubusercontent.com/OpportunityInsights/EconomicTracker/main/data/Affinity%20-%20National%20-%20Daily.csv’

spending_uri = 'https://raw.githubusercontent.com/OpportunityInsights/EconomicTracker/8d9fae46fab3e386a8f4ce798de09a016cbda0f9/data/Affinity%20-%20National%20-%20Daily.csv'
#spending_uri = 'https://raw.githubusercontent.com/Opportunitylab/EconomicTracker/main/data/Affinity%20-%20National%20-%20Weekly.csv' # for latest data

spending = pd.read_csv(spending_uri)
spending.head()
year month day spend_acf spend_aer spend_all spend_all_inchigh spend_all_inclow spend_all_incmiddle spend_apg spend_grf spend_hcs spend_tws
0 2020 1 24 -0.00510 -0.02360 -0.006440 -0.005790 -0.00752 -0.00654 -0.00952 -0.00954 -0.00328 -0.005840
1 2020 1 25 0.00202 -0.01820 0.000432 -0.000625 -0.00201 0.00199 0.00400 0.00991 -0.00469 0.000839
2 2020 1 26 -0.00896 -0.02220 -0.002710 -0.000425 -0.00668 -0.00315 0.00152 0.01920 -0.00647 0.002720
3 2020 1 27 -0.01350 -0.00762 -0.012200 -0.011000 -0.01590 -0.01190 -0.00671 -0.00980 -0.00755 -0.015700
4 2020 1 28 -0.01550 -0.01270 -0.013700 -0.013300 -0.01630 -0.01320 -0.00492 -0.01820 -0.00243 -0.009870
def add_format_date(df):
    df['date'] = df['year'].astype(str) + '-' + df['month'].astype(str) + '-' + df['day'].astype(str)
    df['date'] = pd.to_datetime(df['date'], format="%Y-%m-%d")
    return df
spending = spending.pipe(add_format_date)
spending.head()
year month day spend_acf spend_aer spend_all spend_all_inchigh spend_all_inclow spend_all_incmiddle spend_apg spend_grf spend_hcs spend_tws date
0 2020 1 24 -0.00510 -0.02360 -0.006440 -0.005790 -0.00752 -0.00654 -0.00952 -0.00954 -0.00328 -0.005840 2020-01-24
1 2020 1 25 0.00202 -0.01820 0.000432 -0.000625 -0.00201 0.00199 0.00400 0.00991 -0.00469 0.000839 2020-01-25
2 2020 1 26 -0.00896 -0.02220 -0.002710 -0.000425 -0.00668 -0.00315 0.00152 0.01920 -0.00647 0.002720 2020-01-26
3 2020 1 27 -0.01350 -0.00762 -0.012200 -0.011000 -0.01590 -0.01190 -0.00671 -0.00980 -0.00755 -0.015700 2020-01-27
4 2020 1 28 -0.01550 -0.01270 -0.013700 -0.013300 -0.01630 -0.01320 -0.00492 -0.01820 -0.00243 -0.009870 2020-01-28

Plotting the data -

base=alt.Chart(spending).transform_fold(['spend_all_inchigh', 'spend_all_inclow', 'spend_all_incmiddle']).transform_filter(alt.datum.date > alt.expr.toDate('2020-02-14')).mark_line().encode(
    x=alt.X('date:T', title=None, axis=alt.Axis(format="%b%e", tickCount=5, labelOffset=0, tickOffset=0, labelPadding=25, ticks=False)),
    #y='spend_all_inchigh:Q',
    #x2='date:Q',
    y=alt.Y('value:Q', title=None, axis=alt.Axis(format="%", tickCount=10)),
    color='key:N'
    #detail='date'
).properties(width=900, height=600)

lines={'lines': ['2020-04-15', '2020-05-01'], 'y1': [0,0], 'y2': [-0.4, -0.4]}
lines1={'lines': ['2020-04-15'], 'text': ['First stimulus \n checks received'], 'y': [-0.03]}
lines2={'lines': ['2020-05-01'], 'text': ['Half of states in \n process of reopening'], 'y': [-0.03]}

vert_line = alt.Chart(pd.DataFrame(lines)).mark_rule(strokeDash=[5,5], stroke='grey').encode(
    x='lines:T',
    y=alt.Y('y1:Q', scale=alt.Scale(zero=False)),
    #y2=alt.Y2('y2:Q')
)
text1 = alt.Chart(pd.DataFrame(lines1)).mark_text(lineBreak='\n', dx=-10, align='right').encode(
    text = 'text:N',
    y = 'y:Q',
    x = 'lines:T'
)
text2 = alt.Chart(pd.DataFrame(lines2)).mark_text(lineBreak='\n',dx=10, align='left').encode(
    text = 'text:N',
    y = 'y:Q',
    x = 'lines:T',
)
alt.layer(base, vert_line, text1, text2).configure_view(strokeWidth=0).configure_axis(grid=False).configure_axisX(orient='top', offset=-67)

We can use the same techniques for the vertical lines and text overlay as the chart above in the following charts. Since the idea is similar I will not implement them for all, instead just plot the line charts.

Small businesses in the richest neighborhoods have had the biggest drops in revenue

Note: Latest data from now on

revenue_uri = 'https://raw.githubusercontent.com/Opportunitylab/EconomicTracker/main/data/Womply%20Revenue%20-%20National%20-%20Daily.csv'
revenue = pd.read_csv(revenue_uri)
revenue = revenue.pipe(add_format_date)
revenue.head()
year month day revenue_all revenue_inchigh revenue_inclow revenue_incmiddle revenue_ss40 revenue_ss65 revenue_ss70 date
0 2020 1 10 -0.01170 -0.00490 -0.0274 -0.00860 -0.00999 -0.03060 -0.0162 2020-01-10
1 2020 1 11 -0.00348 0.00729 -0.0222 -0.00302 -0.00692 -0.00958 -0.0138 2020-01-11
2 2020 1 12 0.00195 0.00998 -0.0175 0.00372 -0.00488 0.00284 -0.0117 2020-01-12
3 2020 1 13 -0.01350 -0.00551 -0.0481 -0.00665 -0.00459 -0.04560 -0.0217 2020-01-13
4 2020 1 14 -0.00138 0.00071 -0.0231 0.00392 -0.00174 0.00809 -0.0168 2020-01-14
alt.Chart(revenue).mark_line().transform_fold(['revenue_inclow', 'revenue_incmiddle', 'revenue_inchigh']).transform_filter(alt.datum.date > alt.expr.toDate('2020-02-14')).encode(
    x='date:T',
    y= 'value:Q',
    color= 'key:N'
)

Low-wage workers in the richest neighborhoods have had the biggest drop in earnings

Important: The file for this data has been removed and not updated since July. So we will use the data from the particular commit that had this file.

earning_uri = 'https://raw.githubusercontent.com/OpportunityInsights/EconomicTracker/5f914ee4e71f56a33857b63e0bd07d71bc31e847/data/Low%20Inc%20Earnings%20Small%20Businesses%20-%20National%20-%20Daily.csv'
earning = pd.read_csv(earning_uri)
earning = earning.pipe(add_format_date)
earning.head()
year month day pay pay31_33 pay44_45 pay48_49 pay62 pay72 pay_inclow pay_incmiddle pay_inchigh date
0 2020 1 1 . . . . . . . . . 2020-01-01
1 2020 1 2 . . . . . . . . . 2020-01-02
2 2020 1 3 . . . . . . . . . 2020-01-03
3 2020 1 4 . . . . . . . . . 2020-01-04
4 2020 1 5 . . . . . . . . . 2020-01-05
alt.Chart(earning).mark_line().transform_fold(['pay', 'pay_inclow', 'pay_incmiddle', 'pay_inchigh']).encode(
    x='date:T',
    y= 'value:Q',
    color= 'key:N'
)

Low-wage workers in the richest neighborhoods have had the biggest drop in employment

Important: This file was also eventually removed. So we will use the file from the commit that still had this file-

#employment_uri = 'https://raw.githubusercontent.com/Opportunitylab/EconomicTracker/main/data/Low%20Inc%20Emp%20Small%20Businesses%20-%20National%20-%20Daily.csv' # original file
employment_uri = 'https://raw.githubusercontent.com/OpportunityInsights/EconomicTracker/ba8c0096efb873d90f10cd720576c4ec5e6fc42e/data/Low%20Inc%20Emp%20Small%20Businesses%20-%20National%20-%20Daily.csv'
employment = pd.read_csv(employment_uri)
employment = employment.pipe(add_format_date)
employment.head()
year month day emp emp31_33 emp44_45 emp48_49 emp62 emp72 emp_inclow emp_incmiddle emp_inchigh date
0 2020 1 1 . . . . . . . . . 2020-01-01
1 2020 1 2 . . . . . . . . . 2020-01-02
2 2020 1 3 . . . . . . . . . 2020-01-03
3 2020 1 4 . . . . . . . . . 2020-01-04
4 2020 1 5 . . . . . . . . . 2020-01-05
alt.Chart(employment).mark_line().transform_fold(['emp_inclow', 'emp_incmiddle', 'emp_inchigh']).encode(
    x='date:T',
    y= 'value:Q',
    color= 'key:N'
)