In [1]:
%matplotlib inline

In [2]:
import matplotlib.pyplot as plt
import numpy as np
import seaborn as sn
sn.set_style("whitegrid")
import sympy as sym

import pycollocation
import pyam

In [3]:
pyam.__version__


Out[3]:
'0.1.2a0'

Defining inputs

Need to define some heterogenous factors of production...


In [4]:
# define some workers skill
x, loc1, mu1, sigma1 = sym.var('x, loc1, mu1, sigma1')
skill_cdf = 0.5 + 0.5 * sym.erf((sym.log(x - loc1) - mu1) / sym.sqrt(2 * sigma1**2))
skill_params = {'loc1': 1e0, 'mu1': 0.0, 'sigma1': 1.0}

workers = pyam.Input(var=x,
                     cdf=skill_cdf,
                     params=skill_params,
                     bounds=(1.2, 1e1),  # guesses for the alpha and (1 - alpha) quantiles!
                     alpha=0.005,
                     measure=1.0
                     )

# define some firms
y, loc2, mu2, sigma2 = sym.var('y, loc2, mu2, sigma2')
productivity_cdf = 0.5 + 0.5 * sym.erf((sym.log(y - loc2) - mu2) / sym.sqrt(2 * sigma2**2))
productivity_params = {'loc2': 1e0, 'mu2': 0.0, 'sigma2': 1.0}

firms = pyam.Input(var=y,
                   cdf=productivity_cdf,
                   params=productivity_params,
                   bounds=(1.2, 1e1),  # guesses for the alpha and (1 - alpha) quantiles!
                   alpha=0.005,
                   measure=1.0
                   )

Note that we are shifting the distributions of worker skill and firm productivity to the right by 1.0 in order to try and avoid issues with having workers (firms) with near zero skill (productivity).


In [5]:
xs = np.linspace(workers.lower, workers.upper, 1e4)
plt.plot(xs, workers.evaluate_pdf(xs))
plt.xlabel('Worker skill, $x$', fontsize=20)
plt.show()


Defining a production process

Next need to define some production process...


In [6]:
# define symbolic expression for CES between x and y
omega_A, sigma_A = sym.var('omega_A, sigma_A')
A = ((omega_A * x**((sigma_A - 1) / sigma_A) + 
     (1 - omega_A) * y**((sigma_A - 1) / sigma_A))**(sigma_A / (sigma_A - 1))) 

# define symbolic expression for CES between x and y
r, l, omega_B, sigma_B = sym.var('r, l, omega_B, sigma_B')
B = ((omega_B * r**((sigma_B - 1) / sigma_B) + 
     (1 - omega_B) * l**((sigma_B - 1) / sigma_B))**(sigma_B / (sigma_B - 1))) 

F = A * B

In [7]:
# negative assortativity requires that sigma_A * sigma_B > 1
F_params = {'omega_A':0.25, 'omega_B':0.5, 'sigma_A':2.0, 'sigma_B':1.0 }

Define a boundary value problem


In [8]:
problem = pyam.AssortativeMatchingProblem(assortativity='negative',
                                          input1=workers,
                                          input2=firms,
                                          F=sym.limit(F, sigma_B, 1),
                                          F_params=F_params)

Pick some collocation solver


In [9]:
solver = pycollocation.OrthogonalPolynomialSolver(problem)

Compute some decent initial guess

Currently I guess that $\mu(x)$ is has the form...

$$ \hat{\mu}(x) = \beta_0 + \beta_1 f(x) $$

(i.e., a linear translation) of some function $f$. Using my $\hat{\mu}(x)$, I can then back out a guess for $\theta(x)$ implied by the model...

