Skip to article frontmatterSkip to article content
Site not loading correctly?

This may be due to an incorrect BASE_URL configuration. See the MyST Documentation for reference.

Phillips Curve

by Professor Throckmorton
for Intermediate Macro
W&M ECON 304
Slides

Summary

  1. Phillips Curve: negative relationship b/t inflation (πt\pi_t) and unemployment (utu_t),
    i.e., if central bank wants lower inflation, then the economy must make the sacrifice of higher unemployment

  2. Testing the Phillips Curve in the United States

    • 1960s: low and stable inflation, i.e., πe0corr(πt,ut)<0\pi^e \approx 0 \rightarrow corr(\pi_t,u_t) < 0

    • 1970 to 1995: big positive oil price shocks, ππeπ\uparrow \pi \rightarrow \uparrow \pi^e \rightarrow \uparrow \pi

    • 1996 to 2019: Fed achieves goal of πe2\pi^e \approx 2

  3. Phillips Curve can be expressed relative to the medium-run equilibrium

Starting from Labor Market

  • The Phillips Curve comes from the Labor Market

    W=PeF(u,z)P=(1+m)W\begin{gather*} W = P^e F(u,z) \\ P = (1+m)W \end{gather*}

    and the medium-run equilibrium determines the natural rate of unemployment.

  • Right now PP shows up, but we want relationship between π\pi and uu. Recall for Chapter 2

    πt=Pt/Pt11\begin{gather*} \pi_t = P_t/P_{t-1} -1 \end{gather*}

Adding time and the inflation rate

  1. Want: relationship b/t π\pi and uu

  2. Substitute WS into PS

    P=(1+m)PeF(u,z)\begin{gather*} P = (1+m)P^e F(u,z) \end{gather*}
  3. Add time subscripts and divide by Pt1P_{t-1}

    Pt/Pt1=(1+m)(Pte/Pt1)F(ut,z)\begin{gather*} P_t/P_{t-1} = (1+m)(P_t^e/P_{t-1}) F(u_t,z) \end{gather*}

    where mm and zz are constant w.r.t. time

  4. Note from definition of πt\pi_t that

    Pt/Pt1=1+πt\begin{gather*} P_t/P_{t-1} = 1 + \pi_t \end{gather*}

    and similarly

    Pte/Pt1=1+πte\begin{gather*} P_t^e/P_{t-1} = 1 + \pi_t^e \end{gather*}

Combine equations

  1. Combine results from steps 3. and 4.

    1+πt=(1+m)(1+πte)F(ut,z)\begin{gather*} 1 + \pi_t = (1+m)(1 + \pi_t^e) F(u_t,z) \end{gather*}

    That’s difficult to interpret. It is a nonlinear equation, e.g., πte\pi_t^e multiplies utu_t.

  2. What is worker bargaining power, F(ut,z)F(u_t,z)? Remember utworkerB.P.\uparrow u_t \rightarrow \downarrow worker B.P.. Let’s assume

    F(ut,z)=1αut+z\begin{gather*} F(u_t,z) = 1 - \alpha u_t + z \end{gather*}
  3. Substitute out worker bargaining power function

    1+πt=(1+m)(1+πte)(1αut+z)\begin{gather*} 1 + \pi_t = (1+m)(1 + \pi_t^e) (1 - \alpha u_t + z) \end{gather*}

    This is still nonlinear.

Approximate linearly

  • We’ll use natural logs to approximate the Phillips curve linearly. In disciplines that use dynamic models, e.g., economics, physics, and biology, this is known as log-linearizing.

  • Recall some facts about logs from math class:

    log(XY)=log(X)+log(Y)log(1+X)X if X is small\begin{gather*} \log(XY) = \log(X) + \log(Y) \\ \log(1+X) \approx X \textrm{ if $X$ is small} \end{gather*}

Apply properties of logs

  1. Apply log(XY)=log(X)+log(Y)\log(XY) = \log(X) + \log(Y)

    1+πt=(1+m)(1+πte)(1αut+z)log(1+πt)=log(1+m)+log(1+πte)+log(1αut+z)\begin{align*} 1 + \pi_t &= (1+m)(1 + \pi_t^e) (1 - \alpha u_t + z) \\ \rightarrow \log(1 + \pi_t) &= \log(1+m) + \log(1 + \pi_t^e) + \log(1 - \alpha u_t + z) \end{align*}
  2. Note 0.10<m<0.200.10 < m < 0.20. Also 0<π<0.100 < \pi < 0.10, and the same is true for πe\pi^e, uu, and zz, i.e., they are relatively small.

  3. Apply log(1+X)X\log(1+X) \approx X.

    πtπteαut+m+z\begin{gather*} \pi_t \approx \pi_t^e - \alpha u_t + m + z \end{gather*}

    This is equation 8.2 in Blanchard, Macroeconomics (9th Edition, 2025).

