Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[equalizing_difference] Update numpy code in to remove vectorization #388

Merged
merged 2 commits into from
Feb 29, 2024
Merged
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
156 changes: 45 additions & 111 deletions lectures/equalizing_difference.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ jupytext:
extension: .md
format_name: myst
format_version: 0.13
jupytext_version: 1.14.4
jupytext_version: 1.16.1
kernelspec:
display_name: Python 3 (ipykernel)
language: python
Expand All @@ -29,7 +29,7 @@ To map Friedman's application into our model, think of our high school students

Our presentation is "incomplete" in the sense that it is based on a single equation that would be part of set equilibrium conditions of a more fully articulated model.

This ''equalizing difference'' equation determines a college, high-school wage ratio that equalizes present values of a high school educated worker and a college educated worker.
This ''equalizing difference'' equation determines a college-high-school wage ratio that equalizes present values of a high school educated worker and a college educated worker.

The idea is that lifetime earnings somehow adjust to make a new high school worker indifferent between going to college and not going to college but instead going to work immmediately.

Expand All @@ -50,6 +50,8 @@ As usual, we'll start by importing some Python modules.
```{code-cell} ipython3
import numpy as np
import matplotlib.pyplot as plt
from collections import namedtuple
from sympy import Symbol, Lambda, symbols
```

## The indifference condition
Expand Down Expand Up @@ -206,34 +208,34 @@ prominently including $\gamma_h, \gamma_c, R$.

Now let's write some Python code to compute $\phi$ and plot it as a function of some of its determinants.


```{code-cell} ipython3
class equalizing_diff:
"""
A class of the equalizing difference model
"""
# Define the namedtuple for the equalizing difference model
EqDiffModel = namedtuple('EqDiffModel', 'R T γ_h γ_c w_h0 D π')

def create_edm(R=1.05, # Gross rate of return
T=40, # Time horizon
γ_h=1.01, # High-school wage growth
γ_c=1.01, # College wage growth
w_h0=1, # Initial wage (high school)
D=10, # Cost for college
π=None):

return EqDiffModel(R, T, γ_h, γ_c, w_h0, D, π)

def compute_gap(model):
R, T, γ_h, γ_c, w_h0, D, π = model

A_h = (1 - (γ_h/R)**(T+1)) / (1 - γ_h/R)
A_c = (1 - (γ_c/R)**(T-3)) / (1 - γ_c/R) * (γ_c/R)**4

def __init__(self, R, T, γ_h, γ_c, w_h0, D=0, π=None):
# one switches to the weak model by setting π
self.R, self.γ_h, self.γ_c, self.w_h0, self.D = R, γ_h, γ_c, w_h0, D
self.T, self.π = T, π
# Tweaked model
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

After π=None put # Extra parameter (modified version) and then replace Tweaked model with Modification for reinterpreted model

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hi @jstac,

Since $\pi$ is only used for the entrepreneur model, would it be a good idea to remove it here and create a new namedtuple for the entrepreneur model when moving it into the exercise?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hi @HumphreyYang , yes, good call. Let's do exactly that.

if π is not None:
A_c = π * A_c

def compute_gap(self):
R, γ_h, γ_c, w_h0, D = self.R, self.γ_h, self.γ_c, self.w_h0, self.D
T, π = self.T, self.π

A_h = (1 - (γ_h/R)**(T+1)) / (1 - γ_h/R)
A_c = (1 - (γ_c/R)**(T-3)) / (1 - γ_c/R) * (γ_c/R)**4

# tweaked model
if π!=None:
A_c = π*A_c

ϕ = A_h/A_c + D/(w_h0*A_c)
return ϕ
ϕ = A_h / A_c + D / (w_h0 * A_c)
return ϕ
```


Using vectorization instead of loops,
we build some functions to help do comparative statics .

Expand All @@ -242,75 +244,30 @@ For a given instance of the class, we want to recompute $\phi$ when one paramete
Let's do an example.