$$ \hat{\theta}(x) = \frac{H(x)}{\hat{\mu}'(x)} $$

In [59]:
initial_guess = pyam.OrthogonalPolynomialInitialGuess(solver)
initial_polys = initial_guess.compute_initial_guess("Chebyshev",
                                                    degrees={'mu': 40, 'theta': 70},
                                                    f=lambda x, alpha: x**alpha,
                                                    alpha=1.0)

In [60]:
# quickly plot the initial conditions
xs = np.linspace(workers.lower, workers.upper, 1000)
plt.plot(xs, initial_polys['mu'](xs))
plt.plot(xs, initial_polys['theta'](xs))
plt.grid('on')


Solve the model!


In [61]:
domain = [workers.lower, workers.upper]
initial_coefs = {'mu': initial_polys['mu'].coef,
                 'theta': initial_polys['theta'].coef}

solver.solve(kind="Chebyshev",
             coefs_dict=initial_coefs,
             domain=domain,
             method='hybr')

In [62]:
solver.result.success


Out[62]:
False

Plot some results


In [63]:
viz = pyam.Visualizer(solver)

In [64]:
viz.interpolation_knots = np.linspace(workers.lower, workers.upper, 1000)
viz.residuals.plot()
plt.show()



In [58]:
viz.normalized_residuals[['mu', 'theta']].plot(logy=True)
plt.show()



In [53]:
viz.solution.tail()


Out[53]:
F Fl Flr Fr Fx Fxl Fxr Fxy Fy Fyl Fyr factor_payment_1 factor_payment_2 mu theta
12.508857 18.206082 5.150932 2.575466 9.103041 0.398371 0.112708 0.199185 0.040918 0.934998 0.264533 0.467499 5.150932 9.103041 14.142185 1.767261
12.520161 18.206094 5.153478 2.576739 9.103047 0.397750 0.112589 0.198875 0.040864 0.935230 0.264729 0.467615 5.153478 9.103047 14.142192 1.766389
12.531464 18.206107 5.156022 2.578011 9.103054 0.397132 0.112469 0.198566 0.040811 0.935460 0.264925 0.467730 5.156022 9.103054 14.142198 1.765519
12.542768 18.206122 5.158563 2.579281 9.103061 0.396514 0.112349 0.198257 0.040757 0.935691 0.265121 0.467846 5.158563 9.103061 14.142205 1.764651
12.554071 18.206134 5.161101 2.580551 9.103067 0.395898 0.112230 0.197949 0.040704 0.935921 0.265316 0.467961 5.161101 9.103067 14.142212 1.763784

In [25]:
viz.solution[['mu', 'theta']].plot(subplots=True)
plt.show()



In [26]:
viz.solution[['Fxy', 'Fyl']].plot()
plt.show()


Plot factor payments

Note the factor_payment_1 is wages and factor_payment_2 is profits...


In [27]:
viz.solution[['factor_payment_1', 'factor_payment_2']].plot(subplots=True)
plt.show()


Plot firm size against wages and profits


In [28]:
fig, axes = plt.subplots(1, 2, sharey=True)
axes[0].scatter(viz.solution.factor_payment_1, viz.solution.theta, alpha=0.5,
               edgecolor='none')
axes[0].set_ylim(0, 1.05 * viz.solution.theta.max())
axes[0].set_xlabel('Wages, $w$')
axes[0].set_ylabel(r'Firm size, $\theta$')

axes[1].scatter(viz.solution.factor_payment_2, viz.solution.theta, alpha=0.5,
               edgecolor='none')
axes[1].set_xlabel(r'Profits, $\pi$')

plt.show()



In [29]:
# to get correlation just use pandas!
viz.solution.corr()


Out[29]:
F Fl Flr Fr Fx Fxl Fxr Fxy Fy Fyl Fyr factor_payment_1 factor_payment_2 mu theta
F 1.000000 0.999021 0.999021 1.000000 0.220731 -0.997585 0.220731 -0.972939 0.992605 -0.614955 0.992605 0.999021 1.000000 0.995928 0.946557
Fl 0.999021 1.000000 1.000000 0.999021 0.235079 -0.999269 0.235079 -0.976903 0.996133 -0.634499 0.996133 1.000000 0.999021 0.996189 0.950319
Flr 0.999021 1.000000 1.000000 0.999021 0.235079 -0.999269 0.235079 -0.976903 0.996133 -0.634499 0.996133 1.000000 0.999021 0.996189 0.950319
Fr 1.000000 0.999021 0.999021 1.000000 0.220731 -0.997585 0.220731 -0.972939 0.992605 -0.614955 0.992605 0.999021 1.000000 0.995928 0.946557
Fx 0.220731 0.235079 0.235079 0.220731 1.000000 -0.271075 1.000000 -0.434832 0.309461 -0.842048 0.309461 0.235079 0.220731 0.307638 0.523476
Fxl -0.997585 -0.999269 -0.999269 -0.997585 -0.271075 1.000000 -0.271075 0.984286 -0.998298 0.660006 -0.998298 -0.999269 -0.997585 -0.998115 -0.960981
Fxr 0.220731 0.235079 0.235079 0.220731 1.000000 -0.271075 1.000000 -0.434832 0.309461 -0.842048 0.309461 0.235079 0.220731 0.307638 0.523476
Fxy -0.972939 -0.976903 -0.976903 -0.972939 -0.434832 0.984286 -0.434832 1.000000 -0.988764 0.763266 -0.988764 -0.976903 -0.972939 -0.989281 -0.993717
Fy 0.992605 0.996133 0.996133 0.992605 0.309461 -0.998298 0.309461 -0.988764 1.000000 -0.699261 1.000000 0.996133 0.992605 0.996621 0.969117
Fyl -0.614955 -0.634499 -0.634499 -0.614955 -0.842048 0.660006 -0.842048 0.763266 -0.699261 1.000000 -0.699261 -0.634499 -0.614955 -0.676317 -0.814112
Fyr 0.992605 0.996133 0.996133 0.992605 0.309461 -0.998298 0.309461 -0.988764 1.000000 -0.699261 1.000000 0.996133 0.992605 0.996621 0.969117
factor_payment_1 0.999021 1.000000 1.000000 0.999021 0.235079 -0.999269 0.235079 -0.976903 0.996133 -0.634499 0.996133 1.000000 0.999021 0.996189 0.950319
factor_payment_2 1.000000 0.999021 0.999021 1.000000 0.220731 -0.997585 0.220731 -0.972939 0.992605 -0.614955 0.992605 0.999021 1.000000 0.995928 0.946557
mu 0.995928 0.996189 0.996189 0.995928 0.307638 -0.998115 0.307638 -0.989281 0.996621 -0.676317 0.996621 0.996189 0.995928 1.000000 0.971758
theta 0.946557 0.950319 0.950319 0.946557 0.523476 -0.960981 0.523476 -0.993717 0.969117 -0.814112 0.969117 0.950319 0.946557 0.971758 1.000000

In [30]:
# or a subset
viz.solution[['theta', 'factor_payment_1']].corr()


Out[30]:
theta factor_payment_1
theta 1.000000 0.950319
factor_payment_1 0.950319 1.000000

In [31]:
# or actual values!
viz.solution.corr().loc['theta']['factor_payment_1']


Out[31]:
0.95031918230948131

Plot the density for firm size

As you can see, the theta function is hump-shaped. Nothing special, but when calculating the pdf some arrangements have to be done for this: sort the thetas preserving the order (so we can relate them to their xs) and then use carefully the right x for calculating the pdf.

The principle of Philipp's trick is:

$pdf_x(x_i)$ can be interpreted as number of workers with ability x. $\theta_i$ is the size of the firms that employs workers of kind $x_i$. As all firms that match with workers type $x_i$ choose the same firm size, $pdf_x(x_i)/\theta_i$ is the number of firms of size $\theta_i$.

Say there are 100 workers with ability $x_i$, and their associated firm size $\theta_i$ is 2. Then there are $100/2 = 50$ $ \theta_i$ firms


In [32]:
fig, axes = plt.subplots(1, 3)
theta_pdf = viz.compute_pdf('theta', normalize=True)
theta_pdf.plot(ax=axes[0])
axes[0].set_xlabel(r'Firm size, $\theta$')
axes[0].set_title(r'pdf')

theta_cdf = viz.compute_cdf(theta_pdf)
theta_cdf.plot(ax=axes[1])
axes[1].set_title(r'cdf')
axes[1].set_xlabel(r'Firm size, $\theta$')

theta_sf = viz.compute_sf(theta_cdf)
theta_sf.plot(ax=axes[2])
axes[2].set_title(r'sf')
axes[2].set_xlabel(r'Firm size, $\theta$')

plt.tight_layout()
plt.show()


Distributions of factor payments

Can plot the distributions of average factor payments...


In [33]:
fig, axes = plt.subplots(1, 3)
factor_payment_1_pdf = viz.compute_pdf('factor_payment_1', normalize=True)
factor_payment_1_pdf.plot(ax=axes[0])
axes[0].set_title(r'pdf')

factor_payment_1_cdf = viz.compute_cdf(factor_payment_1_pdf)
factor_payment_1_cdf.plot(ax=axes[1])
axes[1].set_title(r'cdf')

factor_payment_1_sf = viz.compute_sf(factor_payment_1_cdf)
factor_payment_1_sf.plot(ax=axes[2])
axes[2].set_title(r'sf')

plt.tight_layout()
plt.show()



In [34]:
fig, axes = plt.subplots(1, 3)
factor_payment_2_pdf = viz.compute_pdf('factor_payment_2', normalize=True)
factor_payment_2_pdf.plot(ax=axes[0])
axes[0].set_title(r'pdf')

factor_payment_2_cdf = viz.compute_cdf(factor_payment_2_pdf)
factor_payment_2_cdf.plot(ax=axes[1])
axes[1].set_title(r'cdf')

factor_payment_2_sf = viz.compute_sf(factor_payment_2_cdf)
factor_payment_2_sf.plot(ax=axes[2])
axes[2].set_title(r'sf')

plt.tight_layout()
plt.show()


Widget


In [54]:
from IPython.html import widgets

In [55]:
def interactive_plot(viz, omega_A=0.25, omega_B=0.5, sigma_A=0.5, sigma_B=1.0,
                     loc1=1.0, mu1=0.0, sigma1=1.0, loc2=1.0, mu2=0.0, sigma2=1.0):
    # update new parameters as needed
    new_F_params = {'omega_A': omega_A, 'omega_B': omega_B,
                    'sigma_A': sigma_A, 'sigma_B': sigma_B}
    viz.solver.problem.F_params = new_F_params
    
    new_input1_params = {'loc1': loc1, 'mu1': mu1, 'sigma1': sigma1}
    viz.solver.problem.input1.params = new_input1_params
    
    new_input2_params = {'loc2': loc2, 'mu2': mu2, 'sigma2': sigma2}
    viz.solver.problem.input2.params = new_input2_params
    
    # solve the model using a hotstart initial guess
    domain = [viz.solver.problem.input1.lower, viz.solver.problem.input1.upper]
    initial_coefs = viz.solver._coefs_array_to_dict(viz.solver.result.x, viz.solver.degrees)
    viz.solver.solve(kind="Chebyshev",
                     coefs_dict=initial_coefs,
                     domain=domain,
                     method='hybr')
    
    if viz.solver.result.success:
        viz._Visualizer__solution = None  # should not need to access this!
        viz.interpolation_knots = np.linspace(domain[0], domain[1], 1000)
        viz.solution[['mu', 'theta']].plot(subplots=True)
        viz.normalized_residuals[['mu', 'theta']].plot(logy=True)
    else:
        print "Foobar!"

In [56]:
viz_widget = widgets.fixed(viz)

# widgets for the model parameters
eps = 1e-2
omega_A_widget = widgets.FloatSlider(value=0.25, min=eps, max=1-eps, step=eps,
                                     description=r"$\omega_A$")
sigma_A_widget = widgets.FloatSlider(value=0.5, min=eps, max=1-eps, step=eps,
                                     description=r"$\sigma_A$")
omega_B_widget = widgets.FloatSlider(value=0.5, min=eps, max=1-eps, step=eps,
                                     description=r"$\omega_B$")
sigma_B_widget = widgets.fixed(1.0)

# widgets for input distributions
loc_widget = widgets.fixed(1.0)
mu_1_widget = widgets.FloatSlider(value=0.0, min=-1.0, max=1.0, step=eps,
                                  description=r"$\mu_1$")
mu_2_widget = widgets.FloatSlider(value=0.0, min=-1.0, max=1.0, step=eps,
                                  description=r"$\mu_2$")
sigma_1_widget = widgets.FloatSlider(value=1.0, min=eps, max=2-eps, step=eps,
                                     description=r"$\sigma_1$")
sigma_2_widget = widgets.FloatSlider(value=1.0, min=eps, max=2-eps, step=eps,
                                     description=r"$\sigma_2$")

In [57]:
widgets.interact(interactive_plot, viz=viz_widget, omega_A=omega_A_widget,
                 sigma_A=sigma_A_widget, omega_B=omega_B_widget,
                 sigma_B=sigma_B_widget, sigma1=sigma_1_widget,
                 loc1=loc_widget, mu1 = mu_1_widget, 
                 loc2=loc_widget, sigma2=sigma_2_widget, mu2 = mu_2_widget)



In [34]:
# widget is changing the parameters of the underlying solver
solver.result.x


Out[34]:
array([  7.25026241e+01,   4.44958662e+01,  -2.32094502e+01,
         6.62025133e+00,   5.81461799e-01,  -1.55946410e+00,
         6.26485738e-01,   3.47032707e-02,  -1.34255798e-01,
         2.33827843e-02,   5.00922933e-02,  -5.15670685e-02,
         2.86571642e-02,  -1.41408791e-02,   1.11098441e-02,
        -1.18476805e-02,   1.15288324e-02,  -9.86307280e-03,
         8.03792622e-03,  -6.69413471e-03,   5.77789650e-03,
        -5.05085914e-03,   4.38977476e-03,  -3.78748392e-03,
         3.26426857e-03,  -2.82203708e-03,   2.44698900e-03,
        -2.12428421e-03,   1.84442215e-03,  -1.60187017e-03,
         1.39228307e-03,  -1.21137286e-03,   1.05506872e-03,
        -9.19854262e-04,   8.02819187e-04,  -7.01524100e-04,
         6.13866717e-04,  -5.38013838e-04,   4.72372899e-04,
        -4.15568976e-04,   3.66416864e-04,  -3.23892681e-04,
         2.87109830e-04,  -2.55299928e-04,   2.27797146e-04,
        -2.04024618e-04,   1.83482440e-04,  -1.65737213e-04,
         1.50413030e-04,  -1.37183732e-04,   1.25766216e-04,
        -1.15914624e-04,   1.07415296e-04,  -1.00082376e-04,
         9.37539977e-05,  -8.82889744e-05,   8.35639309e-05,
        -7.94707899e-05,   7.59145078e-05,  -7.28109983e-05,
         7.00853553e-05,  -6.76706220e-05,   6.55070496e-05,
        -6.35408793e-05,   6.17211507e-05,  -5.99950522e-05,
         5.83072166e-05,  -5.66099134e-05,   5.48781544e-05,
        -5.30977342e-05,   5.11925525e-05,  -4.89365611e-05,
         4.60308938e-05,  -4.25082051e-05,   3.92086051e-05,
        -1.87680507e-05,   6.10424640e+01,   3.86836905e+01,
        -2.21649811e+01,   5.26294133e+00,   2.01751068e+00,
        -2.41479138e+00,   8.91641452e-01,   3.96751691e-02,
        -1.67706401e-01,   1.85366413e-02,   7.15768521e-02,
        -6.46042588e-02,   2.92261437e-02,  -8.98904604e-03,
         5.76335767e-03,  -7.66261342e-03,   7.85910010e-03,
        -6.14814581e-03,   4.28490171e-03,  -3.12973675e-03,
         2.54158023e-03,  -2.15279042e-03,   1.78914710e-03,
        -1.45008533e-03,   1.17116479e-03,  -9.58354064e-04,
         7.94695948e-04,  -6.62679003e-04,   5.53059870e-04,
        -4.62188213e-04,   3.87674593e-04,  -3.26777656e-04,
         2.76738706e-04,  -2.35322435e-04,   2.00898051e-04,
        -1.72244304e-04,   1.48370419e-04,  -1.28442076e-04,
         1.11765505e-04,  -9.77761048e-05,   8.60169736e-05,
        -7.61151131e-05,   6.77628858e-05,  -6.07054673e-05,
         5.47317959e-05,  -4.96670756e-05,   4.53662817e-05,
        -4.17087266e-05,   3.85936833e-05,  -3.59368849e-05,
         3.36676811e-05,  -3.17266913e-05,   3.00638572e-05,
        -2.86368285e-05,   2.74096272e-05,  -2.63515534e-05,
         2.54362956e-05,  -2.46411808e-05,   2.39464638e-05,
        -2.33346025e-05,   2.27896763e-05,  -2.22973074e-05,
         2.18451093e-05,  -2.14225261e-05,   2.10180922e-05,
        -2.06143949e-05,   2.01871960e-05,  -1.97180633e-05,
         1.92152208e-05,  -1.87045927e-05,   1.81448944e-05,
        -1.73067164e-05,   1.58166339e-05,  -1.35683931e-05,
         1.12566297e-05,  -5.08591626e-06])

In [ ]: