US Macroeconomic Data Explorer | Consumer & Business Confidence

Consumer & Business Confidence


A look at the consumer and business confidence indexes, according to the OECD.

In [1]:
import pandas as pd
import altair as alt

from urllib.request import urlopen, Request
from io import BytesIO
In [2]:
baseUrl = "https://stats.oecd.org/sdmx-json/data/DP_LIVE/{}/OECD?contentType=csv&detail=code&separator=comma&csv-lang=en"

uaString = 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_9_3) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/35.0.1916.47 Safari/537.36'

def fetchSentiment(url, retries=5):
    if retries < 0:
        return None
    try:
        req = Request(url, data=None, headers={ 'User-Agent': uaString })

        data = urlopen(req)
        
        return pd.read_csv(BytesIO(data.read()))
    except:
        return fetch(url, retries-1)

df_consumer = fetchSentiment(baseUrl.format("USA.CCI..."))

#df_consumer.head()

df_business = fetchSentiment(baseUrl.format(".BCI..."))

#df_business.head()

What is the current US consumer sentiment?

According to the OECD

Above 100:

  • signals a boost in confidence towards the future economic situation
  • less prone to save, and more inclined to spend money on major purchases in the next 12 months

Below 100:

  • indicate a pessimistic attitude towards future developments in the economy
  • a tendency to save more and consume less
In [3]:
cUsaSubset = df_consumer[df_consumer['LOCATION'] == 'USA'][['TIME', 'Value']]

def chartFor(df, label, color='darkseagreen'):
    return alt.Chart(df).mark_bar(size=2, color=f'{color}').transform_calculate(
        label=f"'{label}'",
        delta="datum['Value'] - 100"
    ).encode(
        alt.X('TIME:T', axis=alt.Axis(title='')),
        alt.Y('delta:Q', axis=alt.Axis(title='Index Value [Deviation from 100]', grid=False)),
        #alt.Opacity('label:N', legend=alt.Legend(title="")),
        color=alt.condition("datum['delta'] < 0",
            alt.value('indianred'),
            alt.value(color)
        ),
        tooltip=[alt.Tooltip('TIME:T', title='Date'), alt.Tooltip(f'delta:Q', title='Deviation', format=',.02f')]
    ).properties(
        title=f"OECD {label} Sentimemt Index",
        width=750,
        height=400
    )

consumer = chartFor(cUsaSubset.copy(), 'Consumer')

consumer.display()
/usr/share/miniconda/envs/ci/lib/python3.9/site-packages/altair/utils/core.py:317: FutureWarning: iteritems is deprecated and will be removed in a future version. Use .items instead.
  for col_name, dtype in df.dtypes.iteritems():

What is the current US business sentiment?

According to the OECD

Above 100:

  • increased confidence in near future business performance

Below 100:

  • indicate pessimism towards future business performance
In [4]:
bUsaSubset = df_business[df_business['LOCATION'] == 'USA'][['TIME', 'Value']]

business = chartFor(bUsaSubset, 'Business', 'steelblue')

business.display()
/usr/share/miniconda/envs/ci/lib/python3.9/site-packages/altair/utils/core.py:317: FutureWarning: iteritems is deprecated and will be removed in a future version. Use .items instead.
  for col_name, dtype in df.dtypes.iteritems():
In [5]:
step = 150
overlap = 1

def doRidgeLineFor(df, x, y, row, title='US Sentiment for Consumers and Businesses [OECD]'):
    return alt.Chart(df, height=step).transform_calculate(
        delta='datum.value - 100'
    ).transform_joinaggregate(
        mean_val=f'mean({x})', groupby=[row],
    ).mark_area(
        interpolate='monotone',
        fillOpacity=0.8,
        stroke='lightgray',
        strokeWidth=0.5
    ).encode(
        alt.X(f'{x}:T', title=''),
        alt.Y(
            f'{y}:Q',
            scale=alt.Scale(range=[step, -step * overlap]),
            axis=None
        ),
        alt.Fill(
            f'{row}:N',
            title='',
            #legend=None,
            scale=alt.Scale(scheme='dark2')
        ),
        tooltip=[alt.Tooltip(f'{x}:T'), alt.Tooltip(f'{row}:N'), alt.Tooltip(f'{y}:Q', format=',.02f')]
    ).facet(
        row=alt.Row(
            f'{row}:N',
            title=None,
            header=alt.Header(labelColor='white') #, labelAnchor='end')
        )
    ).properties(
        title=title,
        bounds='flush'
    ).configure_facet(
        spacing=5
    ).configure_view(
        stroke=None,
        width=650
    ).configure_title(
        anchor='middle'
    )