```{code-cell} ipython3
# ϕ_R
def ϕ_R(mc, R_new):
mc_new = equalizing_diff(R_new, mc.T, mc.γ_h, mc.γ_c, mc.w_h0, mc.D, mc.π)
return mc_new.compute_gap()

ϕ_R = np.vectorize(ϕ_R)

# ϕ_γh
def ϕ_γh(mc, γh_new):
mc_new = equalizing_diff(mc.R, mc.T, γh_new, mc.γ_c, mc.w_h0, mc.D, mc.π)
return mc_new.compute_gap()

ϕ_γh = np.vectorize(ϕ_γh)

# ϕ_γc
def ϕ_γc(mc, γc_new):
mc_new = equalizing_diff(mc.R, mc.T, mc.γ_h, γc_new, mc.w_h0, mc.D, mc.π)
return mc_new.compute_gap()
ex1 = create_edm()
gap1 = compute_gap(ex1)

ϕ_γc = np.vectorize(ϕ_γc)

# ϕ_π
def ϕ_π(mc, π_new):
mc_new = equalizing_diff(mc.R, mc.T, mc.γ_h, mc.γ_c, mc.w_h0, mc.D, π_new)
return mc_new.compute_gap()

ϕ_π = np.vectorize(ϕ_π)
```

```{code-cell} ipython3
# set benchmark parameters
R = 1.05
T = 40
γ_h, γ_c = 1.01, 1.01
w_h0 = 1
D = 10

# create an instance
ex1 = equalizing_diff(R=R, T=T, γ_h=γ_h, γ_c=γ_c, w_h0=w_h0, D=D)
gap1 = ex1.compute_gap()

print(gap1)
gap1
```

Let's not charge for college and recompute $\phi$.

The initial college wage premium should go down.




```{code-cell} ipython3
# free college
ex2 = equalizing_diff(R, T, γ_h, γ_c, w_h0, D=0)
gap2 = ex2.compute_gap()
print(gap2)
ex2 = create_edm(D=0)
gap2 = compute_gap(ex2)
gap2
```



Let us construct some graphs that show us how the initial college-high-school wage ratio $\phi$ would change if one of its determinants were to change.

Let's start with the gross interest rate $R$.


Let's start with the gross interest rate $R$.

```{code-cell} ipython3
R_arr = np.linspace(1, 1.2, 50)
plt.plot(R_arr, φ_R(ex1, R_arr))
plt.plot(R_arr, compute_gap(create_edm(R=R_arr)))
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Wow, this actually works?? That's amazing. So create_edm(R=R_arr) returns a numpy array of namedtuples??

This definitely needs some comments.

Copy link
Collaborator Author

@HumphreyYang HumphreyYang Feb 28, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think this is passing the model with parameter R as a numpy array into compute_gap but not creating a numpy array of namedtuple.

The R_arr is then unpacked in the compute_gap function, and the operation on the array is broadcasted within the function.

This definitely needs some comments.

I agree! Would you think a comment like # Create a model and compute the gap suffice?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please see my response below.

plt.xlabel(r'$R$')
plt.ylabel(r'wage gap')
plt.show()
Expand All @@ -323,11 +280,12 @@ determinants of $\phi$.

```{code-cell} ipython3
γc_arr = np.linspace(1, 1.2, 50)
plt.plot(γc_arr, φ_γc(ex1, γc_arr))
plt.plot(γc_arr, compute_gap(create_edm(γ_c=γc_arr)))
plt.xlabel(r'$\gamma_c$')
plt.ylabel(r'wage gap')
plt.show()
```

Notice how the intitial wage gap falls when the rate of growth $\gamma_c$ of college wages rises.

The wage gap falls to "equalize" the present values of the two types of career, one as a high school worker, the other as a college worker.
Expand All @@ -338,33 +296,31 @@ The following graph shows what happens.

```{code-cell} ipython3
γh_arr = np.linspace(1, 1.1, 50)
plt.plot(γh_arr, φ_γh(ex1, γh_arr))
plt.plot(γh_arr, compute_gap(create_edm(γ_h=γh_arr)))
plt.xlabel(r'$\gamma_h$')
plt.ylabel(r'wage gap')
plt.show()
```


## Entrepreneur-worker interpretation

Now let's adopt the entrepreneur-worker interpretation of our model.

If the probability that a new business succeeds is $.2$, let's compute the initial wage premium for successful entrepreneurs.
If the probability that a new business succeeds is $0.2$, let's compute the initial wage premium for successful entrepreneurs.

```{code-cell} ipython3
# a model of enterpreneur
ex3 = equalizing_diff(R, T, γ_h, γ_c, w_h0, π=0.2)
gap3 = ex3.compute_gap()
ex3 = create_edm(π=0.2)
gap3 = compute_gap(ex3)

