A look at the consumer and business confidence indexes, according to the OECD.
import pandas as pd
import altair as alt
from urllib.request import urlopen, Request
from io import BytesIO
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()
According to the OECD
Above 100:
Below 100:
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()
According to the OECD
Above 100:
Below 100:
bUsaSubset = df_business[df_business['LOCATION'] == 'USA'][['TIME', 'Value']]
business = chartFor(bUsaSubset, 'Business', 'steelblue')
business.display()
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'
)
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')
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()
bYoy = yoyChartFor(bUsaYoy, 'Business', 'steelblue')
bYoy.display()
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()