Phillips Curve (PC)

πtπteαut+m+z\pi_t \approx \pi_t^e - \alpha u_t + m + z
  • Properties of the Phillips Curve all relate to Labor Market

    • πteπt\uparrow \pi_t^e \rightarrow \uparrow \pi_t: If workers expect prices to rise, then they will demand higher wages, which leads firms to eventually raise prices. Thus the expectation is self-filling.

    • mπt\uparrow m \rightarrow \uparrow \pi_t: If firms markup of price above cost rises, then prices are growing faster.

    • zπt\uparrow z \rightarrow \uparrow \pi_t: Any policies that increase worker bargaining power will allow workers to demand high wages, driving up costs and prices.

An Experiment

  • We defined mm as the markup, but we can also interpret it as any non-labor costs.

  • In the 1970s United States, we saw a 5-fold increase in the price of oil.

  • That kind of shock is known as a “cost-push” shock, which is inflationary, and shifts up the PC.

  • If inflation remains high for too long, then people begin to expect higher inflation, which shifts up the PC again due to a wage-price spiral.

Source
# plot a downward sloping line with slope -1 and y-intercept 10
import matplotlib.pyplot as plt
import numpy as np
x = np.linspace(0, 10, 100)
y = 4 - x
plt.plot(x, y)
plt.xlabel('$u$')
plt.ylabel('$\\pi$')
# Move ylabel to top and rotate it vertically
plt.gca().yaxis.set_label_coords(-.05, .95)
plt.gca().yaxis.label.set_rotation(0)
# move xlabel to right
plt.gca().xaxis.set_label_coords(.95, -0.05)
# set plot dimensions to 6.5 inches by 4 inches 
plt.gcf().set_size_inches(6.5, 4)
# add label for intercept with text "$\pi^e + m + z$" at (0, 5)
plt.text(-.7, 5.5, 'intercept: \n $\\pi^e + m + z$', fontsize=10, verticalalignment='bottom')
# add label for intercept with text "$u_n$" at (5, 0)
plt.text(2, -.5, '$u_n$', fontsize=12, horizontalalignment='center')
# plot a line with intercept at 7 and slope -1
plt.plot(x, 6 - x, linestyle='--', color='red')
# plot vertical dashed line at x=3
plt.axvline(x=2, linestyle='--', color='black')
# add y label at 2 for "\pi_0"
plt.text(-0.2, 2, '$\\pi_0$', fontsize=12, verticalalignment='center')
# set lower y limit to 0 and lower x limit to 0
plt.xlim(left=0,right=4)
plt.ylim(bottom=0,top=7)
# add label for slope with text "slope: \alpha" at (3,1)
plt.text(3, .5, 'slope: $-\\alpha$', fontsize=10, verticalalignment='bottom', horizontalalignment='right')
# add up arrow between lines with text "cost-push shock"
plt.annotate('', xy=(3, 2.9), xytext=(3, 1.1), arrowprops=dict(arrowstyle='->'))
plt.text(3.1, 1.5, 'cost-push shock', fontsize=10, verticalalignment='center')
# add horizontal dashed line at y=2 from x= 0 to x=2
plt.hlines(y=2, xmin=0, xmax=2, colors='black', linestyles='--')
# add horizontal dashed line at y=4 from x= 0 to x=2
plt.hlines(y=4, xmin=0, xmax=2, colors='black', linestyles='--')
# add y label at 4 for "\pi_1"
plt.text(-0.2, 4, '$\\pi_1$', fontsize=12, verticalalignment='center')
# remove all tick labels
plt.xticks([]); plt.yticks([]);
<Figure size 650x400 with 1 Axes>
  • An increase in mm shifts up the PC

  • At unu_n, inflation rises from π0\pi_0 to π1\pi_1

  • We know from labor market that mun\uparrow m \rightarrow \uparrow u_n.

  • Thus α\alpha tells us how much uu must rise for inflation to return to π0\pi_0.

  • The PC would also shift up if πe\pi^e \uparrow

Testing the PC

The Phillips Curve is a testable hypothesis (i.e., linear regression)

πt=πteαut+m+z+residualt\begin{gather*} \pi_t = \pi_t^e - \alpha u_t + m + z +\textrm{residual}_t \end{gather*}