print(gap3)
gap3
```

Now let's study how the initial wage premium for successful entrepreneurs depend on the success probability.


```{code-cell} ipython3
π_arr = np.linspace(0.2, 1, 50)
plt.plot(π_arr, φ_π(ex3, π_arr))
plt.plot(π_arr, compute_gap(create_edm(π=π_arr)))
plt.ylabel(r'wage gap')
plt.xlabel(r'$\pi$')
plt.show()
Expand All @@ -388,16 +344,10 @@ But for a reader interested in how we can get Python to do all the hard work inv

We'll use the Python module 'sympy' to compute partial derivatives of $\phi$ with respect to the parameters that determine it.

Let's import key functions from sympy.

```{code-cell} ipython3
from sympy import Symbol, Lambda, symbols
```

Define symbols

```{code-cell} ipython3
γ_h, γ_c, w_h0, D = symbols('\gamma_h, \gamma_h_c, w_0^h, D', real=True)
γ_h, γ_c, w_h0, D = symbols('\gamma_h, \gamma_c, w_0^h, D', real=True)
R, T = Symbol('R', real=True), Symbol('T', integer=True)
```

Expand Down Expand Up @@ -450,8 +400,6 @@ Now let's compute $\frac{\partial \phi}{\partial D}$ and then evaluate it at the

Thus, as with our earlier graph, we find that raising $R$ increases the initial college wage premium $\phi$.

+++

Compute $\frac{\partial \phi}{\partial T}$ and evaluate it a default parameters

```{code-cell} ipython3
Expand All @@ -469,8 +417,6 @@ We find that raising $T$ decreases the initial college wage premium $\phi$.

This is because college graduates now have longer career lengths to "pay off" the time and other costs they paid to go to college

+++

Let's compute $\frac{\partial \phi}{\partial γ_h}$ and evaluate it at default parameters.

```{code-cell} ipython3
Expand All @@ -486,8 +432,6 @@ Let's compute $\frac{\partial \phi}{\partial γ_h}$ and evaluate it at default p

We find that raising $\gamma_h$ increases the initial college wage premium $\phi$, as we did with our earlier graphical analysis.

+++

Compute $\frac{\partial \phi}{\partial γ_c}$ and evaluate it numerically at default parameter values

```{code-cell} ipython3
Expand All @@ -503,8 +447,6 @@ Compute $\frac{\partial \phi}{\partial γ_c}$ and evaluate it numerically at def

We find that raising $\gamma_c$ decreases the initial college wage premium $\phi$, as we did with our graphical analysis earlier

+++

Let's compute $\frac{\partial \phi}{\partial R}$ and evaluate it numerically at default parameter values

```{code-cell} ipython3
Expand All @@ -518,12 +460,4 @@ Let's compute $\frac{\partial \phi}{\partial R}$ and evaluate it numerically at
ϕ_R_func(D_value, γ_h_value, γ_c_value, R_value, T_value, w_h0_value)
```

+++ {"tags": []}

We find that raising the gross interest rate $R$ increases the initial college wage premium $\phi$, as we did with our graphical analysis earlier



```{code-cell} ipython3

```
We find that raising the gross interest rate $R$ increases the initial college wage premium $\phi$, as we did with our graphical analysis earlier
Loading