download notebook
view notebook w/o solutions
Measuring Thanksgiving-dinner inflation
Thanksgiving is a time to reflect on all the reasons to be grateful. You might be grateful that your live in a world of smartphones and antibiotics. You might be grateful that Gibraltar rock is only a short drive from Madison. You might be grateful for your friends and your family. Maybe you are grateful for your econ instructors...
Thanksgiving is also a time to gather for a meal. It doesn't really matter what you are eating as long as you are doing it with others in the spirit of giving thanks. The traditional American Thanksgiving meal, though, includes turkey, stuffing, potatoes, vegetables, cheese (at least, it does in Wisconsin) and maybe some wine to wash it all down.
How much has the cost of Thanksgiving dinner changed over the years? If dinner for four cost $50 in 2015, what would it cost today? Which components of dinner have seen the largest price changes?
Let's figure this out by computing a fixed-weight price index, like the cpi. We need prices and expenditure weights. The change in the price of dinner (the Thanksgiving-dinner inflation rate) is
where the \(\omega\) are the fixed expenditure weights that sum to one. The expenditure weights are determined in a base period and held constant over time. By introspection, I've determined the weights in a Thanksgiving dinner to be
exp_weights = {'turkey':0.3, 'bread':0.07, 'butter':0.03, 'potatoes':0.15, 'wine':0.23, 'cheese':0.12, 'vegetables':0.1},
which means 30 percent of the cost of dinner is the turkey, 7 percent is bread, etc.
We have weights, so now we just need the inflation rates of the components. Let's use the data from the Bureau of Labor Statistics cpi databases. These are the "all urban consumers" series. You could use the online access to download a bunch of csv files, but we know how to work with apis, so let's approach this like the pros do. I've already dug out the codes for each variable.
An aside on price indexes
There are different types of price indexes out there, including fixed-weight and fixed-price indexes and the chained Fisher index used at the Bureau of Economic Analysis. We have to choose one. Our fixed-weight index is similar to the cpi.
An issue with fixed-weight price indexes is substitution bias. Goods that become relatively more expensive could find their expenditure shares falling (if goods are substitutes), so our fixed shares would overstate inflation. Unfortunately, there is not a perfect price index that is easy to compute, so we do the best we can, just like they do with the cpi.
import pandas as pd
import requests
import json
import matplotlib.pyplot as plt
import seaborn as sns
1. Get the data
We are going to use the BLS api to retrieve the price indexes. This way, when you want to update your work next year to include the most recent data, all you will need to do is re-run the notebook. Nice.
I started with the BLS api docs and the sample code in python.
- Retrieve data for 2015–2023. The code for each good is in the dictionary below.
dinner = {'CUSR0000SEFF02':'turkey', 'CUSR0000SEFB01':'bread', 'CUSR0000SS10011':'butter', 'CUSR0000SEFL01':'potatoes',
'CUSR0000SEFW03':'wine', 'CUSR0000SEFJ02':'cheese', 'CUSR0000SEFL04':'vegetables'}
```
```python
dinner = {'CUSR0000SEFF02':'turkey', 'CUSR0000SEFB01':'bread', 'CUSR0000SS10011':'butter', 'CUSR0000SEFL01':'potatoes',
'CUSR0000SEFW03':'wine', 'CUSR0000SEFJ02':'cheese', 'CUSR0000SEFL04':'vegetables'}
headers = {'Content-type': 'application/json'}
data = json.dumps({"seriesid": list(dinner.keys()),"startyear":"2015", "endyear":"2023"})
p = requests.post('https://api.bls.gov/publicAPI/v2/timeseries/data/', data=data, headers=headers)
json_data = json.loads(p.text)
Once you have downloaded the data, you have a json object. This is basically nested dictionaries and lists to be sorted through. I wrestled with this for awhile, but it's doable. You can do it, too.
- Create a DataFrame with time as rows and the price indexes for each good as columns.
df = pd.DataFrame()
for s in json_data['Results']['series']:
temp = pd.DataFrame(s['data'])
temp = temp.set_index(pd.to_datetime(temp['year'].astype(str) + '/' + temp['periodName'])).sort_index()
temp = temp[['value']].astype(float).copy()
temp['var'] = s['seriesID']
df = pd.concat([df, temp])
df = df.reset_index().set_index(['index','var']).unstack('var')
df.columns = df.columns.droplevel(0)
df.rename(columns=dinner, inplace=True)
df.head(2)
var | bread | turkey | cheese | potatoes | vegetables | wine | butter |
---|---|---|---|---|---|---|---|
index | |||||||
2015-01-01 | 177.851 | 154.611 | 238.322 | 329.881 | 321.445 | 168.716 | 239.560 |
2015-02-01 | 177.640 | 156.360 | 237.470 | 325.725 | 318.984 | 169.160 | 234.432 |
2. Compute inflation
Now that we have the data, let's compute the inflation rate. The inflation rate is just the growth rate of the price indexes.
- Compute the year-over-year growth rate of each price index.
# The year over year growth rate helps with seasonality.
# We compare June with June, December, with December.
df = df.pct_change(periods=12)
- Now compute the Thanksgiving dinner price index. In each year, compute the weighted sum of the inflation rates, where \(\pi\) are the inflation rates and \(\omega\) are the expenditure weights. Notice that the \(\pi\) change over time, but the weights do not.
The weights are
exp_weights = {'turkey':0.3, 'bread':0.07, 'butter':0.03, 'potatoes':0.15, 'wine':0.23, 'cheese':0.12, 'vegetables':0.1}
exp_weights = {'turkey':0.3, 'bread':0.07, 'butter':0.03, 'potatoes':0.15, 'wine':0.23, 'cheese':0.12, 'vegetables':0.1}
df['dinner'] = 0
for c in dinner.values():
df['dinner'] = df['dinner'] + df[c]*exp_weights[c]
df.tail(3)
var | bread | turkey | cheese | potatoes | vegetables | wine | butter | dinner |
---|---|---|---|---|---|---|---|---|
index | ||||||||
2023-08-01 | 0.063282 | 0.065360 | -0.017470 | 0.030629 | 0.008632 | 0.009548 | -0.048080 | 0.028153 |
2023-09-01 | 0.061361 | 0.066738 | -0.027896 | -0.021135 | -0.005792 | 0.013603 | -0.040317 | 0.019139 |
2023-10-01 | 0.062749 | 0.072471 | -0.018689 | -0.033369 | -0.007097 | 0.011079 | -0.036799 | 0.019620 |
- Plot your Thanksgiving dinner inflation rate. I didn't do it, but you could add the overall cpi or the cpi for food to see how it compares to Thanksgiving.
fig, ax = plt.subplots()
ax.plot(df.index, df.dinner*100, color='black')
ax.axhline(y=0, color='black', lw=0.75)
ax.set_ylabel('growth rate of Thanksgiving price (yoy, percent)')
ax.set_title('Thankgiving-dinner inflation rate')
sns.despine()
plt.show()
3. Compute cumulative inflation
How much did inflation change over the whole period? Let's compute cumulative inflation.
- We computed year-over-year inflation rates (an annual rate), so we should only use one observation from each year. Let's use October. Create a DataFrame that only has the October observation from each year.
- Add 1 to each growth rate. If you multiplied your growth rates by 100, divide them by 100 before adding 1.
- Compute the cumulative product of each column.
- Set the 2015 row (which is NA) to be ones.
The last row of the DataFrame will be one plus the cumulative growth rate.
octs = df.loc[df.index.month==10]
octs = octs + 1
cum_octs = octs.cumprod()
cum_octs.loc['2015-10-01', :] = 1
cum_octs
var | bread | turkey | cheese | potatoes | vegetables | wine | butter | dinner |
---|---|---|---|---|---|---|---|---|
index | ||||||||
2015-10-01 | 1.000000 | 1.000000 | 1.000000 | 1.000000 | 1.000000 | 1.000000 | 1.000000 | 1.000000 |
2016-10-01 | 0.995837 | 0.984884 | 0.981920 | 1.002508 | 0.966988 | 0.995812 | 0.939618 | 0.987304 |
2017-10-01 | 0.987601 | 0.954277 | 0.989903 | 1.036020 | 1.010130 | 0.999716 | 0.982078 | 0.990075 |
2018-10-01 | 1.002751 | 0.904980 | 0.980292 | 1.041596 | 1.022387 | 1.012458 | 0.991434 | 0.979828 |
2019-10-01 | 1.033201 | 0.936869 | 0.998454 | 1.132857 | 1.026631 | 1.020936 | 0.998922 | 1.009840 |
2020-10-01 | 1.080413 | 1.043045 | 1.044114 | 1.134313 | 1.048719 | 1.031060 | 1.008355 | 1.057902 |
2021-10-01 | 1.105127 | 1.060687 | 1.037380 | 1.152287 | 1.076338 | 1.034887 | 1.017565 | 1.070639 |
2022-10-01 | 1.268436 | 1.240218 | 1.165763 | 1.326077 | 1.146047 | 1.068323 | 1.289501 | 1.199673 |
2023-10-01 | 1.348029 | 1.330097 | 1.143977 | 1.281827 | 1.137914 | 1.080159 | 1.242049 | 1.223210 |
4. So how much more is that?
Suppose dinner for a family of four was $50 in 2015. What does that dinner cost in 2023?
costs = cum_octs['dinner'] * 50
'Dinner for four in 2023 costs ${:.2f}.'.format(costs.loc['2023-10-01'])
'Dinner for four in 2023 costs $61.16.'
fig, ax = plt.subplots()
ax.plot(cum_octs.index, cum_octs['dinner']*50, color='black')
ax.set_title('Cost of Thanksgiving for a family of four')
ax.set_ylabel('dollars')
sns.despine()
plt.show()
5. What goods drove the increase in prices?
Why is Thanksgiving dinner 61 percent more expensive than it was in 2015? Which parts of dinner had the largest price increases?
- Create a bar plot with the cumulative inflation rates for each of the components of dinner, in October 2023.
# I made my figure orange and brown because I think of Thanksgiving as being orange and brown.
# By assigning the output of ax.barh() to a variable, I capture the bars.
fig, ax = plt.subplots()
cum_octs.rename(columns={'dinner':'total cost of dinner'}, inplace=True)
bars = ax.barh(cum_octs.columns, ((cum_octs.loc['2023-10-01']-1)*100).sort_values(), color='orange')
bars[7].set_color('saddlebrown')
ax.set_title('Cumulative growth of Thanksgiving prices (annual, percent)')
ax.grid(axis='x', color='white')
sns.despine()
plt.show()
It is interesting that cheese and butter have such different inflation rates. It must not be driven by the cost of milk.
- You could continue your analysis by plotting the contributions of each component. For example, butter has a high inflation rate, but its weight is only three percent. I'll leave that for you to experiment with!