def test_gini(): # Unweighted mdf.gini(df, "x") # Weighted mdf.gini(df, "x", "w") # Unweighted, grouped mdf.gini(dfg, "x", groupby="g") # Weighted, grouped mdf.gini(dfg, "x", "w", groupby="g")
def tax(flat_tax, total_type="person"): """Calculate all metrics given a flat tax. Args: flat_tax: Percentage tax rate (0-100). total_type: Whether to use total population and current tax liability from SPM units or persons. Either "person" or "spmu". Defaults to "person". """ flat_tax /= 100 spmu["new_tax"] = spmu.spmu_agi * flat_tax new_revenue = mdf.weighted_sum(spmu, "new_tax", "spm_weight") change_revenue = new_revenue - totals.loc[total_type].fica_fedtax_ac ubi = change_revenue / totals.loc[total_type].person spmu["new_spm_resources"] = (spmu.spm_resources_before_tax + ubi * spmu.spmu_total_people - spmu.new_tax) # Merge back to each person. target_persons = person.merge(spmu[SPM_COLS + ["new_spm_resources"]], on=SPM_COLS) target_persons["new_spm_resources_pp"] = ( target_persons.new_spm_resources / target_persons.spmu_total_people) # Calculate poverty rate target_persons["new_poor"] = (target_persons.new_spm_resources < target_persons.spm_povthreshold) poverty_rate = mdf.weighted_mean(target_persons, "new_poor", "marsupwt") change_poverty_rate = chg(poverty_rate, initial_poverty_rate) # Calculate poverty gap poverty_gaps = np.maximum(spmu.spm_povthreshold - spmu.new_spm_resources, 0) poverty_gap = (poverty_gaps * spmu.spm_weight).sum() change_poverty_gap = chg(poverty_gap, initial_poverty_gap) # Calculate Gini gini = mdf.gini(target_persons, "new_spm_resources_pp", w="marsupwt") change_gini = chg(gini, initial_gini) # Percent winners target_persons["better_off"] = (target_persons.new_spm_resources > target_persons.spm_resources) percent_better_off = mdf.weighted_mean(target_persons, "better_off", "marsupwt") return pd.Series({ "poverty_rate": poverty_rate, "poverty_gap": poverty_gap, "gini": gini, "percent_better_off": percent_better_off, "change_poverty_rate": change_poverty_rate, "change_poverty_gap": change_poverty_gap, "change_gini": change_gini, "change_revenue": change_revenue, "ubi": ubi, })
def test_gini(): # Test nothing breaks. ms.gini() # Unweighted. mdf.gini(df, "x") # Weighted mdf.gini(df, "x", "w") # Unweighted, grouped mdf.gini(dfg, "x", groupby="g") # Weighted, grouped mdf.gini(dfg, "x", "w", groupby="g") # Test old and new match. assert ms.gini() == mdf.gini(df, "x", "w")
def ubi(funding_billions=0, percent=0): """ Calculate the poverty rate among the total US population by: -passing a total level of funding for a UBI proposal (billions USD), -passing a percent of the benefit recieved by a child and the benefit recieved by an adult AND taking into account that funding will be raise by a flat tax leveled on each households taxable income """ percent = percent / 100 funding = funding_billions * 1e9 target_persons = person.copy(deep=True) # i think this is % funding, not % benefit adult_ubi = ((1 - percent) * funding) / adult_pop child_ubi = (percent * funding) / child_pop tax_rate = funding / total_taxable_income target_persons['hh_new_tax'] = target_persons.hh_tax_income * tax_rate target_persons['hh_ubi'] = (target_persons.hh_adults * adult_ubi + target_persons.hh_children * child_ubi) target_persons['new_spm_resources'] = (target_persons.spm_resources + target_persons.hh_ubi - target_persons.hh_new_tax) target_persons['new_spm_resources_pp'] = (target_persons.new_spm_resources / (target_persons.hh_total_people)) # Calculate poverty rate target_persons['poor'] = (target_persons.new_spm_resources < target_persons.spm_povthreshold) total_poor = (target_persons.poor * target_persons.weight).sum() poverty_rate = (total_poor / pop * 100) # Calculate poverty gap target_persons['poverty_gap'] = target_persons.spm_povthreshold - target_persons.new_spm_resources poverty_gap = (((target_persons.poor * target_persons.poverty_gap * target_persons.weight).sum())) # Calculate Gini gini = mdf.gini(target_persons, 'new_spm_resources_pp', w='weight') # Percent winners target_persons['better_off'] = (target_persons.new_spm_resources > target_persons.spm_resources) total_better_off = (target_persons.better_off * target_persons.weight).sum() percent_better_off = total_better_off / pop return pd.Series([poverty_rate, gini, poverty_gap, percent_better_off, adult_ubi, child_ubi])
def gin(data, group): return pd.DataFrame( data.groupby(group).apply( lambda x: mdf.gini(x, "spmftotval", "asecwt")))
"spm_weight") spmu_totals.index = person_totals.index totals = pd.concat([person_totals, spmu_totals], axis=1).T totals.index = ["person", "spmu"] # Calculate status quo person["poor"] = person.spm_resources < person.spm_povthreshold initial_poverty_rate = mdf.weighted_mean(person, "poor", "marsupwt") spmu["initial_poverty_gap"] = np.maximum( spmu.spm_povthreshold - spmu.spm_resources, 0) initial_poverty_gap = (spmu.initial_poverty_gap * spmu.spm_weight).sum() person["spm_resources_pp"] = person.spm_resources / person.spmu_total_people initial_gini = mdf.gini(person, "spm_resources_pp", w="marsupwt") def chg(new, base): return (100 * (new - base) / base).round(1) def tax(flat_tax, total_type="person"): """Calculate all metrics given a flat tax. Args: flat_tax: Percentage tax rate (0-100). total_type: Whether to use total population and current tax liability from SPM units or persons. Either "person" or "spmu". Defaults to "person". """
# append US statistics as additional 'state' pop_df = pop_states.append(pop_us) # melt df from wide to long format pop_df = pop_df.melt(ignore_index=False, var_name="demog") pop_df.insert(loc=1, column="metric", value="pop") # concat poverty and population dfs demog_stats = pd.concat([pov_df, pop_df]) # write to csv file demog_stats.to_csv("demog_stats.csv.gz", compression="gzip") # Caluclate original gini person["spm_resources_per_person"] = person.spmtotres / person.numper # Caluclate original gini for US gini_us = pd.Series( mdf.gini(df=person, col="spm_resources_per_person", w="asecwt")) # add name to series gini_us.index = ["US"] # calculate gini for each group by state gini_states = mdf.gini(df=person, col="spm_resources_per_person", w="asecwt", groupby="state") # append US statistics as additional 'state' gini_ser = gini_states.append(gini_us) gini_ser.name = "gini" # Calculate the original poverty gap spmu["poverty_gap"] = np.where(
def evaluate_reform(reform): baseline = model() reformed = model(reform) period = "2020-10" family_weights = baseline.calculate("benunit_weight", period) adult_weights = baseline.calculate("adult_weight", period) household_weights = baseline.calculate("household_weight", period) net_gain = reformed.calculate( "benunit_net_income", period ) - baseline.calculate("benunit_net_income", period) gross_ubi_cost = ( (reformed.calculate("benunit_basic_income", period)) * family_weights ).sum() * 52 total_net_cost = (net_gain * family_weights).sum() * 52 print("Total cost summary:") print(f" Net cost of reform: {gbp(total_net_cost)}") print(f" Gross cost of UBI: {gbp(gross_ubi_cost)}") poverty_ahc_reduction = percent_reduction( poverty_rate(baseline, "people_in_household"), poverty_rate(reformed, "people_in_household"), ) adult_poverty_ahc_reduction = percent_reduction( poverty_rate(baseline, "adults_in_household"), poverty_rate(reformed, "adults_in_household"), ) child_poverty_ahc_reduction = percent_reduction( poverty_rate(baseline, "children_in_household"), poverty_rate(reformed, "children_in_household"), ) senior_poverty_ahc_reduction = percent_reduction( poverty_rate(baseline, "seniors_in_household"), poverty_rate(reformed, "seniors_in_household"), ) print("Poverty statistics:") print(f" AHC poverty change: {num(poverty_ahc_reduction * 100)}%") print( f" AHC adult poverty change: {num(adult_poverty_ahc_reduction * 100)}%" ) print( f" AHC child poverty change: {num(child_poverty_ahc_reduction * 100)}%" ) print( f" AHC senior poverty change: {num(senior_poverty_ahc_reduction * 100)}%" ) diff_vars_adult = ["state_pension", "JSA_contrib"] diff_vars_family = [ "child_benefit", "income_support", "working_tax_credit", "child_tax_credit", "benunit_income_tax", "benunit_NI", "pension_credit", "JSA_income", "universal_credit", ] print("MTR:") baseline_MTR = calc_mtr(entity="household") reform_MTR = calc_mtr(reform, entity="household") average_baseline_MTR = np.average(baseline_MTR, weights=household_weights) average_reform_MTR = np.average(reform_MTR, weights=household_weights) on_benefits = baseline.calculate( "household_receives_means_tested_benefits", period ).astype(bool) average_baseline_ben_MTR = np.average( baseline_MTR[on_benefits], weights=household_weights[on_benefits] ) average_reform_ben_MTR = np.average( reform_MTR[on_benefits], weights=household_weights[on_benefits] ) print(f" Average baseline MTR: {average_baseline_MTR}") print(f" Average reform MTR: {average_reform_MTR}") print( f" Average baseline MTR for households on benefits: {average_baseline_ben_MTR}" ) print( f" Average reform MTR for households on benefits: {average_reform_ben_MTR}" ) print("Inequality:") household_net_ahc = pd.DataFrame() household_net_ahc["w"] = baseline.calculate( "household_weight", period ) * baseline.calculate("people_in_household", period) household_net_ahc["baseline"] = baseline.calculate( "equiv_household_net_income_ahc", period ) household_net_ahc["reform"] = reformed.calculate( "equiv_household_net_income_ahc", period ) baseline_gini = mdf.gini(household_net_ahc, "baseline", w="w") reform_gini = mdf.gini(household_net_ahc, "reform", w="w") gini_reduction = percent_reduction(baseline_gini, reform_gini) print(f" Gini coefficient reduction: {num(gini_reduction * 100)}%") print("Rise in amounts per year across all:") print(" individuals:") for var in diff_vars_adult: diff = ( (reformed.calculate(var, period) - baseline.calculate(var, period)) * adult_weights ).sum() * 52 print(f" {var}: {gbp(diff)}") print(" families:") for var in diff_vars_family: diff = ( (reformed.calculate(var, period) - baseline.calculate(var, period)) * family_weights ).sum() * 52 print(f" {var}: {gbp(diff)}")
def ubi(statefip, level, agi_tax, benefits, taxes, exclude): if level == "federal": # combine lists and initialize taxes_benefits = taxes + benefits spmu["new_resources"] = spmu.spmtotres revenue = 0 # Calculate the new revenue and spmu resources from tax and benefit change for tax_benefit in taxes_benefits: spmu.new_resources -= spmu[tax_benefit] revenue += mdf.weighted_sum(spmu, tax_benefit, "spmwt") if ("fedtaxac" in taxes_benefits) & ("ctc" in taxes_benefits): spmu.new_resources += spmu.ctc revenue -= mdf.weighted_sum(spmu, "ctc", "spmwt") if ("fedtaxac" in taxes_benefits) & ("eitcred" in taxes_benefits): spmu.new_resources += spmu.eitcred revenue -= mdf.weighted_sum(spmu, "eitcred", "spmwt") # Calculate the new taxes from flat tax on AGI tax_rate = agi_tax / 100 spmu["new_taxes"] = np.maximum(spmu.adjginc, 0) * tax_rate spmu.new_resources -= spmu.new_taxes revenue += mdf.weighted_sum(spmu, "new_taxes", "spmwt") # Calculate the total UBI a spmu recieves based on exclusions spmu["numper_ubi"] = spmu.numper if "children" in exclude: spmu["numper_ubi"] -= spmu.child if "non_citizens" in exclude: spmu["numper_ubi"] -= spmu.non_citizen if ("children" in exclude) and ("non_citizens" in exclude): spmu["numper_ubi"] += spmu.non_citizen_child if "adults" in exclude: spmu["numper_ubi"] -= spmu.adult if ("adults" in exclude) and ("non_citizens" in exclude): spmu["numper_ubi"] += spmu.non_citizen_adult # Assign UBI ubi_population = (spmu.numper_ubi * spmu.spmwt).sum() ubi = revenue / ubi_population spmu["total_ubi"] = ubi * spmu.numper_ubi # Calculate change in resources spmu.new_resources += spmu.total_ubi spmu["new_resources_per_person"] = spmu.new_resources / spmu.numper # Sort by state if statefip == "US": target_spmu = spmu.copy(deep=True) else: target_spmu = spmu[spmu.statefip == statefip].copy(deep=True) if level == "state": # Sort by state if statefip == "US": target_spmu = spmu.copy(deep=True) else: target_spmu = spmu[spmu.statefip == statefip].copy(deep=True) # Initialize target_spmu["new_resources"] = target_spmu.spmtotres revenue = 0 # Change income tax repeal to state level if "fedtaxac" in taxes: target_spmu.new_resources -= target_spmu.stataxac revenue += mdf.weighted_sum(target_spmu, "stataxac", "spmwt") # Calculate change in tax revenue tax_rate = agi_tax / 100 target_spmu["new_taxes"] = target_spmu.adjginc * tax_rate target_spmu.new_resources -= target_spmu.new_taxes revenue += mdf.weighted_sum(target_spmu, "new_taxes", "spmwt") # Calculate the total UBI a spmu recieves based on exclusions target_spmu["numper_ubi"] = target_spmu.numper if "children" in exclude: target_spmu["numper_ubi"] -= target_spmu.child if "non_citizens" in exclude: target_spmu["numper_ubi"] -= target_spmu.non_citizen if ("children" in exclude) and ("non_citizens" in exclude): target_spmu["numper_ubi"] += target_spmu.non_citizen_child if "adults" in exclude: target_spmu["numper_ubi"] -= target_spmu.adult if ("adults" in exclude) and ("non_citizens" in exclude): target_spmu["numper_ubi"] += target_spmu.non_citizen_adult # Assign UBI ubi_population = (target_spmu.numper_ubi * target_spmu.spmwt).sum() ubi = revenue / ubi_population target_spmu["total_ubi"] = ubi * target_spmu.numper_ubi # Calculate change in resources target_spmu.new_resources += target_spmu.total_ubi target_spmu["new_resources_per_person"] = ( target_spmu.new_resources / target_spmu.numper ) # Merge and create target_persons sub_spmu = target_spmu[ ["spmfamunit", "year", "new_resources", "new_resources_per_person"] ] target_persons = person.merge(sub_spmu, on=["spmfamunit", "year"]) # Calculate populations population = target_persons.asecwt.sum() child_population = (target_persons.child * target_persons.asecwt).sum() non_citizen_population = ( target_persons.non_citizen * target_persons.asecwt ).sum() non_citizen_child_population = ( target_persons.non_citizen_child * target_persons.asecwt ).sum() # Calculate total change in resources original_total_resources = ( target_spmu.spmtotres * target_spmu.spmwt ).sum() new_total_resources = (target_spmu.new_resources * target_spmu.spmwt).sum() change_total_resources = new_total_resources - original_total_resources change_pp = change_total_resources / population # Determine people originally in poverty target_persons["original_poor"] = ( target_persons.spmtotres < target_persons.spmthresh ) # Calculate original poverty rate original_total_poor = ( target_persons.original_poor * target_persons.asecwt ).sum() original_poverty_rate = (original_total_poor / population) * 100 # Calculate the original poverty gap target_spmu["poverty_gap"] = np.where( target_spmu.spmtotres < target_spmu.spmthresh, target_spmu.spmthresh - target_spmu.spmtotres, 0, ) original_poverty_gap = mdf.weighted_sum( target_spmu, "poverty_gap", "spmwt" ) # Calculate the orginal demographic poverty rates def pov_rate(column): return ( mdf.weighted_mean( target_persons[target_persons[column]], "original_poor", "asecwt", ) * 100 ) original_child_poverty_rate = pov_rate("child") original_adult_poverty_rate = pov_rate("adult") original_pwd_poverty_rate = pov_rate("pwd") original_white_poverty_rate = pov_rate("white_non_hispanic") original_black_poverty_rate = pov_rate("black") original_hispanic_poverty_rate = pov_rate("hispanic") # Caluclate original gini target_persons["spm_resources_per_person"] = ( target_persons.spmtotres / target_persons.numper ) original_gini = mdf.gini( target_persons, "spm_resources_per_person", "asecwt" ) # Calculate poverty gap target_spmu["new_poverty_gap"] = np.where( target_spmu.new_resources < target_spmu.spmthresh, target_spmu.spmthresh - target_spmu.new_resources, 0, ) poverty_gap = mdf.weighted_sum(target_spmu, "new_poverty_gap", "spmwt") poverty_gap_change = ( (poverty_gap - original_poverty_gap) / original_poverty_gap * 100 ).round(1) # Calculate the change in poverty rate target_persons["poor"] = ( target_persons.new_resources < target_persons.spmthresh ) total_poor = (target_persons.poor * target_persons.asecwt).sum() poverty_rate = (total_poor / population) * 100 poverty_rate_change = ( (poverty_rate - original_poverty_rate) / original_poverty_rate * 100 ).round(1) # Calculate change in Gini gini = mdf.gini(target_persons, "new_resources_per_person", "asecwt") gini_change = ((gini - original_gini) / original_gini * 100).round(1) # Calculate percent winners target_persons["winner"] = ( target_persons.new_resources > target_persons.spmtotres ) total_winners = (target_persons.winner * target_persons.asecwt).sum() percent_winners = (total_winners / population * 100).round(1) # Calculate the new poverty rate for each demographic def pv_rate(column): return ( mdf.weighted_mean( target_persons[target_persons[column]], "poor", "asecwt" ) * 100 ) child_poverty_rate = pv_rate("child") adult_poverty_rate = pv_rate("adult") pwd_poverty_rate = pv_rate("pwd") white_poverty_rate = pv_rate("white_non_hispanic") black_poverty_rate = pv_rate("black") hispanic_poverty_rate = pv_rate("hispanic") # Calculate the percent change in poverty rate for each demographic child_poverty_rate_change = ( (child_poverty_rate - original_child_poverty_rate) / original_child_poverty_rate * 100 ).round(1) adult_poverty_rate_change = ( (adult_poverty_rate - original_adult_poverty_rate) / original_adult_poverty_rate * 100 ).round(1) pwd_poverty_rate_change = ( (pwd_poverty_rate - original_pwd_poverty_rate) / original_pwd_poverty_rate * 100 ).round(1) white_poverty_rate_change = ( (white_poverty_rate - original_white_poverty_rate) / original_white_poverty_rate * 100 ).round(1) black_poverty_rate_change = ( (black_poverty_rate - original_black_poverty_rate) / original_black_poverty_rate * 100 ).round(1) hispanic_poverty_rate_change = ( (hispanic_poverty_rate - original_hispanic_poverty_rate) / original_hispanic_poverty_rate * 100 ).round(1) # Round all numbers for display in hover original_poverty_rate_string = str(round(original_poverty_rate, 1)) poverty_rate_string = str(round(poverty_rate, 1)) original_child_poverty_rate_string = str( round(original_child_poverty_rate, 1) ) child_poverty_rate_string = str(round(child_poverty_rate, 1)) original_adult_poverty_rate_string = str( round(original_adult_poverty_rate, 1) ) adult_poverty_rate_string = str(round(adult_poverty_rate, 1)) original_pwd_poverty_rate_string = str(round(original_pwd_poverty_rate, 1)) pwd_poverty_rate_string = str(round(pwd_poverty_rate, 1)) original_white_poverty_rate_string = str( round(original_white_poverty_rate, 1) ) white_poverty_rate_string = str(round(white_poverty_rate, 1)) original_black_poverty_rate_string = str( round(original_black_poverty_rate, 1) ) black_poverty_rate_string = str(round(black_poverty_rate, 1)) original_hispanic_poverty_rate_string = str( round(original_hispanic_poverty_rate, 1) ) hispanic_poverty_rate_string = str(round(hispanic_poverty_rate, 1)) original_poverty_gap_billions = original_poverty_gap / 1e9 original_poverty_gap_billions = int(original_poverty_gap_billions) original_poverty_gap_billions = "{:,}".format( original_poverty_gap_billions ) poverty_gap_billions = poverty_gap / 1e9 poverty_gap_billions = int(poverty_gap_billions) poverty_gap_billions = "{:,}".format(poverty_gap_billions) original_gini_string = str(round(original_gini, 3)) gini_string = str(round(gini, 3)) # Convert UBI and winners to string for title of chart ubi_int = int(ubi) ubi_int = "{:,}".format(ubi_int) ubi_string = str(ubi_int) winners_string = str(percent_winners) change_pp = int(change_pp) change_pp = "{:,}".format(change_pp) resources_string = str(change_pp) ubi_line = "UBI amount: $" + ubi_string winners_line = "Percent better off: " + winners_string + "%" resources_line = ( "Average change in resources per person: $" + resources_string ) # Create x-axis labels for each chart x = ["Poverty Rate", "Poverty Gap", "Inequality (Gini)"] x2 = [ "Child", "Adult", "People<br>with<br>disabilities", "White", "Black", "Hispanic", ] fig = go.Figure( [ go.Bar( x=x, y=[poverty_rate_change, poverty_gap_change, gini_change], text=[poverty_rate_change, poverty_gap_change, gini_change], hovertemplate=[ "Original poverty rate: " + original_poverty_rate_string + "%<br><extra></extra>" "New poverty rate: " + poverty_rate_string + "%", "Original poverty gap: $" + original_poverty_gap_billions + "B<br><extra></extra>" "New poverty gap: $" + poverty_gap_billions + "B", "Original gini: <extra></extra>" + original_gini_string + "<br>New gini: " + gini_string, ], marker_color=BLUE, ) ] ) # Edit text and display the UBI amount and percent winners in title fig.update_layout( uniformtext_minsize=10, uniformtext_mode="hide", plot_bgcolor="white" ) fig.update_traces(texttemplate="%{text}%", textposition="auto") fig.update_layout(title_text="Economic overview", title_x=0.5) fig.update_xaxes( tickangle=0, title_text="", tickfont={"size": 14}, title_standoff=25 ) fig.update_yaxes( # title_text = "Percent change", ticksuffix="%", tickprefix="", tickfont={"size": 14}, title_standoff=25, ) fig.update_layout( hoverlabel=dict(bgcolor="white", font_size=14, font_family="Roboto") ) fig.update_xaxes(title_font=dict(size=14, family="Roboto", color="black")) fig.update_yaxes(title_font=dict(size=14, family="Roboto", color="black")) fig2 = go.Figure( [ go.Bar( x=x2, y=[ child_poverty_rate_change, adult_poverty_rate_change, pwd_poverty_rate_change, white_poverty_rate_change, black_poverty_rate_change, hispanic_poverty_rate_change, ], text=[ child_poverty_rate_change, adult_poverty_rate_change, pwd_poverty_rate_change, white_poverty_rate_change, black_poverty_rate_change, hispanic_poverty_rate_change, ], hovertemplate=[ "Original child poverty rate: " + original_child_poverty_rate_string + "%<br><extra></extra>" "New child poverty rate: " + child_poverty_rate_string + "%", "Original adult poverty rate: " + original_adult_poverty_rate_string + "%<br><extra></extra>" "New adult poverty rate: " + adult_poverty_rate_string + "%", "Original pwd poverty rate: " + original_pwd_poverty_rate_string + "%<br><extra></extra>" "New pwd poverty rate: " + pwd_poverty_rate_string + "%", "Original White poverty rate: " + original_white_poverty_rate_string + "%<br><extra></extra>" "New White poverty rate: " + white_poverty_rate_string + "%", "Original Black poverty rate: " + original_black_poverty_rate_string + "%<br><extra></extra>" "New Black poverty rate: " + black_poverty_rate_string + "%", "Original Hispanic poverty rate: " + original_hispanic_poverty_rate_string + "%<br><extra></extra>" "New Hispanic poverty rate: " + hispanic_poverty_rate_string + "%", ], marker_color=BLUE, ) ] ) fig2.update_layout( uniformtext_minsize=10, uniformtext_mode="hide", plot_bgcolor="white" ) fig2.update_traces(texttemplate="%{text}%", textposition="auto") fig2.update_layout(title_text="Poverty rate breakdown", title_x=0.5) fig2.update_xaxes( tickangle=0, title_text="", tickfont={"size": 14}, title_standoff=25 ) fig2.update_yaxes( # title_text = "Percent change", ticksuffix="%", tickprefix="", tickfont={"size": 14}, title_standoff=25, ) fig2.update_layout( hoverlabel=dict(bgcolor="white", font_size=14, font_family="Roboto") ) fig2.update_xaxes(title_font=dict(size=14, family="Roboto", color="black")) fig2.update_yaxes(title_font=dict(size=14, family="Roboto", color="black")) return ubi_line, winners_line, resources_line, fig, fig2
def loss_metrics(x: list, baseline_df, reform_base_df, budget) -> pd.Series: """Calculate each potential loss metric. :param x: List of optimization elements: [senior, child, dis_1, dis_2, dis_3, region1, region2, ..., region12] :type x: list :return: Series with five elements: loser_share: Share of the population who come out behind. losses: Total losses among losers in pounds. mean_pct_loss: Average percent loss across the population (including zeroes for people who don't experience losses). mean_pct_loss_pwd2: Average percent loss across the population, with double weight given to people with disabilities. poverty_gap_bhc: Poverty gap before housing costs. poverty_gap_ahc: Poverty gap after housing costs. gini: Gini index of per-person household net income in the reform scenario, weighted by person weight at the household level. :rtype: pd.Series """ senior, child, dis_1, dis_2, dis_3, regions = extract(x) reform_df = set_ubi(reform_base_df, budget, senior, child, dis_1, dis_2, dis_3, regions) # Calculate loss-related loss metrics. change = reform_df.household_net_income - baseline_df.household_net_income loss = np.maximum(-change, 0) weight = baseline_df.household_weight * baseline_df.people_in_household # Calculate loser share. total_pop = np.sum(weight) losers = np.sum(weight * (loss > 0)) loser_share = losers / total_pop # Calculate total losses in pounds. losses = np.sum(weight * loss) # Calculate average percent loss (including zero for non-losers). pct_loss = loss / baseline_df.household_net_income valid_pct_loss = np.isfinite(pct_loss) total_pct_loss = np.sum(weight[valid_pct_loss] * pct_loss[valid_pct_loss]) mean_pct_loss = total_pct_loss / total_pop # Calculate average percent loss with double weight for PWD. pwd2_weight = baseline_df.household_weight * ( baseline_df.is_disabled + baseline_df.people_in_household) total_pct_loss_pwd2 = np.sum(pwd2_weight[valid_pct_loss] * pct_loss[valid_pct_loss]) total_pop_pwd2 = pwd2_weight.sum() # Denominator. mean_pct_loss_pwd2 = total_pct_loss_pwd2 / total_pop_pwd2 # Gini of income per person. reform_hh_net_income_pp = (reform_df.household_net_income / baseline_df.people_in_household) # mdf.gini requires a dataframe. reform_df = pd.DataFrame({ "reform_hh_net_income_pp": reform_hh_net_income_pp, "weight": weight }) gini = mdf.gini(reform_df, "reform_hh_net_income_pp", "weight") # Return Series of all metrics. return pd.Series({ "loser_share": loser_share, "losses": losses, "mean_pct_loss": mean_pct_loss, "mean_pct_loss_pwd2": mean_pct_loss_pwd2, "gini": gini, })
# `gini` example import microdf as mdf x = [-10, -1, 0, 5, 100] w = [1, 2, 3, 4, 5] ## Simple behavior mdf.gini(x) ## Dealing with negatives This will be equivalent to `mdf.gini([0, 0, 0, 5, 100])`. mdf.gini(x, negatives='zero') mdf.gini([0, 0, 0, 5, 100]) This will be equivalent to `mdf.gini([0, 9, 10, 15, 110])`. mdf.gini(x, negatives='shift') mdf.gini([0, 9, 10, 15, 110]) ## Dealing with weights mdf.gini(x, w) mdf.gini([-10, -1, -1,
def ubi(state_dropdown, level, agi_tax, benefits, taxes, include): """this does everything from microsimulation to figure creation. Dash does something automatically where it takes the input arguments in the order given in the @app.callback decorator Args: state_dropdown: takes input from callback input, component_id="state-dropdown" level: component_id="level" agi_tax: component_id="agi-slider" benefits: component_id="benefits-checklist" taxes: component_id="taxes-checklist" include: component_id="include-checklist" Returns: ubi_line: outputs to "ubi-output" in @app.callback revenue_line: outputs to "revenue-output" in @app.callback ubi_population_line: outputs to "revenue-output" in @app.callback winners_line: outputs to "winners-output" in @app.callback resources_line: outputs to "resources-output" in @app.callback fig: outputs to "econ-graph" in @app.callback fig2: outputs to "breakdown-graph" in @app.callback """ # -------------------- calculations based on reform level -------------------- # # if the "Reform level" selected by the user is federal if level == "federal": # combine taxes and benefits checklists into one list to be used to # subset spmu dataframe taxes_benefits = taxes + benefits # initialize new resources column with old resources as baseline spmu["new_resources"] = spmu.spmtotres # initialize revenue at zero revenue = 0 # Calculate the new revenue and spmu resources from tax and benefit change for tax_benefit in taxes_benefits: # subtract taxes and benefits that have been changed from spm unit's resources spmu.new_resources -= spmu[tax_benefit] # add that same value to revenue revenue += mdf.weighted_sum(spmu, tax_benefit, "spmwt") # if "Income taxes" = ? and "child_tax_credit" = ? # in taxes/benefits checklist if ("fedtaxac" in taxes_benefits) & ("ctc" in taxes_benefits): spmu.new_resources += spmu.ctc revenue -= mdf.weighted_sum(spmu, "ctc", "spmwt") if ("fedtaxac" in taxes_benefits) & ("eitcred" in taxes_benefits): spmu.new_resources += spmu.eitcred revenue -= mdf.weighted_sum(spmu, "eitcred", "spmwt") # Calculate the new taxes from flat tax on AGI tax_rate = agi_tax / 100 spmu["new_taxes"] = np.maximum(spmu.adjginc, 0) * tax_rate # subtract new taxes from new resources spmu.new_resources -= spmu.new_taxes # add new revenue when new taxes are applied on spmus, multiplied by weights revenue += mdf.weighted_sum(spmu, "new_taxes", "spmwt") # Calculate the total UBI a spmu recieves based on exclusions spmu["numper_ubi"] = spmu.numper # TODO make into linear equation on one line using array of some kind if "children" not in include: # subtract the number of children from the number of # people in spm unit receiving ubi benefit spmu["numper_ubi"] -= spmu.child if "non_citizens" not in include: spmu["numper_ubi"] -= spmu.non_citizen if ("children" not in include) and ("non_citizens" not in include): spmu["numper_ubi"] += spmu.non_citizen_child if "adults" not in include: spmu["numper_ubi"] -= spmu.adult if ("adults" not in include) and ("non_citizens" not in include): spmu["numper_ubi"] += spmu.non_citizen_adult # Assign UBI ubi_population = (spmu.numper_ubi * spmu.spmwt).sum() ubi_annual = revenue / ubi_population spmu["total_ubi"] = ubi_annual * spmu.numper_ubi # Calculate change in resources spmu.new_resources += spmu.total_ubi spmu["new_resources_per_person"] = spmu.new_resources / spmu.numper # Sort by state # NOTE: the "target" here refers to the population being # measured for gini/poverty rate/etc. # I.e. the total population of the state/country and # INCLUDING those excluding form recieving ubi payments # state here refers to the selection from the drop down, not the reform level if state_dropdown == "US": target_spmu = spmu else: target_spmu = spmu[spmu.state == state_dropdown] # if the "Reform level" dropdown selected by the user is State if level == "state": # Sort by state if state_dropdown == "US": target_spmu = spmu else: target_spmu = spmu[spmu.state == state_dropdown] # Initialize target_spmu["new_resources"] = target_spmu.spmtotres revenue = 0 # Change income tax repeal to state level if "fedtaxac" in taxes: target_spmu.new_resources -= target_spmu.stataxac revenue += mdf.weighted_sum(target_spmu, "stataxac", "spmwt") # Calculate change in tax revenue tax_rate = agi_tax / 100 target_spmu["new_taxes"] = target_spmu.adjginc * tax_rate target_spmu.new_resources -= target_spmu.new_taxes revenue += mdf.weighted_sum(target_spmu, "new_taxes", "spmwt") # Calculate the total UBI a spmu recieves based on exclusions target_spmu["numper_ubi"] = target_spmu.numper if "children" not in include: target_spmu["numper_ubi"] -= target_spmu.child if "non_citizens" not in include: target_spmu["numper_ubi"] -= target_spmu.non_citizen if ("children" not in include) and ("non_citizens" not in include): target_spmu["numper_ubi"] += target_spmu.non_citizen_child if "adults" not in include: target_spmu["numper_ubi"] -= target_spmu.adult if ("adults" not in include) and ("non_citizens" not in include): target_spmu["numper_ubi"] += target_spmu.non_citizen_adult # Assign UBI ubi_population = (target_spmu.numper_ubi * target_spmu.spmwt).sum() ubi_annual = revenue / ubi_population target_spmu["total_ubi"] = ubi_annual * target_spmu.numper_ubi # Calculate change in resources target_spmu.new_resources += target_spmu.total_ubi target_spmu["new_resources_per_person"] = (target_spmu.new_resources / target_spmu.numper) # NOTE: code after this applies to both reform levels # Merge and create target_persons - # NOTE: the "target" here refers to the population being # measured for gini/poverty rate/etc. # I.e. the total population of the state/country and # INCLUDING those excluding form recieving ubi payments sub_spmu = target_spmu[[ "spmfamunit", "year", "new_resources", "new_resources_per_person" ]] target_persons = person.merge(sub_spmu, on=["spmfamunit", "year"]) # filter demog_stats for selected state from dropdown baseline_demog = demog_stats[demog_stats.state == state_dropdown] # TODO: return dictionary of results instead of return each variable def return_demog(demog, metric): """ retrieve pre-processed data by demographic args: demog - string one of ['person', 'adult', 'child', 'black', 'white', 'hispanic', 'pwd', 'non_citizen', 'non_citizen_adult', 'non_citizen_child'] metric - string, one of ['pov_rate', 'pop'] returns: value - float """ # NOTE: baseline_demog is a dataframe with global scope value = baseline_demog.loc[ (baseline_demog["demog"] == demog) & (baseline_demog["metric"] == metric), "value", # NOTE: returns the first value as a float, be careful if you redefine baseline_demog ].values[0] return value population = return_demog(demog="person", metric="pop") child_population = return_demog(demog="child", metric="pop") non_citizen_population = return_demog(demog="non_citizen", metric="pop") non_citizen_child_population = return_demog(demog="non_citizen_child", metric="pop") # filter all state stats gini, poverty_gap, etc. for dropdown state baseline_all_state_stats = all_state_stats[all_state_stats.index == state_dropdown] def return_all_state(metric): """filter baseline_all_state_stats and return value of select metric Keyword arguments: metric - string, one of 'poverty_gap', 'gini', 'total_resources' returns: value- float """ return baseline_all_state_stats[metric].values[0] # Calculate total change in resources original_total_resources = return_all_state("total_resources") # DO NOT PREPROCESS, new_resources new_total_resources = (target_spmu.new_resources * target_spmu.spmwt).sum() change_total_resources = new_total_resources - original_total_resources change_pp = change_total_resources / population original_poverty_rate = return_demog("person", "pov_rate") original_poverty_gap = return_all_state("poverty_gap") # define orignal gini coefficient original_gini = return_all_state("gini") # function to calculate rel difference between one number and another def rel_change(new, old, round=3): return ((new - old) / old).round(round) # Calculate poverty gap target_spmu["new_poverty_gap"] = np.where( target_spmu.new_resources < target_spmu.spmthresh, target_spmu.spmthresh - target_spmu.new_resources, 0, ) poverty_gap = mdf.weighted_sum(target_spmu, "new_poverty_gap", "spmwt") poverty_gap_change = rel_change(poverty_gap, original_poverty_gap) # Calculate the change in poverty rate target_persons["poor"] = (target_persons.new_resources < target_persons.spmthresh) total_poor = (target_persons.poor * target_persons.asecwt).sum() poverty_rate = total_poor / population poverty_rate_change = rel_change(poverty_rate, original_poverty_rate) # Calculate change in Gini gini = mdf.gini(target_persons, "new_resources_per_person", "asecwt") gini_change = rel_change(gini, original_gini, 3) # Calculate percent winners target_persons["winner"] = (target_persons.new_resources > target_persons.spmtotres) total_winners = (target_persons.winner * target_persons.asecwt).sum() percent_winners = (total_winners / population * 100).round(1) # -------------- calculate all of the poverty breakdown numbers -------------- # # Calculate the new poverty rate for each demographic def pv_rate(column): return mdf.weighted_mean(target_persons[target_persons[column]], "poor", "asecwt") # Round all numbers for display in hover def hover_string(metric, round_by=1): """formats 0.121 to 12.1%""" string = str(round(metric * 100, round_by)) + "%" return string DEMOGS = ["child", "adult", "pwd", "white", "black", "hispanic"] # create dictionary for demographic breakdown of poverty rates pov_breakdowns = { # return precomputed baseline poverty rates "original_rates": {demog: return_demog(demog, "pov_rate") for demog in DEMOGS}, "new_rates": {demog: pv_rate(demog) for demog in DEMOGS}, } # add poverty rate changes to dictionary pov_breakdowns["changes"] = { # Calculate the percent change in poverty rate for each demographic demog: rel_change( pov_breakdowns["new_rates"][demog], pov_breakdowns["original_rates"][demog], ) for demog in DEMOGS } # create string for hover template pov_breakdowns["strings"] = { demog: "Original " + demog + " poverty rate: " + hover_string(pov_breakdowns["original_rates"][demog]) + "<br><extra></extra>" + "New " + demog + " poverty rate: " + hover_string(pov_breakdowns["new_rates"][demog]) for demog in DEMOGS } # format original and new overall poverty rate original_poverty_rate_string = hover_string(original_poverty_rate) poverty_rate_string = hover_string(poverty_rate) original_poverty_gap_billions = "{:,}".format( int(original_poverty_gap / 1e9)) poverty_gap_billions = "{:,}".format(int(poverty_gap / 1e9)) original_gini_string = str(round(original_gini, 3)) gini_string = str(round(gini, 3)) # --------------SECTION populates "Results of your reform:" ------------ # # Convert UBI and winners to string for title of chart ubi_string = str("{:,}".format(int(round(ubi_annual / 12)))) # populates Monthly UBI ubi_line = "Monthly UBI: $" + ubi_string # populates 'Funds for UBI' revenue_line = "Funds for UBI: $" + numerize.numerize(revenue, 1) # populates population and revenue for UBI if state selected from dropdown if state_dropdown != "US": # filter for selected state state_spmu = target_spmu[target_spmu.state == state_dropdown] # calculate population of state recieving UBI state_ubi_population = (state_spmu.numper_ubi * state_spmu.spmwt).sum() ubi_population_line = "UBI population: " + numerize.numerize( state_ubi_population, 1) state_revenue = ubi_annual * state_ubi_population revenue_line = ("Funds for UBI (" + state_dropdown + "): $" + numerize.numerize(state_revenue, 1)) else: ubi_population_line = "UBI population: " + numerize.numerize( ubi_population, 1) winners_line = "Percent better off: " + str(percent_winners) + "%" resources_line = ("Average change in resources per person: $" + "{:,}".format(int(change_pp))) # ---------- populate economic breakdown bar chart ------------- # # Create x-axis labels for each chart econ_fig_x_lab = ["Poverty rate", "Poverty gap", "Gini index"] econ_fig_cols = [poverty_rate_change, poverty_gap_change, gini_change] econ_fig = go.Figure([ go.Bar( x=econ_fig_x_lab, y=econ_fig_cols, text=econ_fig_cols, hovertemplate=[ # poverty rates "Original poverty rate: " + original_poverty_rate_string + "<br><extra></extra>" "New poverty rate: " + poverty_rate_string, # poverty gap "Original poverty gap: $" + original_poverty_gap_billions + "B<br><extra></extra>" "New poverty gap: $" + poverty_gap_billions + "B", # gini "Original Gini index: <extra></extra>" + original_gini_string + "<br>New Gini index: " + gini_string, ], marker_color=BLUE, ) ]) # Edit text and display the UBI amount and percent winners in title econ_fig.update_layout( uniformtext_minsize=10, uniformtext_mode="hide", plot_bgcolor="white", title_text="Economic overview", title_x=0.5, hoverlabel_align="right", font_family="Roboto", title_font_size=20, paper_bgcolor="white", hoverlabel=dict(bgcolor="white", font_size=14, font_family="Roboto"), yaxis_tickformat="%", ) econ_fig.update_traces(texttemplate="%{text:.1%f}", textposition="auto") econ_fig.update_xaxes( tickangle=45, title_text="", tickfont={"size": 14}, title_standoff=25, title_font=dict(size=14, family="Roboto", color="black"), ) econ_fig.update_yaxes( tickprefix="", tickfont={"size": 14}, title_standoff=25, title_font=dict(size=14, family="Roboto", color="black"), ) # ------------------ populate poverty breakdown charts ---------------- # breakdown_fig_x_lab = [ "Child", "Adult", "People<br>with<br>disabilities", "White", "Black", "Hispanic", ] breakdown_fig_cols = [pov_breakdowns["changes"][demog] for demog in DEMOGS] hovertemplate = [pov_breakdowns["strings"][demog] for demog in DEMOGS] breakdown_fig = go.Figure([ go.Bar( x=breakdown_fig_x_lab, y=breakdown_fig_cols, text=breakdown_fig_cols, hovertemplate=hovertemplate, marker_color=BLUE, ) ]) breakdown_fig.update_layout( uniformtext_minsize=10, uniformtext_mode="hide", plot_bgcolor="white", title_text="Poverty rate breakdown", title_x=0.5, hoverlabel_align="right", font_family="Roboto", title_font_size=20, paper_bgcolor="white", hoverlabel=dict(bgcolor="white", font_size=14, font_family="Roboto"), yaxis_tickformat="%", ) breakdown_fig.update_traces(texttemplate="%{text:.1%f}", textposition="auto") breakdown_fig.update_xaxes( tickangle=45, title_text="", tickfont=dict(size=14, family="Roboto"), title_standoff=25, title_font=dict(size=14, family="Roboto", color="black"), ) breakdown_fig.update_yaxes( tickprefix="", tickfont=dict(size=14, family="Roboto"), title_standoff=25, title_font=dict(size=14, family="Roboto", color="black"), ) # set both y-axes to the same range full_econ_fig = econ_fig.full_figure_for_development(warn=False) full_breakdown_fig = breakdown_fig.full_figure_for_development(warn=False) # find the minimum of both y-axes global_ymin = min( min(full_econ_fig.layout.yaxis.range), min(full_breakdown_fig.layout.yaxis.range), ) global_ymax = max( max(full_econ_fig.layout.yaxis.range), max(full_breakdown_fig.layout.yaxis.range), ) # update the yaxes of the figure to account for both ends of the ranges econ_fig.update_yaxes( dict(range=[global_ymin, global_ymax], autorange=False)) breakdown_fig.update_yaxes( dict(range=[global_ymin, global_ymax], autorange=False)) return ( ubi_line, revenue_line, ubi_population_line, winners_line, resources_line, econ_fig, breakdown_fig, )
### Add senior UBI to `aftertax_income` and recalculate reform['ubi'] = senior_ubi * reform.n65 reform['aftertax_income'] = reform.aftertax_income + reform.ubi mdf.add_weighted_metrics(reform, 'aftertax_income') np.allclose(base.aftertax_income_m.sum(), reform.aftertax_income_m.sum()) ## Analyze Gini, FPL, distributional impact chart ### Change to Gini index mdf.gini(base.aftertax_income, base.s006) mdf.gini(reform.aftertax_income, reform.s006) ### Change to poverty rate Add federal poverty line with `mdf.fpl`. base['fpl'] = mdf.fpl(base.XTOT) reform['fpl'] = mdf.fpl(reform.XTOT) base['fpl_XTOT_m'] = np.where(base.aftertax_income < base.fpl, base.XTOT_m, 0) reform['fpl_XTOT_m'] = np.where(reform.aftertax_income < reform.fpl, reform.XTOT_m, 0)
def loss_metrics( x: list, baseline_df: pd.DataFrame, reform_base_df: pd.DataFrame, budget: int, ) -> pd.Series: """Calculate each potential loss metric. :param x: List of optimization elements: [senior, child, dis_base, region1, region2, ..., region12] :type x: list :return: Series with five elements: loser_share: Share of the population who come out behind. losses: Total losses among losers in pounds. mean_pct_loss: Average percent loss across the population (including zeroes for people who don't experience losses). gini: Gini index of per-person household net income in the reform scenario, weighted by person weight at the household level. :rtype: pd.Series """ senior, child, dis_base, regions = extract(x) reform_df = set_ubi( reform_base_df, budget, senior, child, dis_base, regions, ) # Calculate loss-related loss metrics. change = reform_df.net_income - baseline_df.net_income loss = np.maximum(-change, 0) weight = baseline_df.household_weight * baseline_df.people # Calculate loser share. total_pop = np.sum(weight) losers = np.sum(weight * (loss > 0)) loser_share = losers / total_pop # Calculate total losses in pounds. losses = np.sum(weight * loss) # Calculate average percent loss (including zero for non-losers). pct_loss = loss / baseline_df.net_income # Avoid infinite percent changes and backward changes due to negative # baseline income. valid_pct_loss = baseline_df.net_income > 0 total_pct_loss = np.sum(weight[valid_pct_loss] * pct_loss[valid_pct_loss]) mean_pct_loss = total_pct_loss / total_pop # Gini of income per person. reform_hh_net_income_pp = reform_df.net_income / baseline_df.people # mdf.gini requires a dataframe. reform_df = pd.DataFrame({ "reform_hh_net_income_pp": reform_hh_net_income_pp, "weight": weight }) gini = mdf.gini(reform_df, "reform_hh_net_income_pp", "weight") # Return Series of all metrics. return pd.Series({ "loser_share": loser_share, "losses": losses, "mean_pct_loss": mean_pct_loss, "gini": gini, })
def ubi( funding_billions=0, child_percent_funding=None, child_percent_ubi=None ): """ args: funding_billions: total annual funding for UBI program in billions USD child_percent_funding: percent (in whole numbers) of annual funds earmarked for child beneficiaries child_percent_ubi: proportion of child beneficiary's UBI benefit compared to adult beneficiary's payment returns: series of elements: poverty_rate: headcount poverty rate using Supplemental Poverty Measure threshold gini: gini coefficient of income poverty_gap: SPM unit poverty gap percent_better_off: percent of population with higher income after new transfers and taxes adult_ubi: annual per adult benefit in dollars child_ubi: annual per adult benefit in dollars """ funding = funding_billions * 1e9 if child_percent_funding is not None: child_percent_funding /= 100 adult_ubi = ((1 - child_percent_funding) * funding) / adult_pop child_ubi = (child_percent_funding * funding) / child_pop else: child_percent_ubi /= 100 adult_ubi = funding / (adult_pop + (child_pop * child_percent_ubi)) child_ubi = adult_ubi * child_percent_ubi tax_rate = funding / total_taxable_income spmu["new_tax"] = tax_rate * spmu.tax_inc spmu["spm_ubi"] = (spmu.child * child_ubi) + (spmu.adult * adult_ubi) spmu["new_spm_resources"] = ( spmu.spm_resources + spmu.spm_ubi - spmu.new_tax ) spmu["new_spm_resources_pp"] = spmu.new_spm_resources / spmu.spm_numper # Calculate poverty gap poverty_gap = pov_gap( spmu, "new_spm_resources", "spm_povthreshold", "spm_weight" ) # Merge person and spmu dataframes spmu_sub = spmu[["spm_id", "new_spm_resources", "new_spm_resources_pp"]] target_persons = pd.merge(spmu_sub, person, on=["spm_id"]) target_persons["poor"] = ( target_persons.new_spm_resources < target_persons.spm_povthreshold ) total_poor = (target_persons.poor * target_persons.person_weight).sum() poverty_rate = total_poor / pop * 100 # Calculate Gini gini = mdf.gini(target_persons, "new_spm_resources_pp", w="person_weight") # Percent winners target_persons["better_off"] = ( target_persons.new_spm_resources > target_persons.spm_resources ) total_better_off = ( target_persons.better_off * target_persons.person_weight ).sum() percent_better_off = total_better_off / pop return pd.Series( { "poverty_rate": poverty_rate, "gini": gini, "poverty_gap": poverty_gap, "percent_better_off": percent_better_off, "adult_ubi": adult_ubi, "child_ubi": child_ubi, } )
index="state", columns="sim_flag") # Construct poverty percentage changes def percent_change(base, new): return 100 * (new - base) / new state["poverty_change_cc"] = percent_change(state.baseline, state.cc_replacement) state["poverty_change_flat"] = percent_change(state.baseline, state.child_allowance) # Gini coefficients mdf.gini(person_sim, "spmftotval", "asecwt") mdf.gini(person_sim, "resources_pp", "asecwt") person_sim.groupby("sim_flag").apply( lambda x: mdf.gini(x, "spmftotval", "asecwt")) person_sim.groupby("sim_flag").apply( lambda x: mdf.gini(x, "resources_pp", "asecwt")) # Re-arrange by poverty rate state.sort_values(by="poverty_change_flat", ascending=False) # Interesting findings: # Flat transfer: Roughly 10* the impact on poverty and gini coefficient # compared to the childcare provision equivalent policy (paying costs) # The poverty change is much larger for female-identifying people. # The poverty change for the flat transfer is largest for Black and # Hispanic populations ~3%, lower for White ~1.2%, and other non-hispanic ~1.8%.