def makeMPCfig(kappa, weights): ''' Plot the CDF of the marginal propensity to consume. A sub-function of makeCSTWresults(). Parameters ---------- kappa : np.array Array of (annualized) marginal propensities to consume for the economy. weights : np.array Age-conditional weight array for the data in kappa. Returns ------- these_percents : np.array Array of percentiles of the marginal propensity to consume. kappa_percentiles : np.array Array of MPCs corresponding to the percentiles in these_percents. ''' these_percents = np.linspace(0.0001, 0.9999, 201) kappa_percentiles = getPercentiles(kappa, weights, percentiles=these_percents) plt.plot(kappa_percentiles, these_percents, '-k', linewidth=1.5) plt.xlabel('Marginal propensity to consume', fontsize=14) plt.ylabel('Cumulative probability', fontsize=14) plt.title('CDF of the MPC', fontsize=16) plt.show() return (these_percents, kappa_percentiles)
def makeMPCfig(kappa,weights): ''' Plot the CDF of the marginal propensity to consume. A sub-function of makeCSTWresults(). Parameters ---------- kappa : np.array Array of (annualized) marginal propensities to consume for the economy. weights : np.array Age-conditional weight array for the data in kappa. Returns ------- these_percents : np.array Array of percentiles of the marginal propensity to consume. kappa_percentiles : np.array Array of MPCs corresponding to the percentiles in these_percents. ''' these_percents = np.linspace(0.0001,0.9999,201) kappa_percentiles = getPercentiles(kappa,weights,percentiles=these_percents) plt.plot(kappa_percentiles,these_percents,'-k',linewidth=1.5) plt.xlabel('Marginal propensity to consume',fontsize=14) plt.ylabel('Cumulative probability',fontsize=14) plt.title('CDF of the MPC',fontsize=16) plt.show() return (these_percents,kappa_percentiles)
def makeValidationFigures(params, use_cohorts): ''' Make several figures that compare simulated outcomes from the estimated model to their data counterparts, for external validation. Parameters ---------- params : np.array Size 33 array of model parameters, like that used for estimation. use_cohorts : bool Indicator for whether or not to model differences across cohorts. Returns ------- None ''' # Make, solve, and simulate the types param_dict = convertVecToDict(params) if use_cohorts: type_list = makeMultiTypeWithCohorts(param_dict) else: type_list = makeMultiTypeSimple(param_dict) for this_type in type_list: this_type.track_vars.append('MedLvlNow') this_type.track_vars.append('iLvlNow') this_type.track_vars.append('HitCfloor') this_type.CalcExpectationFuncs = True this_type.DeleteSolution = False multiThreadCommandsFake(type_list, ['estimationAction()'], num_jobs=5) # Combine simulated data across all types aLvlHist = np.concatenate( [this_type.aLvlNow_hist for this_type in type_list], axis=1) hLvlHist = np.concatenate( [this_type.hLvlNow_hist for this_type in type_list], axis=1) OOPhist = np.concatenate( [this_type.OOPmedNow_hist for this_type in type_list], axis=1) MortHist = np.concatenate( [this_type.DiePrbNow_hist for this_type in type_list], axis=1) WeightHist = np.concatenate( [this_type.CumLivPrb_hist for this_type in type_list], axis=1) MedHist = np.concatenate( [this_type.MedLvlNow_hist for this_type in type_list], axis=1) # Combine data labels across types HealthTert = np.concatenate( [this_type.HealthTert for this_type in type_list]) HealthQuint = np.concatenate( [this_type.HealthQuint for this_type in type_list]) WealthQuint = np.concatenate( [this_type.WealthQuint for this_type in type_list]) IncQuint = np.concatenate( [this_type.IncQuintLong for this_type in type_list]) Sex = np.concatenate([this_type.SexLong for this_type in type_list]) # Combine in-data-span masking array across all types Active = hLvlHist > 0. InDataSpan = np.concatenate( [this_type.InDataSpanArray for this_type in type_list], axis=1) WeightAdj = InDataSpan * WeightHist # For each type, calculate the probability that no health investment is purchased at each age # and the probability the iLvlZeroRate = np.zeros((10, 25)) HitCfloorRate = np.zeros((10, 25)) for j in range(10): this_type = type_list[j] iLvlZero = this_type.iLvlNow_hist == 0. HitCfloor = this_type.HitCfloor_hist == 1. iLvlZeroSum = np.sum(iLvlZero * this_type.CumLivPrb_hist, axis=1) HitCfloorSum = np.sum(HitCfloor * this_type.CumLivPrb_hist, axis=1) PopSum = np.sum(this_type.CumLivPrb_hist, axis=1) iLvlZeroRate[j, :] = iLvlZeroSum / PopSum HitCfloorRate[j, :] = HitCfloorSum / PopSum # Calculate median (pseudo) bank balances for each type bLvl_init_median = np.zeros(10) for n in range(10): bLvl_init_median[n] = np.median( type_list[n].aLvlInit) + type_list[n].IncomeNow[2] # Extract deciles of health by age from the simulated data pctiles = [0.1, 0.2, 0.3, 0.4, 0.5, 0.6, 0.7, 0.8, 0.9] SimHealthPctiles = np.zeros((15, len(pctiles))) for t in range(15): SimHealthPctiles[t, :] = getPercentiles(hLvlHist[t, :], weights=WeightAdj[t, :], percentiles=pctiles) # Plot the probability of purchasing zero health investment by age, sex, and income colors = ['b', 'r', 'g', 'c', 'm'] AgeVec = np.linspace(67., 95., num=15) for n in range(5): plt.plot(AgeVec, iLvlZeroRate[n, :15], '-' + colors[n]) plt.xlabel('Age') plt.ylabel(r'Prob[$n_{it}=0$]') plt.title('Probability of Buying No Health Investment, Women') plt.legend([ 'Bottom quintile', 'Second quintile', 'Third quintile', 'Fourth quintile', 'Top quintile' ]) plt.savefig('../Figures/ZeroInvstWomen.pdf') plt.show() for n in range(5): plt.plot(AgeVec, iLvlZeroRate[n + 5, :15], '-' + colors[n]) plt.xlabel('Age') plt.ylabel(r'Prob[$n_{it}=0$]') plt.title('Probability of Buying No Health Investment, Men') plt.savefig('../Figures/ZeroInvstMen.pdf') plt.show() # Plot the probability of hitting the consumption floor by age, sex, and income colors = ['b', 'r', 'g', 'c', 'm'] AgeVec = np.linspace(67., 95., num=15) for n in range(5): plt.plot(AgeVec, HitCfloorRate[n, :15], '-' + colors[n]) plt.xlabel('Age') plt.ylabel(r'Prob[$c_{it}={c}$]') plt.title('Probability of Using Consumption Floor, Women') plt.legend([ 'Bottom quintile', 'Second quintile', 'Third quintile', 'Fourth quintile', 'Top quintile' ]) plt.savefig('../Figures/cFloorWomen.pdf') plt.show() for n in range(5): plt.plot(AgeVec, HitCfloorRate[n + 5, :15], '-' + colors[n]) plt.xlabel('Age') plt.ylabel(r'Prob[$c_{it}={c}$]') plt.title('Probability of Using Consumption Floor, Men') plt.savefig('../Figures/cFloorMen.pdf') plt.show() # Plot health investment as a function of market resources by type, holding h and Dev fixed B = np.linspace(1., 50., 201) some_ones = np.ones_like(B) hLvl = 0.6 Dev = 0.0 t = 0 Age = str(65 + 2 * t) for i in range(5): this_type = type_list[i] MedShk = np.exp(this_type.MedShkMeanFunc[t](hLvl) + Dev * this_type.MedShkStdFunc(hLvl)) I = np.maximum( this_type.solution[t].PolicyFunc.iFunc(B, hLvl * some_ones, MedShk * some_ones), 0.0) plt.plot(B, I, '-' + colors[i]) plt.xlabel(r'Bank balances $b_{it}$, \$10,000 (y2000)') plt.ylabel(r'Health investment $n_{it}$, \$10,000 (y2000)') plt.xlim([1., 50.]) plt.ylim([-0.01, 0.65]) #plt.legend(['Bottom quintile','Second quintile','Third quintile','Fourth quintile','Top quintile']) plt.title('Health Investment Function at Age ' + Age + ' by Income, Women') plt.savefig('../Figures/iFuncWomen.pdf') plt.show() for i in range(5): this_type = type_list[i + 5] MedShk = np.exp(this_type.MedShkMeanFunc[t](hLvl) + Dev * this_type.MedShkStdFunc(hLvl)) I = np.maximum( this_type.solution[t].PolicyFunc.iFunc(B, hLvl * some_ones, MedShk * some_ones), 0.0) plt.plot(B, I, '-' + colors[i]) plt.xlabel(r'Bank balances $b_{it}$, \$10,000 (y2000)') plt.ylabel(r'Health investment $n_{it}$, \$10,000 (y2000)') plt.xlim([1., 50.]) plt.ylim([-0.01, 0.65]) plt.legend([ 'Bottom quintile', 'Second quintile', 'Third quintile', 'Fourth quintile', 'Top quintile' ], loc=4) plt.title('Health Investment Function at Age ' + Age + ' by Income, Men') plt.savefig('../Figures/iFuncMen.pdf') plt.show() # Plot PDV of total medical expenses by health at median wealth at age 69-70 by income quintile and sex t = 2 H = np.linspace(0.0, 1.0, 201) for n in range(5): B = bLvl_init_median[n] * np.ones_like(H) M = type_list[n].solution[t].TotalMedPDVfunc(B, H) plt.plot(H, M, color=colors[n]) plt.xlim([0., 1.]) plt.ylim([0., 17]) plt.xlabel(r'Health capital $h_{it}$') plt.ylabel('PDV total medical care, $10,000 (y2000)') plt.legend([ 'Bottom quintile', 'Second quintile', 'Third quintile', 'Fourth quintile', 'Top quintile' ]) plt.title('Total Medical Expenses by Health and Income, Women') plt.savefig('../Figures/TotalMedPDVbyIncomeWomen.pdf') plt.show() for n in range(5, 10): B = bLvl_init_median[n] * np.ones_like(H) M = type_list[n].solution[t].TotalMedPDVfunc(B, H) plt.plot(H, M, color=colors[n - 5]) plt.xlim([0., 1.]) plt.ylim([0., 17]) plt.xlabel(r'Health capital $h_{it}$') plt.ylabel('PDV total medical care, $10,000 (y2000)') #plt.legend(['Bottom quintile','Second quintile','Third quintile','Fourth quintile','Top quintile']) plt.title('Total Medical Expenses by Health and Income, Men') plt.savefig('../Figures/TotalMedPDVbyIncomeMen.pdf') plt.show() # Plot PDV of OOP medical expenses by health at median wealth at age 69-70 by income quintile and sex colors = ['b', 'r', 'g', 'c', 'm'] t = 2 H = np.linspace(0.0, 1.0, 201) for n in range(5): B = bLvl_init_median[n] * np.ones_like(H) M = type_list[n].solution[t].OOPmedPDVfunc(B, H) plt.plot(H, M, color=colors[n]) plt.xlim([0., 1.]) plt.ylim([0., 3.5]) plt.xlabel(r'Health capital $h_{it}$') plt.ylabel('PDV OOP medical expenses, $10,000 (y2000)') #plt.legend(['Bottom quintile','Second quintile','Third quintile','Fourth quintile','Top quintile']) plt.title('OOP Medical Expenses by Health and Income, Women') plt.savefig('../Figures/OOPmedPDVbyIncomeWomen.pdf') plt.show() for n in range(5, 10): B = bLvl_init_median[n] * np.ones_like(H) M = type_list[n].solution[t].OOPmedPDVfunc(B, H) plt.plot(H, M, color=colors[n - 5]) plt.xlim([0., 1.]) plt.ylim([0., 3.5]) plt.xlabel(r'Health capital $h_{it}$') plt.ylabel('PDV total medical care, $10,000 (y2000)') #plt.legend(['Bottom quintile','Second quintile','Third quintile','Fourth quintile','Top quintile']) plt.title('OOP Medical Expenses by Health and Income, Men') plt.savefig('../Figures/OOPmedPDVbyIncomeMen.pdf') plt.show() # Plot life expectancy by health at median wealth at age 69-70 by income quintile and sex colors = ['b', 'r', 'g', 'c', 'm'] t = 2 H = np.linspace(0.0, 1.0, 201) for n in range(5): B = bLvl_init_median[n] * np.ones_like(H) M = type_list[n].solution[t].ExpectedLifeFunc(B, H) plt.plot(H, M, color=colors[n]) plt.xlim([0., 1.]) plt.ylim([0., 20.]) plt.xlabel(r'Health capital $h_{it}$') plt.ylabel('Remaining years of life expectancy') plt.legend([ 'Bottom quintile', 'Second quintile', 'Third quintile', 'Fourth quintile', 'Top quintile' ]) plt.title('Life Expectancy at Age 69 by Health and Income, Women') plt.savefig('../Figures/LifeExpectancybyIncomeWomen.pdf') plt.show() for n in range(5, 10): B = bLvl_init_median[n] * np.ones_like(H) M = type_list[n].solution[t].ExpectedLifeFunc(B, H) plt.plot(H, M, color=colors[n - 5]) plt.xlim([0., 1.]) plt.ylim([0., 20.]) plt.xlabel(r'Health capital $h_{it}$') plt.ylabel('Remaining years of life expectancy') plt.legend([ 'Bottom quintile', 'Second quintile', 'Third quintile', 'Fourth quintile', 'Top quintile' ]) plt.title('Life Expectancy at Age 69 by Health and Income, Men') plt.savefig('../Figures/LifeExpectancybyIncomeMen.pdf') plt.show() # Extract deciles of health from the HRS data DataHealthPctiles = np.zeros((15, len(pctiles))) for t in range(15): these = np.logical_and(Data.AgeBoolArray[:, :, t], Data.Alive) h_temp = Data.h_data[these] DataHealthPctiles[t, :] = getPercentiles(h_temp, percentiles=pctiles) # Plot deciles of health by by age plt.plot(AgeVec, SimHealthPctiles, '-k') plt.plot(AgeVec, DataHealthPctiles, '--k') plt.ylim(0., 1.) plt.ylabel('Health capital $h_{it}$') plt.xlabel('Age') plt.title('Simulated vs Actual Distribution of Health by Age') plt.savefig('../Figures/HealthDistribution.pdf') plt.show() OOPmodFunc = lambda x: np.log(10000 * x) # Extract many percentiles of OOP spending from the simulated data OOP_sim = OOPhist.flatten() Weight_temp = WeightAdj.flatten() CDFvalsSim = np.linspace(0.0001, 0.999, 1000) OOPsimCDF_A0 = getPercentiles(OOP_sim * 10000, weights=Weight_temp, percentiles=CDFvalsSim) OOPsimCDF_B0 = getPercentiles(OOPmodFunc(OOP_sim), weights=Weight_temp, percentiles=CDFvalsSim) # Extract some percentiles of OOP spending from the HRS data these = np.logical_and(Data.Alive, np.logical_not(np.isnan(Data.m_data))) OOP_data = Data.m_data[these] CDFvalsData = np.linspace(0.0001, 0.999, 500) OOPdataCDF_A0 = getPercentiles(OOP_data * 10000, weights=None, percentiles=CDFvalsData) OOPdataCDF_B0 = getPercentiles(OOPmodFunc(OOP_data), weights=None, percentiles=CDFvalsData) # Plot the CDF of log out-of-pocket medical spending plt.subplot(211) plt.title('CDF of OOP Medical Spending') plt.plot(OOPdataCDF_B0, CDFvalsData, '-r') plt.plot(OOPsimCDF_B0, CDFvalsSim, '-b') plt.xlim(8., 11.5) plt.ylim(0.85, 1.0) plt.xticks([ np.log(3000), np.log(6000), np.log(12000), np.log(24000), np.log(48000), np.log(96000) ], ['3000', '6000', '12000', '24000', '48000', '96000']) # Plot the CDF of out-of-pocket medical spending plt.subplot(212) plt.plot(OOPdataCDF_A0, CDFvalsData, '-r') plt.plot(OOPsimCDF_A0, CDFvalsSim, '-b') plt.xlim(0., 3000.) plt.ylim(0.0, 0.9) plt.xlabel('Out-of-pocket medical expenses, biannual') plt.ylabel('Cumulative distribution') plt.legend(['HRS data', 'Model'], loc=4) plt.savefig('../Figures/OOPdistribution.pdf') plt.show() # Calculate the serial correlation of log OOP medical spending in simulated data Med_sim = np.log(10000 * OOPhist + 1.) serial_corr_sim = np.zeros(15) serial_corr_sim_inc = np.zeros((15, 5)) for t in range(15): these = np.logical_and(WeightAdj[t + 1, :] > 0., WeightAdj[t + 1, :] < 1.) # Alive but not the first simulated period Med_t = Med_sim[t + 1, these] Med_tm1 = Med_sim[t, these] weight_reg = WeightAdj[t + 1, these] const_reg = np.ones_like(Med_t) regressors = np.transpose(np.vstack([const_reg, Med_tm1])) temp_model = WLS(Med_t, regressors, weights=weight_reg) temp_results = temp_model.fit() serial_corr_sim[t] = temp_results.rsquared for i in range(5): those = np.logical_and(these, IncQuint == i + 1) Med_t = Med_sim[t + 1, those] Med_tm1 = Med_sim[t, those] weight_reg = WeightAdj[t + 1, those] const_reg = np.ones_like(Med_t) regressors = np.transpose(np.vstack([const_reg, Med_tm1])) temp_model = WLS(Med_t, regressors, weights=weight_reg) temp_results = temp_model.fit() serial_corr_sim_inc[t, i] = temp_results.rsquared # Calculate the serial correlation of log OOP medical spending in HRS data DataExists = np.logical_and(np.logical_not(np.isnan(Data.m_data[:-1, :])), np.logical_not(np.isnan(Data.m_data[1:, :]))) BothAlive = np.logical_and(Data.Alive[:-1, :], Data.Alive[1:, :]) Usable = np.logical_and(DataExists, BothAlive) serial_corr_data = np.zeros(15) serial_corr_data_inc = np.zeros((15, 5)) Med_data = np.log(10000 * Data.m_data + 1.) for t in range(15): these = np.logical_and(Usable, Data.AgeBoolArray[:-1, :, t]) Med_t = Med_data[1:, :][these] Med_tm1 = Med_data[:-1, :][these] const_reg = np.ones_like(Med_t) regressors = np.transpose(np.vstack([const_reg, Med_tm1])) temp_model = OLS(Med_t, regressors) temp_results = temp_model.fit() serial_corr_data[t] = temp_results.rsquared for i in range(5): those = np.logical_and(these, Data.IncQuintBoolArray[:-1, :, i]) Med_t = Med_data[1:, :][those] Med_tm1 = Med_data[:-1, :][those] const_reg = np.ones_like(Med_t) regressors = np.transpose(np.vstack([const_reg, Med_tm1])) temp_model = OLS(Med_t, regressors) temp_results = temp_model.fit() serial_corr_data_inc[t, i] = temp_results.rsquared # Make a plot of serial correlation of OOP medical expenses plt.subplot(3, 2, 1) plt.plot(AgeVec, serial_corr_data, '-r') plt.plot(AgeVec, serial_corr_sim, '-b') plt.ylim(0, 0.5) plt.xticks([]) plt.text(75, 0.4, 'All individuals') plt.subplot(3, 2, 2) plt.plot(AgeVec, serial_corr_data_inc[:, 0], '-r') plt.plot(AgeVec, serial_corr_sim_inc[:, 0], '-b') plt.ylim(0, 0.5) plt.xticks([]) plt.yticks([]) plt.text(70, 0.4, 'Bottom income quintile') plt.subplot(3, 2, 3) plt.plot(AgeVec, serial_corr_data_inc[:, 1], '-r') plt.plot(AgeVec, serial_corr_sim_inc[:, 1], '-b') plt.ylim(0, 0.5) plt.xticks([]) plt.text(67, 0.4, 'Second income quintile') plt.ylabel('$R^2$ of regression of $\log(OOP_{t})$ on $\log(OOP_{t-1})$') plt.subplot(3, 2, 4) plt.plot(AgeVec, serial_corr_data_inc[:, 2], '-r') plt.plot(AgeVec, serial_corr_sim_inc[:, 2], '-b') plt.ylim(0, 0.5) plt.xticks([]) plt.yticks([]) plt.text(70, 0.4, 'Third income quintile') plt.subplot(3, 2, 5) plt.plot(AgeVec, serial_corr_data_inc[:, 3], '-r') plt.plot(AgeVec, serial_corr_sim_inc[:, 3], '-b') plt.ylim(0, 0.5) plt.xlabel('Age') plt.text(70, 0.4, 'Fourth income quintile') plt.subplot(3, 2, 6) plt.plot(AgeVec, serial_corr_data_inc[:, 4], '-r') plt.plot(AgeVec, serial_corr_sim_inc[:, 4], '-b') plt.ylim(0, 0.5) plt.xlabel('Age') plt.yticks([]) plt.text(70, 0.4, 'Top income quintile') plt.savefig('../Figures/SerialCorrOOP.pdf') plt.show() # Make a plot of serial correlation of OOP medical expenses plt.plot(AgeVec + 2, serial_corr_data, '-r') plt.plot(AgeVec + 2, serial_corr_sim, '-b') plt.xlabel('Age') plt.ylabel('$R^2$ of regression of $\log(OOP_{t})$ on $\log(OOP_{t-1})$') plt.legend(['HRS data', 'Model'], loc=1) plt.show() # Calculate mortality probability by age and income quintile in simulated data MortByIncAge_data = Data.MortByIncAge MortByIncAge_sim = np.zeros((5, 15)) MortByAge_sim = np.zeros(15) for t in range(15): THESE = np.logical_and(Active[t, :], InDataSpan[t, :]) Weight = WeightHist[t + 1, THESE] WeightSum = np.sum(Weight) Mort = MortHist[t + 1, THESE] MortByAge_sim[t] = np.dot(Mort, Weight) / WeightSum for i in range(5): right_inc = IncQuint == i + 1 these = np.logical_and(THESE, right_inc) Mort = MortHist[t + 1, these] Weight = WeightHist[t + 1, these] WeightSum = np.sum(Weight) MortByIncAge_sim[i, t] = np.dot(Mort, Weight) / WeightSum # Plot mortality probability by age and income quintile income_colors = ['b', 'r', 'g', 'm', 'c'] for i in range(5): plt.plot(AgeVec, MortByIncAge_sim[i, :] - MortByAge_sim, '-' + income_colors[i]) for i in range(5): plt.plot(AgeVec, MortByIncAge_data[i, :] - MortByAge_sim, '.' + income_colors[i]) plt.xlabel('Age') plt.ylabel('Relative death probability (biannual)') plt.title('Death Probability by Income Quintile') plt.legend([ 'Bottom quintile', 'Second quintile', 'Third quintile', 'Fourth quintile', 'Top quintile' ], loc=2) plt.savefig('../Figures/MortByIncAge.pdf') plt.show() # Plot the 99% confidence band of the health production function mean = np.array([-2.13369276099, 1.71842956397]) covar = np.array([[0.02248322, 0.01628292], [0.01628308, 0.01564192]]) dstn = multivariate_normal(mean, covar) N = 10000 M = 201 draws = dstn.rvs(10000) MedVec = np.linspace(0., 1.5, M) func_data = np.zeros((N, M)) def makeHealthProdFunc(LogSlope, LogCurve): LogJerk = 15.6 tempw = np.exp(LogJerk) HealthProd0 = 1. - tempw tempx = np.exp( LogSlope) # Slope of health production function at iLvl=0 HealthProd2 = np.exp(LogJerk - LogCurve) HealthProdFunc = lambda i: tempx / HealthProd0 * ( (i * HealthProd2**( (1. - HealthProd0) / HealthProd0) + HealthProd2** (1. / HealthProd0))**HealthProd0 - HealthProd2) return HealthProdFunc for n in range(N): f = makeHealthProdFunc(draws[n, 0], draws[n, 1]) func_data[n, :] = f(MedVec) f = makeHealthProdFunc(Params.test_param_vec[25], Params.test_param_vec[26]) CI_array = np.zeros((M, 2)) for m in range(M): CI_array[m, :] = getPercentiles(func_data[:, m], percentiles=[0.025, 0.975]) health_prod = f(MedVec) plt.plot(MedVec, health_prod, '-r') plt.plot(MedVec, CI_array[:, 0], '--k', linewidth=0.5) plt.plot(MedVec, CI_array[:, 1], '--k', linewidth=0.5) plt.xlim([-0.005, 1.5]) plt.ylim([0., None]) plt.xlabel('Health investment $n_{it}$, \$10,000 (y2000)') plt.ylabel('Health produced ') plt.title('Estimated Health Production Function') plt.legend([ 'Estimated health production function', 'Pointwise 95% confidence bounds' ], loc=4) plt.savefig('../Figures/HealthProdFunc.pdf') plt.show()
def FagerengObjFunc(center, spread, verbose=False): ''' Objective function for the quick and dirty structural estimation to fit Fagereng, Holm, and Natvik's Table 9 results with a basic infinite horizon consumption-saving model (with permanent and transitory income shocks). Parameters ---------- center : float Center of the uniform distribution of discount factors. spread : float Width of the uniform distribution of discount factors. verbose : bool When True, print to screen MPC table for these parameters. When False, print (center, spread, distance). Returns ------- distance : float Euclidean distance between simulated MPCs and (adjusted) Table 9 MPCs. ''' # Give our consumer types the requested discount factor distribution beta_set = approxUniform(N=TypeCount, bot=center - spread, top=center + spread)[1] for j in range(TypeCount): EstTypeList[j](DiscFac=beta_set[j]) # Solve and simulate all consumer types, then gather their wealth levels multiThreadCommands( EstTypeList, ['solve()', 'initializeSim()', 'simulate()', 'unpackcFunc()']) WealthNow = np.concatenate([ThisType.aLvlNow for ThisType in EstTypeList]) # Get wealth quartile cutoffs and distribute them to each consumer type quartile_cuts = getPercentiles(WealthNow, percentiles=[0.25, 0.50, 0.75]) for ThisType in EstTypeList: WealthQ = np.zeros(ThisType.AgentCount, dtype=int) for n in range(3): WealthQ[ThisType.aLvlNow > quartile_cuts[n]] += 1 ThisType(WealthQ=WealthQ) # Keep track of MPC sets in lists of lists of arrays MPC_set_list = [[[], [], [], []], [[], [], [], []], [[], [], [], []], [[], [], [], []]] # Calculate the MPC for each of the four lottery sizes for all agents for ThisType in EstTypeList: ThisType.simulate(1) c_base = ThisType.cNrmNow MPC_this_type = np.zeros((ThisType.AgentCount, 4)) for k in range(4): # Get MPC for all agents of this type Llvl = lottery_size[k] Lnrm = Llvl / ThisType.pLvlNow if do_secant: SplurgeNrm = Splurge / ThisType.pLvlNow mAdj = ThisType.mNrmNow + Lnrm - SplurgeNrm cAdj = ThisType.cFunc[0](mAdj) + SplurgeNrm MPC_this_type[:, k] = (cAdj - c_base) / Lnrm else: mAdj = ThisType.mNrmNow + Lnrm MPC_this_type[:, k] = cAdj = ThisType.cFunc[0].derivative(mAdj) # Sort the MPCs into the proper MPC sets for q in range(4): these = ThisType.WealthQ == q for k in range(4): MPC_set_list[k][q].append(MPC_this_type[these, k]) # Calculate average within each MPC set simulated_MPC_means = np.zeros((4, 4)) for k in range(4): for q in range(4): MPC_array = np.concatenate(MPC_set_list[k][q]) simulated_MPC_means[k, q] = np.mean(MPC_array) # Calculate Euclidean distance between simulated MPC averages and Table 9 targets diff = simulated_MPC_means - MPC_target if drop_corner: diff[0, 0] = 0.0 distance = np.sqrt(np.sum((diff)**2)) if verbose: print(simulated_MPC_means) else: print(center, spread, distance) return distance
def calcStats(self, aLvlNow, pLvlNow, MPCnow, TranShkNow, EmpNow, t_age, LorenzBool, ManyStatsBool): ''' Calculate various statistics about the current population in the economy. Parameters ---------- aLvlNow : [np.array] Arrays with end-of-period assets, listed by each ConsumerType in self.agents. pLvlNow : [np.array] Arrays with permanent income levels, listed by each ConsumerType in self.agents. MPCnow : [np.array] Arrays with marginal propensity to consume, listed by each ConsumerType in self.agents. TranShkNow : [np.array] Arrays with transitory income shocks, listed by each ConsumerType in self.agents. EmpNow : [np.array] Arrays with employment states: True if employed, False otherwise. t_age : [np.array] Arrays with periods elapsed since model entry, listed by each ConsumerType in self.agents. LorenzBool: bool Indicator for whether the Lorenz target points should be calculated. Usually False, only True when DiscFac has been identified for a particular nabla. ManyStatsBool: bool Indicator for whether a lot of statistics for tables should be calculated. Usually False, only True when parameters have been estimated and we want values for tables. Returns ------- None ''' # Combine inputs into single arrays aLvl = np.hstack(aLvlNow) pLvl = np.hstack(pLvlNow) age = np.hstack(t_age) TranShk = np.hstack(TranShkNow) Emp = np.hstack(EmpNow) # Calculate the capital to income ratio in the economy CohortWeight = self.PopGroFac**(-age) CapAgg = np.sum(aLvl * CohortWeight) IncAgg = np.sum(pLvl * TranShk * CohortWeight) KtoYnow = CapAgg / IncAgg self.KtoYnow = KtoYnow # Store Lorenz data if requested self.LorenzLong = np.nan if LorenzBool: order = np.argsort(aLvl) aLvl = aLvl[order] CohortWeight = CohortWeight[order] wealth_shares = getLorenzShares(aLvl, weights=CohortWeight, percentiles=self.LorenzPercentiles, presorted=True) self.Lorenz = wealth_shares if ManyStatsBool: self.LorenzLong = getLorenzShares(aLvl, weights=CohortWeight, percentiles=np.arange( 0.01, 1.0, 0.01), presorted=True) else: self.Lorenz = np.nan # Store nothing if we don't want Lorenz data # Calculate a whole bunch of statistics if requested if ManyStatsBool: # Reshape other inputs MPC = np.hstack(MPCnow) # Sort other data items if aLvl and CohortWeight were sorted if LorenzBool: pLvl = pLvl[order] MPC = MPC[order] TranShk = TranShk[order] age = age[order] Emp = Emp[order] aNrm = aLvl / pLvl # Normalized assets (wealth ratio) IncLvl = TranShk * pLvl # Labor income this period # Calculate overall population MPC and by subpopulations #MPCsixmonths = 1.0 - 0.25*((1.0 - MPC) + (1.0 - MPC)**2 + (1.0 - MPC)**3 + (1.0 - MPC)**4) MPCsixmonths = 1.0 - (1.0 - MPC)**2 self.MPCall = np.sum( MPCsixmonths * CohortWeight) / np.sum(CohortWeight) employed = Emp unemployed = np.logical_not(employed) if self.T_retire > 0: # Adjust for the lifecycle model, where agents might be retired instead unemployed = np.logical_and(unemployed, age < self.T_retire) employed = np.logical_and(employed, age < self.T_retire) retired = age >= self.T_retire else: retired = np.zeros_like(unemployed, dtype=bool) self.MPCunemployed = np.sum( MPCsixmonths[unemployed] * CohortWeight[unemployed]) / np.sum( CohortWeight[unemployed]) self.MPCemployed = np.sum( MPCsixmonths[employed] * CohortWeight[employed]) / np.sum( CohortWeight[employed]) self.MPCretired = np.sum( MPCsixmonths[retired] * CohortWeight[retired]) / np.sum( CohortWeight[retired]) self.MPCbyWealthRatio = calcSubpopAvg(MPCsixmonths, aNrm, self.cutoffs, CohortWeight) self.MPCbyIncome = calcSubpopAvg(MPCsixmonths, IncLvl, self.cutoffs, CohortWeight) # Calculate the wealth quintile distribution of "hand to mouth" consumers quintile_cuts = getPercentiles(aLvl, weights=CohortWeight, percentiles=[0.2, 0.4, 0.6, 0.8]) wealth_quintiles = np.ones(aLvl.size, dtype=int) wealth_quintiles[aLvl > quintile_cuts[0]] = 2 wealth_quintiles[aLvl > quintile_cuts[1]] = 3 wealth_quintiles[aLvl > quintile_cuts[2]] = 4 wealth_quintiles[aLvl > quintile_cuts[3]] = 5 MPC_cutoff = getPercentiles( MPCsixmonths, weights=CohortWeight, percentiles=[ 2.0 / 3.0 ]) # Looking at consumers with MPCs in the top 1/3 these = MPCsixmonths > MPC_cutoff in_top_third_MPC = wealth_quintiles[these] temp_weights = CohortWeight[these] hand_to_mouth_total = np.sum(temp_weights) hand_to_mouth_pct = [] for q in range(1, 6): hand_to_mouth_pct.append( np.sum(temp_weights[in_top_third_MPC == q]) / hand_to_mouth_total) self.HandToMouthPct = np.array(hand_to_mouth_pct) else: # If we don't want these stats, just put empty values in history self.MPCall = np.nan self.MPCunemployed = np.nan self.MPCemployed = np.nan self.MPCretired = np.nan self.MPCbyWealthRatio = np.nan self.MPCbyIncome = np.nan self.HandToMouthPct = np.nan
def makeCSTWstats(DiscFac, nabla, this_type_list, age_weight, lorenz_distance=0.0, save_name=None): ''' Displays (and saves) a bunch of statistics. Separate from makeCSTWresults() for compatibility with the aggregate shock model. Parameters ---------- DiscFac : float Center of the uniform distribution of discount factors nabla : float Width of the uniform distribution of discount factors this_type_list : [cstwMPCagent] List of agent types in the economy. age_weight : np.array Age-conditional array of weights for the wealth data. lorenz_distance : float Distance between simulated and actual Lorenz curves, for display. save_name : string Name to save the calculated results, for later use in producing figures and tables, etc. Returns ------- none ''' sim_length = this_type_list[0].sim_periods sim_wealth = (np.vstack( (this_type.W_history for this_type in this_type_list))).flatten() sim_wealth_short = (np.vstack( (this_type.W_history[0:sim_length, :] for this_type in this_type_list))).flatten() sim_kappa = (np.vstack( (this_type.kappa_history for this_type in this_type_list))).flatten() sim_income = (np.vstack((this_type.pHist[0:sim_length, :] * np.asarray(this_type.TranShkHist[0:sim_length, :]) for this_type in this_type_list))).flatten() sim_ratio = (np.vstack((this_type.W_history[0:sim_length, :] / this_type.pHist[0:sim_length, :] for this_type in this_type_list))).flatten() if Params.do_lifecycle: sim_unemp = (np.vstack((np.vstack(( this_type.IncUnemp == this_type.TranShkHist[0:Params.working_T, :], np.zeros((Params.retired_T + 1, this_type_list[0].Nagents), dtype=bool))) for this_type in this_type_list))).flatten() sim_emp = (np.vstack((np.vstack( (this_type.IncUnemp != this_type.TranShkHist[0:Params.working_T, :], np.zeros((Params.retired_T + 1, this_type_list[0].Nagents), dtype=bool))) for this_type in this_type_list))).flatten() sim_ret = (np.vstack((np.vstack( (np.zeros((Params.working_T, this_type_list[0].Nagents), dtype=bool), np.ones((Params.retired_T + 1, this_type_list[0].Nagents), dtype=bool))) for this_type in this_type_list))).flatten() else: sim_unemp = np.vstack( (this_type.IncUnemp == this_type.TranShkHist[0:sim_length, :] for this_type in this_type_list)).flatten() sim_emp = np.vstack( (this_type.IncUnemp != this_type.TranShkHist[0:sim_length, :] for this_type in this_type_list)).flatten() sim_ret = np.zeros(sim_emp.size, dtype=bool) sim_weight_all = np.tile(np.repeat(age_weight, this_type_list[0].Nagents), Params.pref_type_count) if Params.do_beta_dist and Params.do_lifecycle: kappa_mean_by_age_type = (np.mean(np.vstack( (this_type.kappa_history for this_type in this_type_list)), axis=1)).reshape( (Params.pref_type_count * 3, DropoutType.T_total + 1)) kappa_mean_by_age_pref = np.zeros( (Params.pref_type_count, DropoutType.T_total + 1)) + np.nan for j in range(Params.pref_type_count): kappa_mean_by_age_pref[ j, ] = Params.d_pct * kappa_mean_by_age_type[ 3 * j + 0, ] + Params.h_pct * kappa_mean_by_age_type[ 3 * j + 1, ] + Params.c_pct * kappa_mean_by_age_type[ 3 * j + 2, ] kappa_mean_by_age = np.mean(kappa_mean_by_age_pref, axis=0) kappa_lo_beta_by_age = kappa_mean_by_age_pref[0, :] kappa_hi_beta_by_age = kappa_mean_by_age_pref[Params.pref_type_count - 1, :] lorenz_fig_data = makeLorenzFig(Params.SCF_wealth, Params.SCF_weights, sim_wealth, sim_weight_all) mpc_fig_data = makeMPCfig(sim_kappa, sim_weight_all) kappa_all = calcWeightedAvg( np.vstack((this_type.kappa_history for this_type in this_type_list)), np.tile(age_weight / float(Params.pref_type_count), Params.pref_type_count)) kappa_unemp = np.sum( sim_kappa[sim_unemp] * sim_weight_all[sim_unemp]) / np.sum( sim_weight_all[sim_unemp]) kappa_emp = np.sum(sim_kappa[sim_emp] * sim_weight_all[sim_emp]) / np.sum( sim_weight_all[sim_emp]) kappa_ret = np.sum(sim_kappa[sim_ret] * sim_weight_all[sim_ret]) / np.sum( sim_weight_all[sim_ret]) my_cutoffs = [(0.99, 1), (0.9, 1), (0.8, 1), (0.6, 0.8), (0.4, 0.6), (0.2, 0.4), (0.0, 0.2)] kappa_by_ratio_groups = calcSubpopAvg(sim_kappa, sim_ratio, my_cutoffs, sim_weight_all) kappa_by_income_groups = calcSubpopAvg(sim_kappa, sim_income, my_cutoffs, sim_weight_all) quintile_points = getPercentiles(sim_wealth_short, weights=sim_weight_all, percentiles=[0.2, 0.4, 0.6, 0.8]) wealth_quintiles = np.ones(sim_wealth_short.size, dtype=int) wealth_quintiles[sim_wealth_short > quintile_points[0]] = 2 wealth_quintiles[sim_wealth_short > quintile_points[1]] = 3 wealth_quintiles[sim_wealth_short > quintile_points[2]] = 4 wealth_quintiles[sim_wealth_short > quintile_points[3]] = 5 MPC_cutoff = getPercentiles(sim_kappa, weights=sim_weight_all, percentiles=[2.0 / 3.0]) these_quintiles = wealth_quintiles[sim_kappa > MPC_cutoff] these_weights = sim_weight_all[sim_kappa > MPC_cutoff] hand_to_mouth_total = np.sum(these_weights) hand_to_mouth_pct = [] for q in range(5): hand_to_mouth_pct.append( np.sum(these_weights[these_quintiles == (q + 1)]) / hand_to_mouth_total) results_string = 'Estimate is DiscFac=' + str(DiscFac) + ', nabla=' + str( nabla) + '\n' results_string += 'Lorenz distance is ' + str(lorenz_distance) + '\n' results_string += 'Average MPC for all consumers is ' + mystr( kappa_all) + '\n' results_string += 'Average MPC in the top percentile of W/Y is ' + mystr( kappa_by_ratio_groups[0]) + '\n' results_string += 'Average MPC in the top decile of W/Y is ' + mystr( kappa_by_ratio_groups[1]) + '\n' results_string += 'Average MPC in the top quintile of W/Y is ' + mystr( kappa_by_ratio_groups[2]) + '\n' results_string += 'Average MPC in the second quintile of W/Y is ' + mystr( kappa_by_ratio_groups[3]) + '\n' results_string += 'Average MPC in the middle quintile of W/Y is ' + mystr( kappa_by_ratio_groups[4]) + '\n' results_string += 'Average MPC in the fourth quintile of W/Y is ' + mystr( kappa_by_ratio_groups[5]) + '\n' results_string += 'Average MPC in the bottom quintile of W/Y is ' + mystr( kappa_by_ratio_groups[6]) + '\n' results_string += 'Average MPC in the top percentile of y is ' + mystr( kappa_by_income_groups[0]) + '\n' results_string += 'Average MPC in the top decile of y is ' + mystr( kappa_by_income_groups[1]) + '\n' results_string += 'Average MPC in the top quintile of y is ' + mystr( kappa_by_income_groups[2]) + '\n' results_string += 'Average MPC in the second quintile of y is ' + mystr( kappa_by_income_groups[3]) + '\n' results_string += 'Average MPC in the middle quintile of y is ' + mystr( kappa_by_income_groups[4]) + '\n' results_string += 'Average MPC in the fourth quintile of y is ' + mystr( kappa_by_income_groups[5]) + '\n' results_string += 'Average MPC in the bottom quintile of y is ' + mystr( kappa_by_income_groups[6]) + '\n' results_string += 'Average MPC for the employed is ' + mystr( kappa_emp) + '\n' results_string += 'Average MPC for the unemployed is ' + mystr( kappa_unemp) + '\n' results_string += 'Average MPC for the retired is ' + mystr( kappa_ret) + '\n' results_string += 'Of the population with the 1/3 highest MPCs...' + '\n' results_string += mystr( hand_to_mouth_pct[0] * 100) + '% are in the bottom wealth quintile,' + '\n' results_string += mystr( hand_to_mouth_pct[1] * 100) + '% are in the second wealth quintile,' + '\n' results_string += mystr(hand_to_mouth_pct[2] * 100) + '% are in the third wealth quintile,' + '\n' results_string += mystr( hand_to_mouth_pct[3] * 100) + '% are in the fourth wealth quintile,' + '\n' results_string += 'and ' + mystr( hand_to_mouth_pct[4] * 100) + '% are in the top wealth quintile.' + '\n' print(results_string) if save_name is not None: with open('./Results/' + save_name + 'LorenzFig.txt', 'w') as f: my_writer = csv.writer( f, delimiter='\t', ) for j in range(len(lorenz_fig_data[0])): my_writer.writerow([ lorenz_fig_data[0][j], lorenz_fig_data[1][j], lorenz_fig_data[2][j] ]) f.close() with open('./Results/' + save_name + 'MPCfig.txt', 'w') as f: my_writer = csv.writer(f, delimiter='\t') for j in range(len(mpc_fig_data[0])): my_writer.writerow([lorenz_fig_data[0][j], mpc_fig_data[1][j]]) f.close() if Params.do_beta_dist and Params.do_lifecycle: with open('./Results/' + save_name + 'KappaByAge.txt', 'w') as f: my_writer = csv.writer(f, delimiter='\t') for j in range(len(kappa_mean_by_age)): my_writer.writerow([ kappa_mean_by_age[j], kappa_lo_beta_by_age[j], kappa_hi_beta_by_age[j] ]) f.close() with open('./Results/' + save_name + 'Results.txt', 'w') as f: f.write(results_string) f.close()
# Make a boolean array of usable observations (for non-mortality moments) BelowCohort16 = np.tile(np.reshape(cohort_data,(1,obs)),(8,1)) < 16 Alive = h_data > 0. NotInit = np.logical_not(InitBoolArray) Useable = np.logical_and(BelowCohort16,np.logical_and(Alive,NotInit)) # Calculate health quintiles at data entry by age (and overwrite old health tertile info) health_tertile_cuts = np.zeros((2,15)) health_quintile_cuts = np.zeros((4,15)) health_tert_data = np.zeros_like(health_tert_data_alt,dtype=int) health_quint_data = np.zeros_like(health_tert_data_alt,dtype=int) for a in range(15): these = np.logical_and(Alive,AgeBoolArray[:,:,a]) health_temp = h_data[these] health_quintile_cuts[:,a] = getPercentiles(health_temp,percentiles=[0.2,0.4,0.6,0.8]) health_tertile_cuts[:,a] = getPercentiles(health_temp,percentiles=[0.333,0.666]) those = BornBoolArray[a,:] h_init_temp = h_init[those] health_quint_data[those] = np.searchsorted(health_quintile_cuts[:,a],h_init_temp) + 1 health_tert_data[those] = np.searchsorted(health_tertile_cuts[:,a],h_init_temp) + 1 # Make data objects for the health production pre-estimation UseableAlt = np.logical_and(BelowCohort16,Alive) inc_quint_data_rep = np.tile(np.reshape(inc_quint_data,(1,obs)),(8,1)) wealth_quint_data_rep = np.tile(np.reshape(wealth_quint_data,(1,obs)),(8,1)) sex_data_rep = np.tile(np.reshape(sex_data,(1,obs)),(8,1)) WealthArraysBySexIncAge = [] HealthArraysBySexIncAge = [] WealthQuintArraysBySexIncAge = [] for s in range(2):
def makeCSTWstats(DiscFac,nabla,this_type_list,age_weight,lorenz_distance=0.0,save_name=None): ''' Displays (and saves) a bunch of statistics. Separate from makeCSTWresults() for compatibility with the aggregate shock model. Parameters ---------- DiscFac : float Center of the uniform distribution of discount factors nabla : float Width of the uniform distribution of discount factors this_type_list : [cstwMPCagent] List of agent types in the economy. age_weight : np.array Age-conditional array of weights for the wealth data. lorenz_distance : float Distance between simulated and actual Lorenz curves, for display. save_name : string Name to save the calculated results, for later use in producing figures and tables, etc. Returns ------- none ''' sim_length = this_type_list[0].sim_periods sim_wealth = (np.vstack((this_type.W_history for this_type in this_type_list))).flatten() sim_wealth_short = (np.vstack((this_type.W_history[0:sim_length,:] for this_type in this_type_list))).flatten() sim_kappa = (np.vstack((this_type.kappa_history for this_type in this_type_list))).flatten() sim_income = (np.vstack((this_type.pHist[0:sim_length,:]*np.asarray(this_type.TranShkHist[0:sim_length,:]) for this_type in this_type_list))).flatten() sim_ratio = (np.vstack((this_type.W_history[0:sim_length,:]/this_type.pHist[0:sim_length,:] for this_type in this_type_list))).flatten() if Params.do_lifecycle: sim_unemp = (np.vstack((np.vstack((this_type.IncUnemp == this_type.TranShkHist[0:Params.working_T,:],np.zeros((Params.retired_T+1,this_type_list[0].Nagents),dtype=bool))) for this_type in this_type_list))).flatten() sim_emp = (np.vstack((np.vstack((this_type.IncUnemp != this_type.TranShkHist[0:Params.working_T,:],np.zeros((Params.retired_T+1,this_type_list[0].Nagents),dtype=bool))) for this_type in this_type_list))).flatten() sim_ret = (np.vstack((np.vstack((np.zeros((Params.working_T,this_type_list[0].Nagents),dtype=bool),np.ones((Params.retired_T+1,this_type_list[0].Nagents),dtype=bool))) for this_type in this_type_list))).flatten() else: sim_unemp = np.vstack((this_type.IncUnemp == this_type.TranShkHist[0:sim_length,:] for this_type in this_type_list)).flatten() sim_emp = np.vstack((this_type.IncUnemp != this_type.TranShkHist[0:sim_length,:] for this_type in this_type_list)).flatten() sim_ret = np.zeros(sim_emp.size,dtype=bool) sim_weight_all = np.tile(np.repeat(age_weight,this_type_list[0].Nagents),Params.pref_type_count) if Params.do_beta_dist and Params.do_lifecycle: kappa_mean_by_age_type = (np.mean(np.vstack((this_type.kappa_history for this_type in this_type_list)),axis=1)).reshape((Params.pref_type_count*3,DropoutType.T_total+1)) kappa_mean_by_age_pref = np.zeros((Params.pref_type_count,DropoutType.T_total+1)) + np.nan for j in range(Params.pref_type_count): kappa_mean_by_age_pref[j,] = Params.d_pct*kappa_mean_by_age_type[3*j+0,] + Params.h_pct*kappa_mean_by_age_type[3*j+1,] + Params.c_pct*kappa_mean_by_age_type[3*j+2,] kappa_mean_by_age = np.mean(kappa_mean_by_age_pref,axis=0) kappa_lo_beta_by_age = kappa_mean_by_age_pref[0,:] kappa_hi_beta_by_age = kappa_mean_by_age_pref[Params.pref_type_count-1,:] lorenz_fig_data = makeLorenzFig(Params.SCF_wealth,Params.SCF_weights,sim_wealth,sim_weight_all) mpc_fig_data = makeMPCfig(sim_kappa,sim_weight_all) kappa_all = calcWeightedAvg(np.vstack((this_type.kappa_history for this_type in this_type_list)),np.tile(age_weight/float(Params.pref_type_count),Params.pref_type_count)) kappa_unemp = np.sum(sim_kappa[sim_unemp]*sim_weight_all[sim_unemp])/np.sum(sim_weight_all[sim_unemp]) kappa_emp = np.sum(sim_kappa[sim_emp]*sim_weight_all[sim_emp])/np.sum(sim_weight_all[sim_emp]) kappa_ret = np.sum(sim_kappa[sim_ret]*sim_weight_all[sim_ret])/np.sum(sim_weight_all[sim_ret]) my_cutoffs = [(0.99,1),(0.9,1),(0.8,1),(0.6,0.8),(0.4,0.6),(0.2,0.4),(0.0,0.2)] kappa_by_ratio_groups = calcSubpopAvg(sim_kappa,sim_ratio,my_cutoffs,sim_weight_all) kappa_by_income_groups = calcSubpopAvg(sim_kappa,sim_income,my_cutoffs,sim_weight_all) quintile_points = getPercentiles(sim_wealth_short,weights=sim_weight_all,percentiles=[0.2, 0.4, 0.6, 0.8]) wealth_quintiles = np.ones(sim_wealth_short.size,dtype=int) wealth_quintiles[sim_wealth_short > quintile_points[0]] = 2 wealth_quintiles[sim_wealth_short > quintile_points[1]] = 3 wealth_quintiles[sim_wealth_short > quintile_points[2]] = 4 wealth_quintiles[sim_wealth_short > quintile_points[3]] = 5 MPC_cutoff = getPercentiles(sim_kappa,weights=sim_weight_all,percentiles=[2.0/3.0]) these_quintiles = wealth_quintiles[sim_kappa > MPC_cutoff] these_weights = sim_weight_all[sim_kappa > MPC_cutoff] hand_to_mouth_total = np.sum(these_weights) hand_to_mouth_pct = [] for q in range(5): hand_to_mouth_pct.append(np.sum(these_weights[these_quintiles == (q+1)])/hand_to_mouth_total) results_string = 'Estimate is DiscFac=' + str(DiscFac) + ', nabla=' + str(nabla) + '\n' results_string += 'Lorenz distance is ' + str(lorenz_distance) + '\n' results_string += 'Average MPC for all consumers is ' + mystr(kappa_all) + '\n' results_string += 'Average MPC in the top percentile of W/Y is ' + mystr(kappa_by_ratio_groups[0]) + '\n' results_string += 'Average MPC in the top decile of W/Y is ' + mystr(kappa_by_ratio_groups[1]) + '\n' results_string += 'Average MPC in the top quintile of W/Y is ' + mystr(kappa_by_ratio_groups[2]) + '\n' results_string += 'Average MPC in the second quintile of W/Y is ' + mystr(kappa_by_ratio_groups[3]) + '\n' results_string += 'Average MPC in the middle quintile of W/Y is ' + mystr(kappa_by_ratio_groups[4]) + '\n' results_string += 'Average MPC in the fourth quintile of W/Y is ' + mystr(kappa_by_ratio_groups[5]) + '\n' results_string += 'Average MPC in the bottom quintile of W/Y is ' + mystr(kappa_by_ratio_groups[6]) + '\n' results_string += 'Average MPC in the top percentile of y is ' + mystr(kappa_by_income_groups[0]) + '\n' results_string += 'Average MPC in the top decile of y is ' + mystr(kappa_by_income_groups[1]) + '\n' results_string += 'Average MPC in the top quintile of y is ' + mystr(kappa_by_income_groups[2]) + '\n' results_string += 'Average MPC in the second quintile of y is ' + mystr(kappa_by_income_groups[3]) + '\n' results_string += 'Average MPC in the middle quintile of y is ' + mystr(kappa_by_income_groups[4]) + '\n' results_string += 'Average MPC in the fourth quintile of y is ' + mystr(kappa_by_income_groups[5]) + '\n' results_string += 'Average MPC in the bottom quintile of y is ' + mystr(kappa_by_income_groups[6]) + '\n' results_string += 'Average MPC for the employed is ' + mystr(kappa_emp) + '\n' results_string += 'Average MPC for the unemployed is ' + mystr(kappa_unemp) + '\n' results_string += 'Average MPC for the retired is ' + mystr(kappa_ret) + '\n' results_string += 'Of the population with the 1/3 highest MPCs...' + '\n' results_string += mystr(hand_to_mouth_pct[0]*100) + '% are in the bottom wealth quintile,' + '\n' results_string += mystr(hand_to_mouth_pct[1]*100) + '% are in the second wealth quintile,' + '\n' results_string += mystr(hand_to_mouth_pct[2]*100) + '% are in the third wealth quintile,' + '\n' results_string += mystr(hand_to_mouth_pct[3]*100) + '% are in the fourth wealth quintile,' + '\n' results_string += 'and ' + mystr(hand_to_mouth_pct[4]*100) + '% are in the top wealth quintile.' + '\n' print(results_string) if save_name is not None: with open('./Results/' + save_name + 'LorenzFig.txt','w') as f: my_writer = csv.writer(f, delimiter='\t',) for j in range(len(lorenz_fig_data[0])): my_writer.writerow([lorenz_fig_data[0][j], lorenz_fig_data[1][j], lorenz_fig_data[2][j]]) f.close() with open('./Results/' + save_name + 'MPCfig.txt','w') as f: my_writer = csv.writer(f, delimiter='\t') for j in range(len(mpc_fig_data[0])): my_writer.writerow([lorenz_fig_data[0][j], mpc_fig_data[1][j]]) f.close() if Params.do_beta_dist and Params.do_lifecycle: with open('./Results/' + save_name + 'KappaByAge.txt','w') as f: my_writer = csv.writer(f, delimiter='\t') for j in range(len(kappa_mean_by_age)): my_writer.writerow([kappa_mean_by_age[j], kappa_lo_beta_by_age[j], kappa_hi_beta_by_age[j]]) f.close() with open('./Results/' + save_name + 'Results.txt','w') as f: f.write(results_string) f.close()
def calcStats(self,aLvlNow,pLvlNow,MPCnow,TranShkNow,EmpNow,t_age,LorenzBool,ManyStatsBool): ''' Calculate various statistics about the current population in the economy. Parameters ---------- aLvlNow : [np.array] Arrays with end-of-period assets, listed by each ConsumerType in self.agents. pLvlNow : [np.array] Arrays with permanent income levels, listed by each ConsumerType in self.agents. MPCnow : [np.array] Arrays with marginal propensity to consume, listed by each ConsumerType in self.agents. TranShkNow : [np.array] Arrays with transitory income shocks, listed by each ConsumerType in self.agents. EmpNow : [np.array] Arrays with employment states: True if employed, False otherwise. t_age : [np.array] Arrays with periods elapsed since model entry, listed by each ConsumerType in self.agents. LorenzBool: bool Indicator for whether the Lorenz target points should be calculated. Usually False, only True when DiscFac has been identified for a particular nabla. ManyStatsBool: bool Indicator for whether a lot of statistics for tables should be calculated. Usually False, only True when parameters have been estimated and we want values for tables. Returns ------- None ''' # Combine inputs into single arrays aLvl = np.hstack(aLvlNow) pLvl = np.hstack(pLvlNow) age = np.hstack(t_age) TranShk = np.hstack(TranShkNow) Emp = np.hstack(EmpNow) # Calculate the capital to income ratio in the economy CohortWeight = self.PopGroFac**(-age) CapAgg = np.sum(aLvl*CohortWeight) IncAgg = np.sum(pLvl*TranShk*CohortWeight) KtoYnow = CapAgg/IncAgg self.KtoYnow = KtoYnow # Store Lorenz data if requested self.LorenzLong = np.nan if LorenzBool: order = np.argsort(aLvl) aLvl = aLvl[order] CohortWeight = CohortWeight[order] wealth_shares = getLorenzShares(aLvl,weights=CohortWeight,percentiles=self.LorenzPercentiles,presorted=True) self.Lorenz = wealth_shares if ManyStatsBool: self.LorenzLong = getLorenzShares(aLvl,weights=CohortWeight,percentiles=np.arange(0.01,1.0,0.01),presorted=True) else: self.Lorenz = np.nan # Store nothing if we don't want Lorenz data # Calculate a whole bunch of statistics if requested if ManyStatsBool: # Reshape other inputs MPC = np.hstack(MPCnow) # Sort other data items if aLvl and CohortWeight were sorted if LorenzBool: pLvl = pLvl[order] MPC = MPC[order] TranShk = TranShk[order] age = age[order] Emp = Emp[order] aNrm = aLvl/pLvl # Normalized assets (wealth ratio) IncLvl = TranShk*pLvl # Labor income this period # Calculate overall population MPC and by subpopulations MPCannual = 1.0 - (1.0 - MPC)**4 self.MPCall = np.sum(MPCannual*CohortWeight)/np.sum(CohortWeight) employed = Emp unemployed = np.logical_not(employed) if self.T_retire > 0: # Adjust for the lifecycle model, where agents might be retired instead unemployed = np.logical_and(unemployed,age < self.T_retire) employed = np.logical_and(employed,age < self.T_retire) retired = age >= self.T_retire else: retired = np.zeros_like(unemployed,dtype=bool) self.MPCunemployed = np.sum(MPCannual[unemployed]*CohortWeight[unemployed])/np.sum(CohortWeight[unemployed]) self.MPCemployed = np.sum(MPCannual[employed]*CohortWeight[employed])/np.sum(CohortWeight[employed]) self.MPCretired = np.sum(MPCannual[retired]*CohortWeight[retired])/np.sum(CohortWeight[retired]) self.MPCbyWealthRatio = calcSubpopAvg(MPCannual,aNrm,self.cutoffs,CohortWeight) self.MPCbyIncome = calcSubpopAvg(MPCannual,IncLvl,self.cutoffs,CohortWeight) # Calculate the wealth quintile distribution of "hand to mouth" consumers quintile_cuts = getPercentiles(aLvl,weights=CohortWeight,percentiles=[0.2, 0.4, 0.6, 0.8]) wealth_quintiles = np.ones(aLvl.size,dtype=int) wealth_quintiles[aLvl > quintile_cuts[0]] = 2 wealth_quintiles[aLvl > quintile_cuts[1]] = 3 wealth_quintiles[aLvl > quintile_cuts[2]] = 4 wealth_quintiles[aLvl > quintile_cuts[3]] = 5 MPC_cutoff = getPercentiles(MPCannual,weights=CohortWeight,percentiles=[2.0/3.0]) # Looking at consumers with MPCs in the top 1/3 these = MPCannual > MPC_cutoff in_top_third_MPC = wealth_quintiles[these] temp_weights = CohortWeight[these] hand_to_mouth_total = np.sum(temp_weights) hand_to_mouth_pct = [] for q in range(1,6): hand_to_mouth_pct.append(np.sum(temp_weights[in_top_third_MPC == q])/hand_to_mouth_total) self.HandToMouthPct = np.array(hand_to_mouth_pct) else: # If we don't want these stats, just put empty values in history self.MPCall = np.nan self.MPCunemployed = np.nan self.MPCemployed = np.nan self.MPCretired = np.nan self.MPCbyWealthRatio = np.nan self.MPCbyIncome = np.nan self.HandToMouthPct = np.nan