Skip to content
This repository was archived by the owner on Apr 24, 2020. It is now read-only.

Commit 31ad323

Browse files
authored
Fix wealth dynamics lecture (#833)
* misc * finished updates
1 parent 626918b commit 31ad323

File tree

1 file changed

+62
-78
lines changed

1 file changed

+62
-78
lines changed

source/rst/wealth_dynamics.rst

Lines changed: 62 additions & 78 deletions
Original file line numberDiff line numberDiff line change
@@ -221,15 +221,7 @@ normal in :math:`\mathbb R^3`.
221221
The value of :math:`c_r` should be close to zero, since rates of return
222222
on assets do not exhibit large trends.
223223

224-
When we simulate a population of households, we will take
225-
226-
- :math:`\{z_t\}` to be an **aggregate shock** that is common to all
227-
households
228-
- :math:`\{\xi_t\}` and :math:`\{\zeta_t\}` to be **idiosyncratic
229-
shocks**
230-
231-
Idiosyncratic shocks are specific to individual households and
232-
independent across them.
224+
When we simulate a population of households, we will assume all shocks are idiosyncratic (i.e., specific to individual households and independent across them).
233225

234226
Regarding the savings function :math:`s`, our default model will be
235227

@@ -247,6 +239,7 @@ their wealth.
247239
We are using something akin to a fixed savings rate model, while
248240
acknowledging that low wealth households tend to save very little.
249241

242+
250243
Implementation
251244
==============
252245

@@ -267,6 +260,7 @@ Here's some type information to help Numba.
267260
('b', float64), # aggregate shock parameter
268261
('σ_z', float64), # aggregate shock parameter
269262
('z_mean', float64), # mean of z process
263+
('z_var', float64), # variance of z process
270264
('y_mean', float64), # mean of y process
271265
('R_mean', float64) # mean of R process
272266
]
@@ -280,15 +274,15 @@ the aggregate state and household wealth.
280274
class WealthDynamics:
281275
282276
def __init__(self,
283-
w_hat=10.0,
277+
w_hat=1.0,
284278
s_0=0.75,
285279
c_y=1.0,
286-
μ_y=1.5,
280+
μ_y=1.0,
287281
σ_y=0.2,
288282
c_r=0.05,
289-
μ_r=0.0,
290-
σ_r=0.6,
291-
a=0.85,
283+
μ_r=0.1,
284+
σ_r=0.5,
285+
a=0.5,
292286
b=0.0,
293287
σ_z=0.1):
294288
@@ -299,8 +293,8 @@ the aggregate state and household wealth.
299293
300294
# Record stationary moments
301295
self.z_mean = b / (1 - a)
302-
z_var = σ_z**2 / (1 - a**2)
303-
exp_z_mean = np.exp(self.z_mean + z_var / 2)
296+
self.z_var = σ_z**2 / (1 - a**2)
297+
exp_z_mean = np.exp(self.z_mean + self.z_var / 2)
304298
self.R_mean = c_r * exp_z_mean + np.exp(μ_r + σ_r**2 / 2)
305299
self.y_mean = c_y * exp_z_mean + np.exp(μ_y + σ_y**2 / 2)
306300
@@ -320,82 +314,54 @@ the aggregate state and household wealth.
320314
self.a, self.b, self.σ_z)
321315
return parameters
322316
323-
def update_z(self, z):
317+
def update_states(self, w, z):
324318
"""
325-
Update exogenous state one period, given current state z.
319+
Update one period, given current wealth w and persistent
320+
state z.
326321
"""
322+
327323
# Simplify names
328324
params = self.parameters()
329325
w_hat, s_0, c_y, μ_y, σ_y, c_r, μ_r, σ_r, a, b, σ_z = params
330-
331-
# Update z
332326
zp = a * z + b + σ_z * np.random.randn()
333-
return zp
334-
335-
def update_wealth(self, w, zp):
336-
"""
337-
Update one period, given current wealth w and next period
338-
exogenous state zp.
339-
"""
340-
# Simplify names
341-
params = self.parameters()
342-
w_hat, s_0, c_y, μ_y, σ_y, c_r, μ_r, σ_r, a, b, σ_z = params
343327
344328
# Update wealth
345329
y = c_y * np.exp(zp) + np.exp(μ_y + σ_y * np.random.randn())
346330
wp = y
347331
if w >= w_hat:
348332
R = c_r * np.exp(zp) + np.exp(μ_r + σ_r * np.random.randn())
349333
wp += R * s_0 * w
350-
return wp
334+
return wp, zp
351335
352-
353-
Here’s a function to simulate the aggregate state:
336+
337+
Here's function to simulate the time series of wealth for in individual households.
354338