The test is whether α\alpha is statistically significantly positive when controlling for expected inflation.

  • πt\pi_t: current inflation

  • πte\pi_t^e: expected inflation (measured by market/survey data)

  • α\alpha: sensitivity of wages demanded to the business cycle

  • utu_t: current unemployment (summarizes the business cycle)

  • mm: markup of price over cost

  • zz: other things that affect worker bargaining power

Expected Inflation

  • In practice, there are two popular measures of expected inflation

    1. Survey data: ask people for their inflation forecasts, e.g., Philadelphia Fed Survey of Professional Forecasters

    2. Market data: observe difference in bond prices on nominal bonds and inflation-indexed bonds. Inflation-index bonds are officially called Treasury Inflation Protected Securities (TIPS).

  • In theory, we can simplify the model with an assumption about expected inflation

    1. Assume expected inflation is anchored to some value, πte=πˉ\pi_t^e = \bar{\pi}.

    2. Assume expected inflation is adapting to recent data, πte=πt1\pi_t^e = \pi_{t-1}.

Source
# Libraries
from fredapi import Fred
import pandas as pd
# Read data
fred_api_key = pd.read_csv('fred_api_key.txt', header=None).iloc[0,0]
fred = Fred(api_key=fred_api_key)
# get unemployment rate series
data = fred.get_series('UNRATE').to_frame(name='Unemployment Rate')
# append CPI inflation rate to data
data['CPI'] = fred.get_series('CPIAUCSL')
# reindex data to QE by using last value of quarter
data = data.resample('QE').last()
# calculate inflation rate as percentage change from same quarter last year
data['CPI Inflation Rate'] = data['CPI'].pct_change(periods=4) * 100

# plot unemployment rate and CPI inflation rate
fig, ax1 = plt.subplots(figsize=(8.5,4))
ax1.plot(data.index, data['Unemployment Rate'], '-b')
ax1.plot(data.index, data['CPI Inflation Rate'], '-r')
ax1.set_xlabel('Year')
# relocate legend to inside upper right and make horizontal
ax1.legend(['Unemployment Rate', 'CPI Inflation Rate'], loc='lower right', ncol=2)
# add grid lines
ax1.grid()
# add % to ylabels
ax1.yaxis.set_major_formatter(plt.FuncFormatter(lambda y, _: '{:.0f}%'.format(y)))
# set xlims to 1947-01-01 to 2025-9-30
ax1.set_xlim(pd.Timestamp('1949-04-01'), pd.Timestamp('2025-09-30'));
<Figure size 850x400 with 1 Axes>
  • Inflation is low and stable around 1% in early-mid 1960s.

  • It is safe to assume that at that time expected inflation was anchored to 1%.

The 1960s

  • Notice inflation in early 1960s is about 1%1\% (low and stable)

  • Let’s assume people are forming expectations given what they observe:

    πte=πˉ1%\begin{gather*} \pi_t^e = \bar{\pi} \approx 1\% \end{gather*}

    i.e., expectations are anchored at some value

  • Phillips curve becomes

    πt=πˉαut+m+z\begin{gather*} \pi_t = \bar{\pi} -\alpha u_t + m + z \end{gather*}
  • If the assumption that worker bargaining power is countercyclical, i.e., ut worker B.P.\uparrow u_t \rightarrow \downarrow \textrm{ worker } B.P., is good, then Phillips curve predicts:

    Corr(πt,ut)<0\begin{gather*} \textrm{Corr}(\pi_t,u_t) < 0 \end{gather*}
Source
# take data 2 and resample to annual frequency using average
data_annual = data.resample('YE').mean()
# create subset of data_annual for years 1960-1969
data_annual_1960s = data_annual[(data_annual.index.year >= 1960) & (data_annual.index.year < 1970)]
# plot scatter plot of inflation rate vs unemployment rate for 1960s only, set marker size to 10
plt.figure(figsize=(6,4))
plt.scatter(data_annual_1960s['Unemployment Rate'], data_annual_1960s['CPI Inflation Rate'], c='k', s=10)
plt.xlabel('Unemployment Rate')
plt.ylabel('CPI Inflation Rate')
# label each point with year
for i, year in enumerate(data_annual_1960s.index.year):
    plt.text(data_annual_1960s['Unemployment Rate'].iloc[i]-.06, data_annual_1960s['CPI Inflation Rate'].iloc[i]-.3, str(year)[2:4])
