def ETR_income(r, w, b, n, factor, e, etr_params, p): ''' Calculates effective personal income tax rate. Args: r (array_like): real interest rate w (array_like): real wage rate b (Numpy array): savings n (Numpy array): labor supply factor (scalar): scaling factor converting model units to dollars e (Numpy array): effective labor units etr_params (Numpy array): effective tax rate function parameters p (OG-Core Specifications object): model parameters Returns: tau (Numpy array): effective tax rate on total income ''' X = (w * e * n) * factor Y = (r * b) * factor tau = get_tax_rates(etr_params, X, Y, None, p.tax_func_type, 'etr', for_estimation=False) return tau
def MTR_income(r, w, b, n, factor, mtr_capital, e, etr_params, mtr_params, p): r''' Generates the marginal tax rate on labor income for households. Args: r (array_like): real interest rate w (array_like): real wage rate b (Numpy array): savings n (Numpy array): labor supply factor (scalar): scaling factor converting model units to dollars mtr_capital (bool): whether to compute the marginal tax rate on capital income or labor income e (Numpy array): effective labor units etr_params (Numpy array): effective tax rate function parameters p (OG-Core Specifications object): model parameters Returns: tau (Numpy array): marginal tax rate on income source ''' X = (w * e * n) * factor Y = (r * b) * factor if p.analytical_mtrs: tau = get_tax_rates(etr_params, X, Y, None, p.tax_func_type, 'mtr', p.analytical_mtrs, mtr_capital, for_estimation=False) else: tau = get_tax_rates(mtr_params, X, Y, None, p.tax_func_type, 'mtr', p.analytical_mtrs, mtr_capital, for_estimation=False) return tau
def test_get_tax_rates(tax_func_type, rate_type, params, analytical_mtrs, mtr_capital, for_estimation, expected): ''' Teset of txfunc.get_tax_rates() function. There are 6 cases to test: 1) DEP function, for estimation 2) DEP function, not for estimation 3) GS function, etr 4) GS function, mtr 5) DEP_totalinc function, for estimation 6) DEP_totalinc function, not for estimation ''' wgts = np.array([0.1, 0.25, 0.55, 0.1]) X = np.array([32.0, 44.0, 1.6, 0.4]) Y = np.array([32.0, 55.0, 0.9, 0.03]) print('Params = ', params) test_txrates = txfunc.get_tax_rates(params, X, Y, wgts, tax_func_type, rate_type, analytical_mtrs, mtr_capital, for_estimation) assert np.allclose(test_txrates, expected)
def get_tax_fn( year, start_year, tax_param_list, age=None, tax_func_type=["DEP"], rate_type="etr", over_labinc=True, other_inc_val=1000, max_inc_amt=1000000, data_list=None, labels=["1st Functions"], title=None, path=None, ): # Check that inputs are valid year, start_year = int(year), int(start_year) assert year >= start_year # if list of tax function types less than list of params, assume # all the same functional form if len(tax_func_type) < len(tax_param_list): tax_func_type = [tax_func_type[0]] * len(tax_param_list) for i, v in enumerate(tax_func_type): assert v in ["DEP", "DEP_totalinc", "GS", "linear"] assert rate_type in ["etr", "mtrx", "mtry"] assert len(tax_param_list) == len(labels) # Set age and year to look at if age is not None: assert isinstance(age, int) s = age - 21 else: s = 0 # if not age-specific, all ages have the same values t = year - start_year # create rate_key to correspond to keys in tax func dicts rate_key = "tfunc_" + rate_type + "_params_S" # Set income range to plot over (min income value hard coded to 5) inc_sup = np.exp(np.linspace(np.log(5), np.log(max_inc_amt), 100)) # Set income value for other income inc_fix = other_inc_val if over_labinc: key1 = "total_labinc" X = inc_sup Y = inc_fix else: key1 = "total_capinc" X = inc_fix Y = inc_sup # get tax rates for each point in the income support and plot for i, tax_params in enumerate(tax_param_list): rates = txfunc.get_tax_rates( tax_params[rate_key][s, t, :], X, Y, None, tax_func_type[i], rate_type, for_estimation=False, ) return lambda x, y: txfunc.get_tax_rates( tax_params[rate_key][s, t, :], x, y, None, tax_func_type[i], rate_type, for_estimation=False, )
def plot_2D_taxfunc(year, start_year, tax_param_list, age=None, tax_func_type=['DEP'], rate_type='etr', over_labinc=True, other_inc_val=1000, max_inc_amt=1000000, data_list=None, labels=['1st Functions'], title=None, path=None): ''' This function plots OG-Core tax functions in two dimensions. The tax rates are plotted over capital or labor income, as entered by the user. Args: year (int): year of policy tax functions represent start_year (int): first year tax functions estimated for in tax_param_list elements tax_param_list (list): list of arrays containing tax function parameters age (int): age for tax functions to plot, use None if tax function parameters were not age specific tax_func_type (list): list of strings in ["DEP", "DEP_totalinc", "GS", "linear"] and specifies functional form of tax functions in tax_param_list rate_type (str): string that is in ["etr", "mtrx", "mtry"] and determines the type of tax rate that is plotted over_labinc (bool): indicates that x-axis of the plot is over labor income, if False then plot is over capital income other_inc_val (scalar): dollar value at which to hold constant the amount of income that is not represented on the x-axis max_inc_amt (scalar): largest income amount to represent on the x-axis of the plot data_list (list): list of DataFrames with data to scatter plot with tax functions, needs to be of format output from ogcore.get_micro_data.get_data labels (list): list of labels for tax function parameters title (str): title for the plot path (str): path to which to save plot, if None then figure returned Returns: fig (Matplotlib plot object): plot of tax functions ''' # Check that inputs are valid assert isinstance(start_year, int) assert isinstance(year, int) assert (year >= start_year) # if list of tax function types less than list of params, assume # all the same functional form if len(tax_func_type) < len(tax_param_list): tax_func_type = [tax_func_type[0]] * len(tax_param_list) for i, v in enumerate(tax_func_type): assert (v in ['DEP', 'DEP_totalinc', 'GS', 'linear']) assert (rate_type in ['etr', 'mtrx', 'mtry']) assert (len(tax_param_list) == len(labels)) # Set age and year to look at if age is not None: assert isinstance(age, int) s = age - 21 else: s = 0 # if not age-specific, all ages have the same values t = year - start_year # create rate_key to correspond to keys in tax func dicts rate_key = 'tfunc_' + rate_type + '_params_S' # Set income range to plot over (min income value hard coded to 5) inc_sup = np.exp(np.linspace(np.log(5), np.log(max_inc_amt), 100)) # Set income value for other income inc_fix = other_inc_val if over_labinc: key1 = 'total_labinc' X = inc_sup Y = inc_fix else: key1 = 'total_capinc' X = inc_fix Y = inc_sup # get tax rates for each point in the income support and plot fig, ax = plt.subplots() for i, tax_params in enumerate(tax_param_list): rates = txfunc.get_tax_rates(tax_params[rate_key][s, t, :], X, Y, None, tax_func_type[i], rate_type, for_estimation=False) plt.plot(inc_sup, rates, label=labels[i]) # plot raw data (if passed) if data_list is not None: rate_type_dict = { 'etr': 'etr', 'mtrx': 'mtr_labinc', 'mtry': 'mtr_capinc' } # censor data to range of the plot for d, data in enumerate(data_list): data_to_plot = data[str(year)].copy() if age is not None: data_to_plot.drop( data_to_plot[data_to_plot['age'] != age].index, inplace=True) # other censoring data_to_plot.drop( data_to_plot[data_to_plot[key1] > max_inc_amt].index, inplace=True) # other censoring used in txfunc.py data_to_plot = txfunc.tax_data_sample(data_to_plot) # set number of bins to 100 or bins of $1000 dollars n_bins = min(100, np.floor_divide(max_inc_amt, 1000)) # need to compute weighted averages by group... wm = lambda x: np.average( x, weights=data_to_plot.loc[x.index, "weight"]) data_to_plot['inc_bin'] = pd.cut(data_to_plot[key1], n_bins) groups = pd.DataFrame( data_to_plot.groupby(["inc_bin" ]).agg(rate=(rate_type_dict[rate_type], wm), income=(key1, wm))) plt.scatter(groups['income'], groups['rate'], alpha=0.1) # add legend, labels, etc to plot plt.legend(loc='center right') if title: plt.title(title) if over_labinc: plt.xlabel(r'Labor income') else: plt.xlabel(r'Capital income') plt.ylabel(VAR_LABELS[rate_type]) if path is None: return fig else: plt.savefig(path)
def txfunc_graph(s, t, df, X, Y, txrates, rate_type, tax_func_type, get_tax_rates, params_to_plot, output_dir): ''' This function creates a 3D plot of the fitted tax function against the data. Args: s (int): age of individual, >= 21 t (int): year of analysis, >= 2016 df (Pandas DataFrame): 11 variables with N observations of tax rates X (Pandas DataSeries): labor income Y (Pandas DataSeries): capital income Y (Pandas DataSeries): tax rates from the data rate_type (str): type of tax rate: mtrx, mtry, etr tax_func_type (str): functional form of tax functions get_tax_rates (function): function to generate tax rates given parameters params_to_plot (Numpy Array): tax function parameters output_dir (str): output directory for saving plot files Returns: None ''' cmap1 = matplotlib.cm.get_cmap('summer') # Make comparison plot with full income domains fig = plt.figure() ax = fig.add_subplot(111, projection='3d') ax.scatter(X, Y, txrates, c='r', marker='o') ax.set_xlabel('Total Labor Income') ax.set_ylabel('Total Capital Income') if rate_type == 'etr': tx_label = 'ETR' elif rate_type == 'mtrx': tx_label = 'MTRx' elif rate_type == 'mtry': tx_label = 'MTRy' ax.set_zlabel(tx_label) plt.title(tx_label + ' vs. Predicted ' + tx_label + ': Age=' + str(s) + ', Year=' + str(t)) gridpts = 50 X_vec = np.exp(np.linspace(np.log(5), np.log(X.max()), gridpts)) Y_vec = np.exp(np.linspace(np.log(5), np.log(Y.max()), gridpts)) X_grid, Y_grid = np.meshgrid(X_vec, Y_vec) txrate_grid = txfunc.get_tax_rates(params_to_plot, X_grid, Y_grid, None, tax_func_type, rate_type, for_estimation=False) ax.plot_surface(X_grid, Y_grid, txrate_grid, cmap=cmap1, linewidth=0) filename = (tx_label + '_age_' + str(s) + '_Year_' + str(t) + '_vsPred.png') fullpath = os.path.join(output_dir, filename) fig.savefig(fullpath, bbox_inches='tight') plt.close() # Make comparison plot with truncated income domains df_trnc_gph = df[(df['total_labinc'] > 5) & (df['total_labinc'] < 800000) & (df['total_capinc'] > 5) & (df['total_capinc'] < 800000)] X_gph = df_trnc_gph['total_labinc'] Y_gph = df_trnc_gph['total_capinc'] if rate_type == 'etr': txrates_gph = df_trnc_gph['etr'] elif rate_type == 'mtrx': txrates_gph = df_trnc_gph['mtr_labinc'] elif rate_type == 'mtry': txrates_gph = df_trnc_gph['mtr_capinc'] fig = plt.figure() ax = fig.add_subplot(111, projection='3d') ax.scatter(X_gph, Y_gph, txrates_gph, c='r', marker='o') ax.set_xlabel('Total Labor Income') ax.set_ylabel('Total Capital Income') ax.set_zlabel(tx_label) plt.title('Truncated ' + tx_label + ', Lab. Inc., and Cap. ' + 'Inc., Age=' + str(s) + ', Year=' + str(t)) gridpts = 50 X_vec = np.exp(np.linspace(np.log(5), np.log(X_gph.max()), gridpts)) Y_vec = np.exp(np.linspace(np.log(5), np.log(Y_gph.max()), gridpts)) X_grid, Y_grid = np.meshgrid(X_vec, Y_vec) txrate_grid = txfunc.get_tax_rates(params_to_plot, X_grid, Y_grid, None, tax_func_type, rate_type, for_estimation=False) ax.plot_surface(X_grid, Y_grid, txrate_grid, cmap=cmap1, linewidth=0) filename = (tx_label + 'trunc_age_' + str(s) + '_Year_' + str(t) + '_vsPred.png') fullpath = os.path.join(output_dir, filename) fig.savefig(fullpath, bbox_inches='tight') plt.close()