Consumer and business sentiment next to eachother...

In [6]:
cUsaSubset['Type'] = cUsaSubset.index.map(lambda v: 'Consumer')
bUsaSubset['Type'] = bUsaSubset.index.map(lambda v: 'Business')

merged = pd.concat([cUsaSubset, bUsaSubset]).melt(id_vars=['TIME', 'Type'])

#c = 
doRidgeLineFor(merged[['TIME', 'Type', 'value']].dropna(), 'TIME', 'delta', 'Type')
/usr/share/miniconda/envs/ci/lib/python3.9/site-packages/altair/utils/core.py:317: FutureWarning: iteritems is deprecated and will be removed in a future version. Use .items instead.
  for col_name, dtype in df.dtypes.iteritems():
Out[6]:

Has consumer sentiment gotten better or worse compared to the same time last year?

In [7]:
cUsaYoy = cUsaSubset.set_index('TIME')['Value'].pct_change(12).apply(lambda v: v * 100).reset_index()
bUsaYoy = bUsaSubset.set_index('TIME')['Value'].pct_change(12).apply(lambda v: v * 100).reset_index()

def yoyChartFor(df, label, color='navy'):
    return alt.Chart(df.dropna()[-180:]).mark_bar(width=1.5, color=f'{color}').transform_calculate(
        label=f"'{label}'",
        change_in_percent="format(datum.Value, ',.2f')",
    ).encode(
        alt.X('TIME:T', axis=alt.Axis(title='')),
        alt.Y('Value:Q', axis=alt.Axis(title='Year-over-Year Change [%]', grid=False)),
        #alt.Opacity('label:O', legend=alt.Legend(title="")),
        color=alt.condition("datum['Value'] < 0",
            alt.value('darkred'),
            alt.value(color)
        ),
        tooltip=[alt.Tooltip('TIME:T', title='Date', format='%b %Y'), alt.Tooltip('label:O'), alt.Tooltip('change_in_percent:O')]
    ).properties(
        title=f"Changing {label} Sentiment [OECD]",
        width=750,
        height=400
    )

cYoy = yoyChartFor(cUsaYoy, 'Consumer', 'green')
cYoy.display()
/usr/share/miniconda/envs/ci/lib/python3.9/site-packages/altair/utils/core.py:317: FutureWarning: iteritems is deprecated and will be removed in a future version. Use .items instead.
  for col_name, dtype in df.dtypes.iteritems():

Has business sentiment gotten better or worse compared to the same time last year?

In [8]:
bYoy = yoyChartFor(bUsaYoy, 'Business', 'steelblue')
bYoy.display()

Consumer and business sentiment year-over-year changes next to eachother...

In [9]:
merged['change'] = merged.set_index('TIME')['value'].pct_change(12).apply(lambda v: v*100).values

c = doRidgeLineFor(merged[['TIME', 'Type', 'change']].dropna(), 'TIME', 'change', 'Type', title='Changing US Sentiment [OECD]')

#c = (cYoy + bYoy).properties(title='Changing US Sentiment [OECD]', background='white')

c.save('sentiment.png')
c.display()
/usr/share/miniconda/envs/ci/lib/python3.9/site-packages/altair/utils/core.py:317: FutureWarning: iteritems is deprecated and will be removed in a future version. Use .items instead.
  for col_name, dtype in df.dtypes.iteritems():
/usr/share/miniconda/envs/ci/lib/python3.9/site-packages/altair/utils/core.py:317: FutureWarning: iteritems is deprecated and will be removed in a future version. Use .items instead.
  for col_name, dtype in df.dtypes.iteritems():

© kdunn926