# add grid lines
plt.grid()
# move points on top of grid lines
plt.gca().set_axisbelow(False)
# add % to labels
plt.gca().xaxis.set_major_formatter(plt.FuncFormatter(lambda x, _: f'{x}%'))
plt.gca().yaxis.set_major_formatter(plt.FuncFormatter(lambda y, _: f'{y}%'))
<Figure size 600x400 with 1 Axes>
  • In 1960s, obvious negative relationship between inflation and unemployment.

  • Relationship looks nonlinear since the unemployment rate cannot fall further.

Source
# create subset of data_annual for years 1970-1995
data_annual_1970s_1990s = pd.DataFrame(data_annual[(data_annual.index.year >= 1970) & (data_annual.index.year < 1996)])
# plot scatter plot of inflation rate vs unemployment rate for 1970-1995 only, set marker size to 10
plt.figure(figsize=(6,4))
plt.scatter(data_annual_1970s_1990s['Unemployment Rate'], data_annual_1970s_1990s['CPI Inflation Rate'], c='k', s=10)
plt.xlabel('Unemployment Rate')
plt.ylabel('CPI Inflation Rate')
# label each point with year
for i, year in enumerate(data_annual_1970s_1990s.index.year):
    plt.text(data_annual_1970s_1990s['Unemployment Rate'].iloc[i]-.09, data_annual_1970s_1990s['CPI Inflation Rate'].iloc[i]-.7, str(year)[2:4])
# add grid lines
plt.grid()
# move points on top of grid lines
plt.gca().set_axisbelow(False)
# add % to labels
plt.gca().xaxis.set_major_formatter(plt.FuncFormatter(lambda x, _: f'{x}%'))
plt.gca().yaxis.set_major_formatter(plt.FuncFormatter(lambda y, _: f'{y}%'))
<Figure size 600x400 with 1 Axes>
  • After 1960s, no clear relationship between inflation and unemployment.

  • Q: Is the Phillips curve irrelevant?

  • A: It depends on how people form expectations.

1970 to 1995

  • Big positive oil price shocks cause inflation to rise rapidly

  • People form expectations given what they observe:

    πte=πt1\begin{gather*} \pi_t^e = \pi_{t-1} \end{gather*}

    i.e., expectations have become unanchored from a particular value

  • Phillips curve becomes

    πt=πt1αut+m+zπtπt1=αut+m+zΔπt=αut+m+z\begin{align*} \pi_t &= \pi_{t-1} -\alpha u_t + m + z \\ \pi_t - \pi_{t-1} &= -\alpha u_t + m + z \\ \Delta \pi_t &= -\alpha u_t + m + z \end{align*}

    where Δπtπtπt1\Delta \pi_t \equiv \pi_t - \pi_{t-1}.

  • The Phillips curve predicts:

    Corr(Δπt,ut)<0\begin{gather*} \textrm{Corr}(\Delta \pi_t ,u_t) < 0 \end{gather*}
Source
# create column in data_annual_1970s_1990s for year for first difference of inflatinon rate
data_annual_1970s_1990s['Inflation Rate First Difference'] = data_annual_1970s_1990s['CPI Inflation Rate'].diff()
# plot scatter plot of inflation rate vs unemployment rate for 1970-1995 only, set marker size to 10
plt.figure(figsize=(6,4))
plt.scatter(data_annual_1970s_1990s['Unemployment Rate'], data_annual_1970s_1990s['Inflation Rate First Difference'], c='k', s=10)
plt.xlabel('Unemployment Rate')
plt.ylabel('Percentage Point Change in Inflation Rate')
# add grid lines
plt.grid()
# move points on top of grid lines
plt.gca().set_axisbelow(False)
# add % to labels
plt.gca().xaxis.set_major_formatter(plt.FuncFormatter(lambda x, _: f'{x}%'))
# fit a linear trend line to the scatter plot
import numpy as np
# import linear regression from statsmodels
import statsmodels.api as sm
# create regressors with constant
ur = data_annual_1970s_1990s['Unemployment Rate']
X = pd.DataFrame({
    'const': 1,
    'ur': ur,
})
y = data_annual_1970s_1990s['Inflation Rate First Difference']
model = sm.OLS(y[1:-1], X[1:-1]).fit()
x_fit = np.linspace(X['ur'].min(), X['ur'].max(), 100)
y_fit = model.predict(pd.DataFrame({  'const': 1, 'ur': x_fit}))
plt.plot(x_fit, y_fit, '-r', label='Best Linear Fit')
plt.legend();
# add equation for trend line parameter estimates to plot in red text
slope = model.params['ur']
intercept = model.params['const']
eqstr = f'\\Delta \\pi_t = {intercept:.1f} - {abs(slope):.1f} u_t'
plt.text(8, -1.3, f'${eqstr}$', color='red');
<Figure size 600x400 with 1 Axes>
  • Each point is a year between 1970 and 1995.

  • The vertical axis is the first difference of the inflation rate.

  • The pp-value on the slope is close to zero, so the slope is statistically significantly different from zero.

