def calcLorenzDistance(SomeTypes): ''' Calculates the Euclidean distance between the simulated and actual (from SCF data) Lorenz curves at the 20th, 40th, 60th, and 80th percentiles. Parameters ---------- SomeTypes : [AgentType] List of AgentTypes that have been solved and simulated. Current levels of individual assets should be stored in the attribute aLvl. Returns ------- lorenz_distance : float Euclidean distance (square root of sum of squared differences) between simulated and actual Lorenz curves. ''' # Define empirical Lorenz curve points lorenz_SCF = np.array([-0.00183091, 0.0104425 , 0.0552605 , 0.1751907 ]) # Extract asset holdings from all consumer types aLvl_sim = np.concatenate([ThisType.aLvl for ThisType in MyTypes]) # Calculate simulated Lorenz curve points lorenz_sim = get_lorenz_shares(aLvl_sim,percentiles=[0.2,0.4,0.6,0.8]) # Calculate the Euclidean distance between the simulated and actual Lorenz curves lorenz_distance = np.sqrt(np.sum((lorenz_SCF - lorenz_sim)**2)) # Return the Lorenz distance return lorenz_distance
# %% [markdown] # ## Plotting the Lorenz Curve # %% # Plot Lorenz curves for model with uniform distribution of time preference from HARK.datasets import load_SCF_wealth_weights from HARK.utilities import get_lorenz_shares, get_percentiles SCF_wealth, SCF_weights = load_SCF_wealth_weights() pctiles = np.linspace(0.001, 0.999, 200) sim_wealth = np.concatenate( [ThisType.state_now["aLvl"] for ThisType in MyTypes]) SCF_Lorenz_points = get_lorenz_shares(SCF_wealth, weights=SCF_weights, percentiles=pctiles) sim_Lorenz_points = get_lorenz_shares(sim_wealth, percentiles=pctiles) plt.plot(pctiles, SCF_Lorenz_points, '--k') plt.plot(pctiles, sim_Lorenz_points, '-b') plt.xlabel('Percentile of net worth') plt.ylabel('Cumulative share of wealth') plt.show(block=False) # %% [markdown] # ## Calculating the Lorenz Distance at Targets # # Now we want to construct a function that calculates the Euclidean distance between simulated and actual Lorenz curves at the four percentiles of interest: 20, 40, 60, and 80. # %% [markdown] # ## The Distribution Of the Marginal Propensity to Consume
def calc_stats(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) EmpNow = 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 = get_lorenz_shares( aLvl, weights=CohortWeight, percentiles=self.LorenzPercentiles, presorted=True) self.Lorenz = wealth_shares if ManyStatsBool: self.LorenzLong = get_lorenz_shares(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] EmpNow = EmpNow[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 = EmpNow 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 = calc_subpop_avg(MPCannual, aNrm, self.cutoffs, CohortWeight) self.MPCbyIncome = calc_subpop_avg(MPCannual, IncLvl, self.cutoffs, CohortWeight) # Calculate the wealth quintile distribution of "hand to mouth" consumers quintile_cuts = get_percentiles(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 = get_percentiles( 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
def runRoszypalSchlaffmanExperiment(CorrAct, CorrPcvd, DiscFac_center, DiscFac_spread): ''' 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. 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. Returns ------- AggWealthRatio: float Ratio of Aggregate wealth to income. Lorenz: numpy.array A list of two 1D array reprensenting 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 7 point approximation to a uniform distribution of DiscFac DiscFac_list = Uniform(bot=DiscFac_center - DiscFac_spread, top=DiscFac_center + DiscFac_spread).approx(N=7).X 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 type *believe* he will face a different level of persistence ThisType.PrstIncCorr = CorrPcvd ThisType.update_pLvlNextFunc( ) # Now he *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.update_pLvlNextFunc() # Simulate the agents for many periods ThisType.T_sim = 100 #ThisType.track_vars = ['cLvl','aLvl','pLvl','MPCnow'] ThisType.initialize_sim() ThisType.simulate() type_list.append(ThisType) # Get the most recent simulated values of X = cLvl, MPCnow, aLvl, pLvl for all types cLvl_all = np.concatenate( [ThisType.controls["cLvl"] for ThisType in type_list]) aLvl_all = np.concatenate( [ThisType.state_now["aLvl"] for ThisType in type_list]) MPC_all = np.concatenate([ThisType.MPCnow for ThisType in type_list]) pLvl_all = np.concatenate( [ThisType.state_now["pLvl"] 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 = get_lorenz_shares(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 = calc_subpop_avg(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
return AgeDstn ############################################################################### ### ACTUAL WORK BEGINS BELOW THIS LINE ####################################### ############################################################################### if __name__ == '__main__': # Set targets for K/Y and the Lorenz curve based on the data if do_liquid: lorenz_target = np.array([0.0, 0.004, 0.025, 0.117]) KY_target = 6.60 else: # This is hacky until I can find the liquid wealth data and import it lorenz_target = get_lorenz_shares( Params.SCF_wealth, weights=Params.SCF_weights, percentiles=Params.percentiles_to_match) lorenz_long_data = np.hstack( (np.array(0.0), get_lorenz_shares(Params.SCF_wealth, weights=Params.SCF_weights, percentiles=np.arange(0.01, 1.0, 0.01).tolist()), np.array(1.0))) #lorenz_target = np.array([-0.002, 0.01, 0.053,0.171]) KY_target = 10.26 # Set total number of simulated agents in the population if do_param_dist: if do_agg_shocks: Population = Params.pop_sim_agg_dist