355339
.. code:: ipython3
356340
357341
@njit
358-
def z_time_series(wdy, ts_length=10_000):
342+
def wealth_time_series(wdy, w_0, n):
359343
"""
360-
Generate a single time series of length ts_length for the
361-
aggregate shock process {z_t}.
344+
Generate a single time series of length n for wealth given
345+
initial value w_0.
362346
363-
* wdy is an instance of WealthDynamics
364-
365-
"""
366-
z = np.empty(ts_length)
367-
z[0] = wdy.z_mean
368-
for t in range(ts_length-1):
369-
z[t+1] = wdy.update_z(z[t])
370-
return z
347+
The initial persistent state z_0 for each household is drawn from
348+
the stationary distribution of the AR(1) process.
371349
372-
Here’s a function to simulate the wealth of a single household, taking the
373-
time path of the aggregate state as an argument.
350+
* wdy: an instance of WealthDynamics
351+
* w_0: scalar
352+
* n: int
374353
375-
.. code:: ipython3
376-
377-
@njit
378-
def wealth_time_series(wdy, w_0, z):
379-
"""
380-
Generate a single time series for wealth given aggregate shock
381-
sequence z.
382354
383-
* wdy: an instance of WealthDynamics
384-
* w_0: a scalar representing initial wealth.
385-
* z: array_like
386-
387-
The returned time series w satisfies len(w) = len(z).
388355
"""
389-
n = len(z)
356+
z = wdy.z_mean + np.sqrt(wdy.z_var) * np.random.randn()
390357
w = np.empty(n)
391358
w[0] = w_0
392359
for t in range(n-1):
393-
w[t+1] = wdy.update_wealth(w[t], z[t+1])
360+
w[t+1], z = wdy.update_states(w[t], z)
394361
return w
395362
396363
397-
Finally, here's function to simulate a cross section of households forward in
398-
time.
364+
Now here's function to simulate a cross section of households forward in time.
399365

400366
Note the use of parallelization to speed up computation.
401367

@@ -414,16 +380,16 @@ Note the use of parallelization to speed up computation.
414380
j = shift_length.
415381
416382
Returns the new distribution.
383+
417384
"""
418385
new_distribution = np.empty_like(w_distribution)
419386
420-
z = z_time_series(wdy, ts_length=shift_length)
421-
422387
# Update each household
423388
for i in prange(len(new_distribution)):
389+
z = wdy.z_mean + np.sqrt(wdy.z_var) * np.random.randn()
424390
w = w_distribution[i]
425391
for t in range(shift_length-1):
426-
w = wdy.update_wealth(w, z[t+1])
392+
w, z = wdy.update_states(w, z)
427393
new_distribution[i] = w
428394
return new_distribution
429395
@@ -450,8 +416,8 @@ Let's look at the wealth dynamics of an individual household.
450416
451417
wdy = WealthDynamics()
452418
453-
z = z_time_series(wdy, ts_length=10_000)
454-
w = wealth_time_series(wdy, wdy.y_mean, z)
419+
ts_length = 200
420+
w = wealth_time_series(wdy, wdy.y_mean, ts_length)
455421
456422
fig, ax = plt.subplots()
457423
ax.plot(w)
@@ -474,7 +440,7 @@ curve and Gini coefficient.
474440

475441
.. code:: ipython3
476442
477-
def generate_lorenz_and_gini(wdy, num_households=250_000, T=500):
443+
def generate_lorenz_and_gini(wdy, num_households=100_000, T=500):
478444
"""
479445
Generate the Lorenz curve data and gini coefficient corresponding to a
480446
WealthDynamics mode by simulating num_households forward to time T.
@@ -485,8 +451,7 @@ curve and Gini coefficient.
485451
ψ_star = update_cross_section(wdy, ψ_0, shift_length=T)
486452
return qe.gini_coefficient(ψ_star), qe.lorenz_curve(ψ_star)
487453
488-
Now we investigate how the Lorenz curves associated with the wealth distribution change
489-
as return to savings increase.
454+
Now we investigate how the Lorenz curves associated with the wealth distribution change as return to savings varies.
490455