1996 to 2019

Source
# create subset of data_annual for years 1996-2019
data_annual_1990s_2010s = pd.DataFrame(data_annual[(data_annual.index.year >= 1996) & (data_annual.index.year <= 2019)])
# create column in data_annual_1990s_2010s for year for first difference of inflatinon rate
data_annual_1990s_2010s['Inflation Rate First Difference'] = data_annual_1990s_2010s['CPI Inflation Rate'].diff()
# plot scatter plot of inflation rate vs unemployment rate for 1996-2019 only, set marker size to 10
plt.figure(figsize=(6,4))
plt.scatter(data_annual_1990s_2010s['Unemployment Rate'], data_annual_1990s_2010s['CPI Inflation Rate'], c='k', s=10)
plt.xlabel('Unemployment Rate')
plt.ylabel('Inflation Rate')
# add grid lines
plt.grid()
# move points on top of grid lines
plt.gca().set_axisbelow(False)
# add % to labels
plt.gca().xaxis.set_major_formatter(plt.FuncFormatter(lambda x, _: f'{x}%'))
# fit a linear trend line to the scatter plot
import numpy as np
# import linear regression from statsmodels
import statsmodels.api as sm
# create regressors with constant
ur = data_annual_1990s_2010s['Unemployment Rate']
X = pd.DataFrame({
    'const': 1,
    'ur': ur,
    })
y = data_annual_1990s_2010s['CPI Inflation Rate']
model = sm.OLS(y[1:-1], X[1:-1]).fit()
x_fit = np.linspace(X['ur'].min(), X['ur'].max(), 100)
y_fit = model.predict(pd.DataFrame({  'const': 1, 'ur': x_fit}))
plt.plot(x_fit, y_fit, '-r', label='Best Linear Fit')
plt.legend();
# add equation for trend line parameter estimates to plot in red text
slope = model.params['ur']
intercept = model.params['const']
eqstr = f'\\Delta \\pi_t = {intercept:.1f} - {abs(slope):.1f} u_t'
plt.text(8, 2, f'${eqstr}$', color='red');
<Figure size 600x400 with 1 Axes>
  • In early 1990s, Fed settles on 2%2\% inflation target

  • Fed (mostly) successfully achieves and maintains target from 1996 to 2019

  • Inflation expectations are anchored, πe2%\pi^e \approx 2\%

  • Thus, the best model for the PC is the same as 1960s

Medium-run Equilibrium

  • Substitute πt=πte\pi_t = \pi_t^e and ut=unu_t = u_n into Phillips curve

    πt=πtαun+m+z\begin{gather*} \pi_t = \pi_t - \alpha u_n + m + z \end{gather*}
  • Solve for unu_n

    un=(m+z)/α\begin{gather*} u_n = (m + z)/\alpha \end{gather*}
  • The equilibrium/natural unemployment rate increases if

    • the markup increases

    • worker B.P. increases (not because of the business cycle)

    • the sensitivity of wages demanded to an increase in B.P. falls

  1. Want: Phillips Curve relative to eqm so we can study how inflation responds to events/policy changes

  2. Substitute un=(m+z)/αu_n = (m + z)/\alpha into Phillips curve

    πt=πteαut+αm+zαπt=πteαut+αun\begin{align*} \pi_t &= \pi_t^e - \alpha u_t + {\color{red}\alpha}\frac{m + z}{\color{red}\alpha} \\ \pi_t &= \pi_t^e - \alpha u_t + \alpha u_n \end{align*}
  3. Rearranging/simplifying

    πtπte=α(utun)\begin{gather*} \pi_t - \pi_t^e &= - \alpha (u_t - u_n) \end{gather*}
  • Suppose πte=πˉ\pi_t^e = \bar{\pi} (as supported by data from 1996 to 2019, and hopefully from 2024 onward)

    πtπˉ=α(utun)\begin{align*} \pi_t - \bar{\pi} &= - \alpha (u_t - u_n) \end{align*}
  • Interpretation

    • If ut=unu_t = u_n (eqm), then πt\pi_t is stable at the target

    • If ut>unu_t > u_n (not eqm), then πt\pi_t is below the target

    • If ut<unu_t < u_n (not eqm), then πt\pi_t is above the target

  • The last two, if sustained for a significant time, may erode the central bank’s credibility and possibly unanchor expectations.