Undergraduate Computational Macro
The CCDF is often plotted on a log-log scale. i.e. \(\log(x)\) vs. \(\log(1 - F(x))\)
Taking the log of the probability lets us see the speed that the tail drops off
For the Pareto distribution, the CCDF is \[ \left(\frac{x}{x_m}\right)^{-\alpha} \]
var(dist) + mean(dist) ^ 2 = 3.0
\[ \hat{F}(x) = \frac{\text{number of observations } X_n \leq x}{N} \]
Given the least squares problem \[ \min_{a,b} \sum_{i=1}^n (y_i - (a x_i + b))^2 \]
Calculate the means, \(\bar{x} = \frac{1}{n}\sum_{i=1}^n x_i\) and \(\bar{y} = \frac{1}{n}\sum_{i=1}^n y_i\)
Then the coefficients are
\[ a = \frac{\sum_{i=1}^{n} (x_i - \bar{x})(y_i - \bar{y})}{\sum_{i=1}^{n} (x_i - \bar{x})^2},\quad b = \bar{y} - a\bar{x} \]
function simple_regression(x, y)
x_bar = mean(x)
y_bar = mean(y)
a = sum((x .- x_bar).*(y .- y_bar)) / sum((x .- x_bar).^2)
b = y_bar - a * x_bar
return (;a, b)
end
N = 1000
alpha = 1.1
X = sort(rand(Pareto(alpha, 1.0), N))
F = (1:N) / N
y = log.(1 .- F)
x = log.(X)
(;a, b) = simple_regression(x[1:end-1], y[1:end-1])
plot(x, y; label = L"\log(1-F(X))", xlabel=L"\log(X)")
plot!(x, a*x .+ b; label = L"a=%$(round(a, digits = 2)), \alpha = %$alpha", style = :dash)
See https://www.science.org/doi/10.1126/science.1062081 by Axtell
Recall the Kesten Process, which generalizes this by adding in the \(y_{t+1}\) term
\[ X_{t+1} = a_{t+1} X_t + y_{t+1} \]
Another example of a related process
\[ X_{t+1} = \max\{s_0, a_{t+1} X_t\} \]
function iterate_map_iid_ensemble(f, dist, x0, T, num_samples)
x = zeros(num_samples, T + 1)
x[:, 1] .= x0
for t in 2:(T + 1)
x[:, t] .= f.(x[:, t - 1], rand(dist, num_samples))
end
return x
end
num_samples = 100000
T = 500
s_0 = 1.0 # exit/entry at X = 1.0
X_0 = 1.0
a_dist = LogNormal(-0.01, 0.1)
h(X, a) = max(s_0, a * X)
X = iterate_map_iid_ensemble(h, a_dist, X_0, T, num_samples)
histogram(log.(X[:, end]); label = L"Histogram", xlabel = L"\log X_t", normalized=true)
X_T = sort(X[:, end])
x_T = log.(X_T)
F_T = (1:length(X_T)) / length(X_T)
y_T = log.(1 .- F_T)
(;a, b) = simple_regression(x_T[1:end-1],
y_T[1:end-1])
plot(x_T, y_T; label = L"\log(1-F(X))",
xlabel=L"\log(X)", size = (600, 400))
a_r = round(a, digits = 2)
plot!(x_T, a*x_T .+ b;
label = L"a=%$a_r", style = :dash)
\[ L(p) = \frac{\int_{-\infty}^{Q(p)} x f(x) dx}{\int_{-\infty}^{\infty} x f(x) dx} \]
function lorenz(v) # assumed sorted vector
S = cumsum(v) # cumulative sums: [v[1], v[1] + v[2], ... ]
F = (1:length(v)) / length(v) # empirical CDF since everyone has the same weight!
L = S ./ S[end]
return (; F, L) # returns named tuple
end
n = 10_000
w = sort(exp.(randn(n))); # lognormal draws
(; F, L) = lorenz(w)
plot(F, L, label = "Lorenz curve, lognormal sample", legend = :topleft)
plot!(F, F, label = "Lorenz curve, equality")
\[ G = \frac{2\sum_{i=1}^n i v_i}{n \sum_{i=1}^n v_i} - \frac{n+1}{n} \]
function gini(v)
return (2 * sum(i * y for (i, y) in enumerate(v)) / sum(v)
- (length(v) + 1)) / length(v)
end
a_vals = 1:19
n = 100
ginis = [gini(sort(rand(Weibull(a), n))) for a in a_vals]
ginis_theoretical = [1 - 2^(-1 / a) for a in a_vals]
plot(a_vals, ginis, label = "estimated gini coefficient",
xlabel = L"Weibull parameter $a$", ylabel = "Gini coefficient")
plot!(a_vals, ginis_theoretical, label = "theoretical gini coefficient")
\(w_t\) is wealth
Stochastic variables, which may have an underlying state
The total income saved is an exogenous \(s(w_t)\). Consumption implied
\[ w_{t+1} = R_{t+1}s(w_t) + y_{t+1} \]
Wealth net of consumption, \(s(w_t)\), is a simple threshold
\[ s(w_t) = \begin{cases} s_0 w_t & \text{if } w_t > \hat{w}\\ 0 & \text{otherwise}\end{cases} \]
We will introduce a correlation between income and returns based on an underlying state
\[ z_{t+1} = a z_t + b + \sigma_z \epsilon_{t+1} \]
Given this latent state the returns have IID shocks
\[ R_t := 1 + r_t = c_r \exp(z_t) + \exp(\mu_r + \sigma_r \xi_t) \]
And income has a similar IID component
\[ y_t = c_y \exp(z_t) + \exp(\mu_y + \sigma_y \zeta_t) \]
Where \(\epsilon_t, \xi_t, \zeta_t\) are IID and standard normal
function wealth_dynamics_model(; # all named arguments
w_hat = 1.0, s_0 = 0.75, # savings
c_y = 1.0, mu_y = 1.0, sigma_y = 0.2, # labor income
c_r = 0.05, mu_r = 0.1, sigma_r = 0.5, # rate of return
a = 0.5, b = 0.0, sigma_z = 0.1)
z_mean = b / (1 - a)
z_var = sigma_z^2 / (1 - a^2)
exp_z_mean = exp(z_mean + z_var / 2)
R_mean = c_r * exp_z_mean + exp(mu_r + sigma_r^2 / 2)
y_mean = c_y * exp_z_mean + exp(mu_y + sigma_y^2 / 2)
alpha = R_mean * s_0
z_stationary_dist = Normal(z_mean, sqrt(z_var))
@assert alpha <= 1 # check stability condition that wealth does not diverge
return (; w_hat, s_0, c_y, mu_y, sigma_y, c_r, mu_r, sigma_r, a, b, sigma_z,
z_mean, z_var, z_stationary_dist, exp_z_mean, R_mean, y_mean, alpha)
end
function simulate_wealth_dynamics(w_0, z_0, T, params)
(; w_hat, s_0, c_y, mu_y, sigma_y, c_r, mu_r, sigma_r, a, b, sigma_z) = params # unpack
w = zeros(T + 1)
z = zeros(T + 1)
w[1] = w_0
z[1] = z_0
for t in 2:(T + 1)
z[t] = a * z[t - 1] + b + sigma_z * randn()
y = c_y * exp(z[t]) + exp(mu_y + sigma_y * randn())
w[t] = y # income goes to next periods wealth
if w[t - 1] >= w_hat # if above minimum wealth level, add savings
R = c_r * exp(z[t]) + exp(mu_r + sigma_r * randn())
w[t] += R * s_0 * w[t - 1]
end
end
return w, z
end
(f1(0.8), f2(0.8), f3(0.8)) = (2.8, 2.8, 2.8)
(f1(1.8), f2(1.8), f3(1.8)) = (3.8, 3.8, 3.8)
function simulate_panel(N, T, p)
(; w_hat, s_0, c_y, mu_y, sigma_y, c_r, mu_r, sigma_r, a, b, sigma_z) = p
w = p.y_mean * ones(N) # start at the mean of y
z = rand(p.z_stationary_dist, N)
zp = similar(z)
wp = similar(w)
R = similar(w)
for t in 1:T
z_shock = randn(N)
R_shock = randn(N)
w_shock = randn(N)
@inbounds for i in 1:N
zp[i] = a * z[i] + b + sigma_z * z_shock[i]
R[i] = (w[i] >= w_hat) ? c_r * exp(zp[i]) + exp(mu_r + sigma_r * R_shock[i]) : 0.0
wp[i] = c_y * exp(zp[i]) + exp(mu_y + sigma_y * w_shock[i]) + R[i] * s_0 * w[i]
end
w .= wp
z .= zp
end
sort!(w) # sorts the wealth so we can calculate gini/lorenz
F, L = lorenz(w)
return (; w, F, L, gini = gini(w))
end
sigma_r_vals = range(0.35, 0.53, 5)
results = map(sigma_r -> simulate_panel(N, T, wealth_dynamics_model(; sigma_r)),
sigma_r_vals);
plt = plot(results[1].F, results[1].F, label = "equality", legend = :topleft)
[plot!(plt, res.F, res.L, label = L"\sigma_r = %$sigma_r")
for (sigma_r, res) in zip(sigma_r_vals, results)]
plt
!
to denote a function which mutates its arguments and to put any arguments that will be modified first.$
to interpolate the variables into the function, avoiding global scope and global variables 474.510 ns (3 allocations: 7.88 KiB)
BenchmarkTools.Trial: 10000 samples with 280 evaluations. Range (min … max): 501.936 ns … 49.274 μs ┊ GC (min … max): 0.00% … 18.56% Time (median): 587.811 ns ┊ GC (median): 0.00% Time (mean ± σ): 903.527 ns ± 1.499 μs ┊ GC (mean ± σ): 23.17% ± 15.47% █▇▄▂ ▁ ▁▁ ▁ █████▆▅▁▁▃▁▃▁▃▁▃▁▁▁▃▁▁▁▁▁▃▁▁▃▁▁█▇▅▁▄▁▁▁▁▁▁▁▁▁▃▄▃▄▅▇▆██████▇▆ █ 502 ns Histogram: log(frequency) by time 6.07 μs < Memory estimate: 7.88 KiB, allocs estimate: 3.