491456
The code below plots Lorenz curves for three different values of :math:`\mu_r`.
492457

@@ -499,7 +464,7 @@ In fact the code, which is JIT compiled and parallelized, runs extremely fast re
499464
.. code:: ipython3
500465
501466
fig, ax = plt.subplots()
502-
μ_r_vals = np.linspace(0.0, 0.05, 3)
467+
μ_r_vals = (0.0, 0.025, 0.05)
503468
gini_vals = []
504469
505470
for μ_r in μ_r_vals:
@@ -544,30 +509,49 @@ Now let's check the Gini coefficient.
544509
Once again, we see that inequality increases as returns on financial income
545510
rise.
546511

547-
This makes sense because higher returns reinforce the effect of existing wealth.
512+
Let's finish this section by investigating what happens when we change the
513+
volatility term :math:`\sigma_r` in financial returns.
548514

549515

516+
.. code:: ipython3
517+
518+
fig, ax = plt.subplots()
519+
σ_r_vals = (0.35, 0.45, 0.52)
520+
gini_vals = []
521+
522+
for σ_r in σ_r_vals:
523+
wdy = WealthDynamics(σ_r=σ_r)
524+
gv, (f_vals, l_vals) = generate_lorenz_and_gini(wdy)
525+
ax.plot(f_vals, l_vals, label=f'$\psi^*$ at $\mu_r = {σ_r:0.2}$')
526+
gini_vals.append(gv)
527+
528+
ax.plot(f_vals, f_vals, label='equality')
529+
ax.legend(loc="upper left")
530+
plt.show()
531+
532+
533+
We see that greater volatility has the effect of increasing inequality in this model.
534+
550535

551536
Exercises
552537
=========
553538

554539
Exercise 1
555540
----------
556541

557-
For a wealth or income distribution with Pareto tail, a higher tail index suggests lower
558-
In fact, it is possible to prove that the Gini coefficient of the Pareto
542+
For a wealth or income distribution with Pareto tail, a higher tail index suggests lower inequality.
543+
544+
Indeed, it is possible to prove that the Gini coefficient of the Pareto
559545
distribution with tail index :math:`a` is :math:`1/(2a - 1)`.
560546

561547
To the extent that you can, confirm this by simulation.
562548

563549
In particular, generate a plot of the Gini coefficient against the tail index
564-
using both the theoretical value and the value computed from a sample via
565-
``qe.gini_coefficient``.
550+
using both the theoretical value just given and the value computed from a sample via ``qe.gini_coefficient``.
566551

567552
For the values of the tail index, use ``a_vals = np.linspace(1, 10, 25)``.
568553

569-
Use sample of size 1,000 for each :math:`a` and the sampling method for generating
570-
Pareto draws employed in the discussion of Lorenz curves for the Pareto distribution.
554+
Use sample of size 1,000 for each :math:`a` and the sampling method for generating Pareto draws employed in the discussion of Lorenz curves for the Pareto distribution.
571555

572556
To the extend that you can, interpret the monotone relationship between the
573557
Gini index and :math:`a`.

0 commit comments

Comments
 (0)