def calculateLorenzDifference(sim_wealth, weights, percentiles, target_levels): ''' Calculates the sum of squared differences between the simulatedLorenz curve at the specified percentile levels and the target Lorenz levels. Parameters ---------- sim_wealth : numpy.array Array with simulated wealth values. weights : numpy.array List of weights for each row of sim_wealth. percentiles : [float] Points in the distribution of wealth to match. target_levels : np.array Actual U.S. Lorenz curve levels at the specified percentiles. Returns ------- distance : float Sum of squared distances between simulated and target Lorenz curves. ''' sim_lorenz = getLorenzShares(sim_wealth, weights=weights, percentiles=percentiles) distance = sum((100 * sim_lorenz - 100 * target_levels)**2) return distance
def makeLorenzFig(real_wealth, real_weights, sim_wealth, sim_weights): ''' Produces a Lorenz curve for the distribution of wealth, comparing simulated to actual data. A sub-function of makeCSTWresults(). Parameters ---------- real_wealth : np.array Data on household wealth. real_weights : np.array Weighting array of the same size as real_wealth. sim_wealth : np.array Simulated wealth holdings of many households. sim_weights :np.array Weighting array of the same size as sim_wealth. Returns ------- these_percents : np.array An array of percentiles of households, by wealth. real_lorenz : np.array Lorenz shares for real_wealth corresponding to these_percents. sim_lorenz : np.array Lorenz shares for sim_wealth corresponding to these_percents. ''' these_percents = np.linspace(0.0001, 0.9999, 201) real_lorenz = getLorenzShares(real_wealth, weights=real_weights, percentiles=these_percents) sim_lorenz = getLorenzShares(sim_wealth, weights=sim_weights, percentiles=these_percents) plt.plot(100 * these_percents, real_lorenz, '-k', linewidth=1.5) plt.plot(100 * these_percents, sim_lorenz, '--k', linewidth=1.5) plt.xlabel('Wealth percentile', fontsize=14) plt.ylabel('Cumulative wealth ownership', fontsize=14) plt.title('Simulated vs Actual Lorenz Curves', fontsize=16) plt.legend(('Actual', 'Simulated'), loc=2, fontsize=12) plt.ylim(-0.01, 1) plt.show() return (these_percents, real_lorenz, sim_lorenz)
def makeLorenzFig(real_wealth,real_weights,sim_wealth,sim_weights): ''' Produces a Lorenz curve for the distribution of wealth, comparing simulated to actual data. A sub-function of makeCSTWresults(). Parameters ---------- real_wealth : np.array Data on household wealth. real_weights : np.array Weighting array of the same size as real_wealth. sim_wealth : np.array Simulated wealth holdings of many households. sim_weights :np.array Weighting array of the same size as sim_wealth. Returns ------- these_percents : np.array An array of percentiles of households, by wealth. real_lorenz : np.array Lorenz shares for real_wealth corresponding to these_percents. sim_lorenz : np.array Lorenz shares for sim_wealth corresponding to these_percents. ''' these_percents = np.linspace(0.0001,0.9999,201) real_lorenz = getLorenzShares(real_wealth,weights=real_weights,percentiles=these_percents) sim_lorenz = getLorenzShares(sim_wealth,weights=sim_weights,percentiles=these_percents) plt.plot(100*these_percents,real_lorenz,'-k',linewidth=1.5) plt.plot(100*these_percents,sim_lorenz,'--k',linewidth=1.5) plt.xlabel('Wealth percentile',fontsize=14) plt.ylabel('Cumulative wealth ownership',fontsize=14) plt.title('Simulated vs Actual Lorenz Curves',fontsize=16) plt.legend(('Actual','Simulated'),loc=2,fontsize=12) plt.ylim(-0.01,1) plt.show() return (these_percents,real_lorenz,sim_lorenz)
def calculateLorenzDifference(sim_wealth,weights,percentiles,target_levels): ''' Calculates the sum of squared differences between the simulatedLorenz curve at the specified percentile levels and the target Lorenz levels. Parameters ---------- sim_wealth : numpy.array Array with simulated wealth values. weights : numpy.array List of weights for each row of sim_wealth. percentiles : [float] Points in the distribution of wealth to match. target_levels : np.array Actual U.S. Lorenz curve levels at the specified percentiles. Returns ------- distance : float Sum of squared distances between simulated and target Lorenz curves. ''' sim_lorenz = getLorenzShares(sim_wealth,weights=weights,percentiles=percentiles) distance = sum((100*sim_lorenz-100*target_levels)**2) 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
MrkvArray[t + 1, 0] = 1.0 w, v = np.linalg.eig(np.transpose(MrkvArray)) idx = (np.abs(w - 1.0)).argmin() x = v[:, idx].astype(float) AgeDstn = (x / np.sum(x)) return AgeDstn # Set targets for K/Y and the Lorenz curve based on the data if Params.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 = getLorenzShares(Params.SCF_wealth, weights=Params.SCF_weights, percentiles=Params.percentiles_to_match) lorenz_long_data = np.hstack( (np.array(0.0), getLorenzShares(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 # Make AgentTypes for estimation if Params.do_lifecycle: DropoutType = cstwMPCagent(**Params.init_dropout) DropoutType.AgeDstn = calcStationaryAgeDstn(DropoutType.LivPrb, True) HighschoolType = deepcopy(DropoutType)
j += 1 # Only run below this line if module is run rather than imported: if __name__ == "__main__": # ================================================================= # ====== Make the list of consumer types for estimation =========== #================================================================== # Set target Lorenz points and K/Y ratio (MOVE THIS TO SetupParams) if Params.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 = getLorenzShares( Params.SCF_wealth, weights=Params.SCF_weights, percentiles=Params.percentiles_to_match) #lorenz_target = np.array([-0.002, 0.01, 0.053,0.171]) KY_target = 10.26 # Make a vector of initial wealth-to-permanent income ratios a_init = drawDiscrete(N=Params.sim_pop_size, P=Params.a0_probs, X=Params.a0_values, seed=Params.a0_seed) # Make the list of types for this run, whether infinite or lifecycle if Params.do_lifecycle: # Make cohort scaling array cohort_scale = Params.TFP_growth**(-np.arange(Params.total_T + 1)) cohort_scale_array = np.tile(
this_type.update() j += 1 # Only run below this line if module is run rather than imported: if __name__ == "__main__": # ================================================================= # ====== Make the list of consumer types for estimation =========== #================================================================== # Set target Lorenz points and K/Y ratio (MOVE THIS TO SetupParams) if Params.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 = getLorenzShares(Params.SCF_wealth,weights=Params.SCF_weights,percentiles=Params.percentiles_to_match) #lorenz_target = np.array([-0.002, 0.01, 0.053,0.171]) KY_target = 10.26 # Make a vector of initial wealth-to-permanent income ratios a_init = drawDiscrete(N=Params.sim_pop_size,P=Params.a0_probs,X=Params.a0_values,seed=Params.a0_seed) # Make the list of types for this run, whether infinite or lifecycle if Params.do_lifecycle: # Make cohort scaling array cohort_scale = Params.TFP_growth**(-np.arange(Params.total_T+1)) cohort_scale_array = np.tile(np.reshape(cohort_scale,(Params.total_T+1,1)),(1,Params.sim_pop_size)) # Make base consumer types for each education level DropoutType = cstwMPCagent(**Params.init_dropout) DropoutType.a_init = a_init
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
MrkvArray[t,t+1] = LivPrb[t] MrkvArray[t+1,0] = 1.0 w, v = np.linalg.eig(np.transpose(MrkvArray)) idx = (np.abs(w-1.0)).argmin() x = v[:,idx].astype(float) AgeDstn = (x/np.sum(x)) return AgeDstn # Set targets for K/Y and the Lorenz curve based on the data if Params.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 = getLorenzShares(Params.SCF_wealth,weights=Params.SCF_weights,percentiles=Params.percentiles_to_match) lorenz_long_data = np.hstack((np.array(0.0),getLorenzShares(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 # Make AgentTypes for estimation if Params.do_lifecycle: DropoutType = cstwMPCagent(**Params.init_dropout) DropoutType.AgeDstn = calcStationaryAgeDstn(DropoutType.LivPrb,True) HighschoolType = deepcopy(DropoutType) HighschoolType(**Params.adj_highschool) HighschoolType.AgeDstn = calcStationaryAgeDstn(HighschoolType.LivPrb,True) CollegeType = deepcopy(DropoutType) CollegeType(**Params.adj_college) CollegeType.AgeDstn = calcStationaryAgeDstn(CollegeType.LivPrb,True) DropoutType.update()