def objectiveFuncMPC(center,spread): ''' Objective function of the beta-dist estimation, similar to cstwMPC. Minimizes the distance between simulated and actual mean semiannual MPCs by wealth quintile. Parameters ---------- center : float Mean of distribution of discount factor. spread : float Half width of span of discount factor. Returns ------- distance : float Distance between simulated and actual moments. ''' DiscFacSet = approxUniform(N=TypeCount,bot=center-spread,top=center+spread)[1] for j in range(TypeCount): Agents[j](DiscFac = DiscFacSet[j]) multiThreadCommands(Agents,['solve()','initializeSim()','simulate()']) aLvl_sim = np.concatenate([agent.aLvlNow for agent in Agents]) MPC_sim = np.concatenate([agent.MPCnow for agent in Agents]) MPC_alt = 1. - (1. - MPC_sim)**2 MPC_by_aLvl = calcSubpopAvg(MPC_alt,aLvl_sim,cutoffs) moments_sim = MPC_by_aLvl moments_diff = moments_sim - moments_data moments_diff[1:] *= 1 # Rescale Lorenz shares distance = np.sqrt(np.dot(moments_diff,moments_diff)) print('Tried center=' + str(center) + ', spread=' + str(spread) + ', got distance=' + str(distance)) print(moments_sim) return distance
def runRoszypalSchlaffmanExperiment(CorrAct, CorrPcvd, DiscFac_center, DiscFac_spread, numTypes, simPeriods): ''' Solve and simulate a consumer type who misperceives the extent of serial correlation of persistent shocks to income. Parameters ---------- CorrAct : float Serial correlation coefficient for *actual* persistent income. CorrPcvd : float List or array of *perceived* persistent income serial correlation DiscFac_center : float A measure of centrality for the distribution of the beta parameter, DiscFac. DiscFac_spread : float A measure of spread or diffusion for the distribution of the beta parameter. numTypes: int Number of different types of agents (distributed using DiscFac_center and DiscFac_spread) simPeriods: int Number of periods to simulate before calculating distributions Returns ------- AggWealthRatio: float Ratio of Aggregate wealth to income. Lorenz: numpy.array A list of two 1D array representing the Lorenz curve for assets in the most recent simulated period. Gini: float Gini coefficient for assets in the most recent simulated period. Avg_MPC: numpy.array Average marginal propensity to consume by income quintile in the latest simulated period. ''' # Make a dictionary to construct our consumer type ThisDict = copy(BaselineDict) ThisDict['PrstIncCorr'] = CorrAct # Make a N=numTypes point approximation to a uniform distribution of DiscFac DiscFac_list = approxUniform(N=numTypes,bot=DiscFac_center-DiscFac_spread,top=DiscFac_center+DiscFac_spread)[1] type_list = [] # Make a PersistentShockConsumerTypeX for each value of beta saved in DiscFac_list for i in range(len(DiscFac_list)): ThisDict['DiscFac'] = DiscFac_list[i] ThisType = PersistentShockConsumerTypeX(**ThisDict) # Make the consumer *believe* he will face a different level of persistence ThisType.PrstIncCorr = CorrPcvd ThisType.updatepLvlNextFunc() # *thinks* E[p_{t+1}] as a function of p_t is different than it is # Solve the consumer's problem with *perceived* persistence ThisType.solve() # Make the consumer type experience the true level of persistence during simulation ThisType.PrstIncCorr = CorrAct ThisType.updatepLvlNextFunc() # Simulate the agents for many periods ThisType.T_sim = simPeriods #ThisType.track_vars = ['cLvlNow','aLvlNow','pLvlNow','MPCnow'] ThisType.initializeSim() ThisType.simulate() type_list.append(ThisType) # Get the most recent simulated values of X = cLvlNow, MPCnow, aLvlNow, pLvlNow for all types cLvl_all = np.concatenate([ThisType.cLvlNow for ThisType in type_list]) aLvl_all = np.concatenate([ThisType.aLvlNow for ThisType in type_list]) MPC_all = np.concatenate([ThisType.MPCnow for ThisType in type_list]) pLvl_all = np.concatenate([ThisType.pLvlNow for ThisType in type_list]) # The ratio of aggregate assets over the income AggWealthRatio = np.mean(aLvl_all) / np.mean(pLvl_all) # first 1D array: Create points in the range (0,1) wealth_percentile = np.linspace(0.001,0.999,201) # second 1D array: Compute Lorenz shares for the created points Lorenz_init = getLorenzShares(aLvl_all, percentiles=wealth_percentile) # Stick 0 and 1 at the boundaries of both arrays to make it inclusive on the range [0,1] Lorenz_init = np.concatenate([[0],Lorenz_init,[1]]) wealth_percentile = np.concatenate([[0],wealth_percentile,[1]]) # Create a list of wealth_percentile 1D array and Lorenz Shares 1D array Lorenz = np.stack((wealth_percentile, Lorenz_init)) # Compute the Gini coefficient Gini = 1.0 - 2.0*np.mean(Lorenz_init[1]) # Compute the average MPC by income quintile in the latest simulated period Avg_MPC = calcSubpopAvg(MPC_all, pLvl_all, cutoffs=[(0.0,0.2), (0.2,0.4), (0.4,0.6), (0.6,0.8), (0.8,1.0)]) return AggWealthRatio, Lorenz, Gini, Avg_MPC
def calcStats(self, aLvlNow, pLvlNow, MPCnow, lIncomeLvl, 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. lIncomeLvl : [np.array] Arrays with labor income levels, 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) IncLvl = np.hstack(lIncomeLvl) 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(IncLvl * 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] IncLvl = IncLvl[order] age = age[order] Emp = Emp[order] aNrm = aLvl / pLvl # Normalized assets (wealth ratio) # Calculate overall population MPC and by subpopulations # MPC_cf_BPP is the MPC that is comparable with the empirical estimation method MPC_cf_BPP = 1.0 - 0.25 * ((1.0 - MPC) + (1.0 - MPC)**2 + (1.0 - MPC)**3 + (1.0 - MPC)**4) self.MPCall = np.sum( MPC_cf_BPP * CohortWeight) / np.sum(CohortWeight) employed = Emp unemployed = np.logical_not(employed) self.MPCbyWealthRatio = calcSubpopAvg(MPC_cf_BPP, aNrm, self.cutoffs, CohortWeight) self.MPCbyIncome = calcSubpopAvg(MPC_cf_BPP, 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( MPC_cf_BPP, weights=CohortWeight, percentiles=[ 2.0 / 3.0 ]) # Looking at consumers with MPCs in the top 1/3 these = MPC_cf_BPP > 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()