library(tidyverse)library(ptaxsim)library(DBI)library(httr)library(jsonlite)library(glue)library(DT)knitr::opts_chunk$set(warning =FALSE, message =FALSE)file_path <-"C:/Users/aleaw/"taxyear <-c(2021)taxyear =as.data.frame(taxyear)source("../scripts/helper_tc_muninames.R") tc_muninames <- tc_muninames |>select(-tax_code_rate)#ptaxsim_db_conn <- DBI::dbConnect(RSQLite::SQLite(), "./ptaxsim.db/ptaxsim-2023.0.0.db")if (file.exists(file_path)){ ptaxsim_db_conn <- DBI::dbConnect(RSQLite::SQLite(), "C:/Users/aleaw/OneDrive/Documents/PhD Fall 2021 - Spring 2022/Merriman RA/ptax/ptaxsim.db/ptaxsim-2023.0.0.db")} else { ptaxsim_db_conn <- DBI::dbConnect(RSQLite::SQLite(), "./ptaxsim.db/ptaxsim-2023.0.0.db")}current_dir =getwd()if (grepl("Website", current_dir)) { dots ="../"} else { dots ="./"}path <-paste0(dots, "Necessary_Files/class_dict_singlefamcodes.csv")class_dict <-read_csv(path) %>%mutate(class_code =as.character(class_code)) # change variable type to character so the join works.path <-paste0(dots, "inputs/ccao_loa.csv")ccao_loa <-read_csv(path) %>%mutate(class_code =as.character(class_code))eq_factor =3.0027# get all tax codes and tax ratestax_codes <- DBI::dbGetQuery( ptaxsim_db_conn, glue_sql(" SELECT DISTINCT tax_code_num, tax_code_rate FROM tax_code WHERE year = 2021 " , .con = ptaxsim_db_conn)) |>mutate(tax_code_rate = tax_code_rate /100)# get percent of eav that goes to a TIF:tif_distribution <- DBI::dbGetQuery( ptaxsim_db_conn, glue_sql(" SELECT DISTINCT* FROM tif_distribution WHERE year = 2021 " , .con = ptaxsim_db_conn)) %>%select(-c(tax_code_rate, agency_num)) |>mutate(tax_code_distribution_pct = tax_code_distribution_pct /100)# All PINs - get AV and exemption amounts -----------------------------# Include ALL PINs for calculating muni level composite rates:cook_pins <- DBI::dbGetQuery( ptaxsim_db_conn, glue_sql(" SELECT DISTINCT* FROM pin WHERE year = 2021 " , .con = ptaxsim_db_conn)) # create some variables that we need:muni_rates <- cook_pins |>left_join(tax_codes) |>left_join(tc_muninames) |>left_join(tif_distribution) |>mutate(tax_code_distribution_pct =ifelse(is.na(tax_code_distribution_pct), 0, tax_code_distribution_pct),in_tif =ifelse(tax_code_num %in% tif_distribution$tax_code_num, 1, 0),has_HO_exe =ifelse(exe_homeowner >0, 1, 0),has_other_exe =ifelse(exe_senior + exe_freeze + exe_longtime_homeowner + exe_disabled + exe_vet_returning + exe_vet_dis_lt50 + exe_vet_dis_50_69 + exe_vet_dis_ge70 >0, 1, 0 ),final_tax_to_tif = tax_bill_total * tax_code_distribution_pct, # TIF revenuefinal_tax_to_dist = tax_bill_total - final_tax_to_tif, # local government revenueall_exemptions =rowSums(across(starts_with("exe_"))),tif_increment_eav = final_tax_to_tif / tax_code_rate,nontif_eav_preexe = av_clerk*eq_factor - tif_increment_eav,nontif_eav_postexe = av_clerk*eq_factor - tif_increment_eav - all_exemptions,current_taxbase = final_tax_to_dist / tax_code_rate, # should match nontif_eav_postexe amounteav_postexemptions = av_clerk*eq_factor - all_exemptions, # ignores tif distributioneav_preexemptions = av_clerk*eq_factor, # ignores tif distribution ) %>%mutate(av = av_clerk) |># av used for calculations is the final av value from the clerk's officegroup_by(clean_name)|>summarize(tax_bill_total =sum(tax_bill_total), # TIF and local government revenuetif_increment_eav =sum(tif_increment_eav, na.rm=T), # should be sum of frozen EAVmuni_taxed_eav =sum(nontif_eav_postexe, na.rm=T), # non-TIF and non-exempt EAV taxed by government agenciesmuni_exempt_eav_from_GHE =sum(exe_homeowner, na.rm=T),muni_levy =sum(final_tax_to_dist, na.rm=T),muni_min_rate =min(tax_code_rate),muni_median_rate =median(tax_code_rate),muni_avg_rate =mean(tax_code_rate, na.rm=T),muni_max_rate =max(tax_code_rate),final_tax_to_dist =sum(final_tax_to_dist, na.rm=T),final_tax_to_tif =sum(final_tax_to_tif, na.rm=T)) |>mutate(muni_hyp_taxbase = muni_taxed_eav + muni_exempt_eav_from_GHE, muni_hyp_taxrate = muni_levy / muni_hyp_taxbase)## Tax code level "levies" and tax bases ----------------tax_code_change <- cook_pins |>left_join(tax_codes) |>left_join(tc_muninames) |>left_join(tif_distribution) |>mutate(tax_code_distribution_pct =ifelse(is.na(tax_code_distribution_pct), 0, tax_code_distribution_pct),in_tif =ifelse(tax_code_num %in% tif_distribution$tax_code_num, 1, 0),has_HO_exe =ifelse(exe_homeowner >0, 1, 0),has_other_exe =ifelse(exe_senior + exe_freeze + exe_longtime_homeowner + exe_disabled + exe_vet_returning + exe_vet_dis_lt50 + exe_vet_dis_50_69 + exe_vet_dis_ge70 >0, 1, 0 ),all_exemptions =rowSums(across(starts_with("exe_"))),final_tax_to_tif = tax_bill_total * tax_code_distribution_pct/100,final_tax_to_dist = tax_bill_total - final_tax_to_tif,tif_increment_eav = final_tax_to_tif / tax_code_rate,nontif_eav_preexe = av_clerk*eq_factor - tif_increment_eav,nontif_eav_postexe = av_clerk*eq_factor - tif_increment_eav - all_exemptions,eav_postexemptions = av_clerk*eq_factor - all_exemptions,eav_preexemptions = av_clerk*eq_factor,total_bill = final_tax_to_tif + final_tax_to_dist ) %>%group_by(clean_name, tax_code_num, tax_code_rate) |>summarize(tc_pc =n(),tc_tax_bill_total =sum(tax_bill_total), # TIF and local government revenue at tax code leveltc_eav_inTIF =sum(tif_increment_eav, na.rm=T), # should be sum of frozen EAVtc_taxed_eav =sum(nontif_eav_postexe, na.rm=T), # non-TIF and non-exempt EAV taxed by government agenciestc_exempt_eav_from_GHE =sum(exe_homeowner, na.rm=T),tc_final_tax_to_dist =sum(final_tax_to_dist, na.rm=T),tc_final_tax_to_tif =sum(final_tax_to_tif, na.rm=T)) |>mutate(tc_hyp_taxbase = tc_taxed_eav + tc_exempt_eav_from_GHE, tc_hyp_taxrate = tc_final_tax_to_dist / tc_hyp_taxbase)# Identify Class 2 Residential PINs ------------- c2pins <- DBI::dbGetQuery( ptaxsim_db_conn, glue_sql(" SELECT DISTINCT* FROM pin WHERE class > 199 AND class < 300 AND year = 2021 " , .con = ptaxsim_db_conn))c2pins <- c2pins |>mutate(has_HO_exe =ifelse( exe_homeowner >0, 1, 0),has_other_exe =ifelse(exe_senior + exe_freeze + exe_longtime_homeowner + exe_disabled + exe_vet_returning + exe_vet_dis_lt50 + exe_vet_dis_50_69 + exe_vet_dis_ge70 >0, 1, 0 )) %>%left_join(tax_codes) |>left_join(tc_muninames) %>%mutate(av = av_clerk) |>left_join(tax_code_change) |>left_join(tif_distribution) |>mutate(tax_code_distribution_pct =ifelse(is.na(tax_code_distribution_pct), 0, tax_code_distribution_pct),in_tif =ifelse(tax_code_num %in% tif_distribution$tax_code_num, 1, 0),has_HO_exe =ifelse(exe_homeowner >0, 1, 0),has_other_exe =ifelse(exe_senior + exe_freeze + exe_longtime_homeowner + exe_disabled + exe_vet_returning + exe_vet_dis_lt50 + exe_vet_dis_50_69 + exe_vet_dis_ge70 >0, 1, 0 ),all_exemptions =rowSums(across(starts_with("exe_"))),final_tax_to_tif = tax_bill_total * tax_code_distribution_pct/100,final_tax_to_dist = tax_bill_total-final_tax_to_tif,tif_increment_eav = final_tax_to_tif / tax_code_rate,nontif_eav_preexe = av_clerk*eq_factor - tif_increment_eav,nontif_eav_postexe = av_clerk*eq_factor - tif_increment_eav - all_exemptions,eav_postexemptions = av_clerk*eq_factor - all_exemptions,eav_preexemptions = av_clerk*eq_factor)
Code
q =c(.1, .25, .5, .75, .9)## ranks properties that are considered C2 properties in order of AV for each Munimuni_quartiles <- c2pins %>%group_by(clean_name) %>%arrange(av) %>%summarize(count_pins =n(), min =min(av),quant10 =round(quantile(av, probs = q[1])), quant25 =round(quantile(av, probs = q[2])), quant50 =round(quantile(av, probs = q[3])),quant75 =round(quantile(av, probs = q[4])),quant90 =round(quantile(av, probs = q[5])),max =max(av) ) %>%arrange(desc(quant50))# Class 2 Descriptive Stats C2_munistats <- c2pins %>%filter(class >199& class <300) %>%group_by(clean_name) %>%arrange(av) %>%summarize(median_eav =round(median(eav_postexemptions)), median_av =round(median(av)), avg_av =round(mean(av)),avg_eav =round(mean(eav_postexemptions)),C2_pins_in_muni =n(),C2_current_exemptions =sum(all_exemptions, na.rm =TRUE),C2_HO_exemps =sum(exe_homeowner, na.rm =TRUE), ) # All Class 2 Propertiesmunis_ranked <- c2pins %>%inner_join(muni_quartiles, by =c("clean_name")) %>%mutate(rank =case_when( av > (quant10-500) & (av<quant10+500) ~"q10", av > (quant25-500) & (av<quant25+500) ~"q25", av > (quant50-500) & (av<quant50+500) ~"q50", av > (quant75-500) & (av<quant75+500) ~"q75", av > (quant90-500) & (av<quant90+500) ~"q90") ) %>%select(clean_name, rank, av, pin, class, everything())
Which taxpayers benefit the most from homestead exemptions?
Table 1. Comparison of Tax Bills for Median Assessed Value (AV) Class 2 Properties in Selected Municpalities under the Status Quo and Alternative Scenario
Assessed Value is used to identify the quartile breaks within each municipality for single-family properties. These break points will then be used to look at how the tax bill changes for properties with AVs at the 25th percentile, 50th percentile, and 75th percentile.
The assessed value and original equalized assessed values come from the pin data table within PTAXSIM. This table also has every type of exemption that the property received and the amount of EAV that was exempt due to the exemption.
The table created from the code calculates the median AV for those with exemptions and those without exemptions. Usually these values are significantly different. In the Report, the table was simplified to show the save assessed value for both types of homeowners (See image of table pasted below).
Notes: AV = Assessed Value. “Tax Calculator” reduction refers to the section of the tax bill received by property tax payers (See Figure 1). It does not take into account changes in property tax rates due to the larger taxbase that would come from having a larger tax base. Figures in columns titled Current Bill, Tax Bill without GHE, and “Tax Calculator” Reduction in Tax Liability are estimated using an average of the properties with AV within $1,000 of the municipality median in order to enlarge the number of observations therefore do not precisely represent the median figure.
Figure 1. Examples of Actual Tax Bills
Park Forest vs. Winnetka
Figure 2. Value of Median Residential Property Indifferent to Elimination of GHE
Code
muni_breakeven_points <- c2pins |>left_join(muni_rates) |>filter(exe_homeowner ==10000) %>%filter(exe_senior ==0& exe_freeze ==0& exe_longtime_homeowner ==0& exe_disabled ==0& exe_vet_returning ==0& exe_vet_dis_lt50 ==0& exe_vet_dis_50_69 ==0& exe_vet_dis_ge70 ==0& exe_abate ==0) %>%group_by(clean_name) %>%summarize(median_AV =median(av), # median class 2 AV for properties that did claim the GHE but not other exemptionsbill_current =mean(eav_postexemptions * tax_code_rate), # money collected by non-TIF agenciesbill_noGHE =mean(tc_hyp_taxrate * (eav_postexemptions+exe_homeowner)), # this uses tax code tax raterate_change =mean(tax_code_rate - tc_hyp_taxrate), # avg tax code level rate change muni_avg_rate_change =mean(muni_avg_rate - muni_hyp_taxrate),nobillchange_propertyEAV =round(mean(exe_homeowner * (tax_code_rate / (tax_code_rate-tc_hyp_taxrate)))), # nobillchange_propertyEAV_muni =round(mean(exe_homeowner * (muni_avg_rate/(muni_avg_rate-muni_hyp_taxrate) ))) ) |>mutate(nochange_av = nobillchange_propertyEAV / eq_factor,nochange_av_muni = nobillchange_propertyEAV_muni/ eq_factor, ) muni_breakeven_points %>%filter(clean_name %in%c("Chicago","Park Forest", "Phoenix", "Riverdale", "Winnetka")) |>ggplot(aes(y=median_AV, x = clean_name)) +geom_col()+geom_text(aes(y=median_AV +3000, label =round(median_AV) ) ) +scale_y_continuous(labels = scales::dollar) +theme_classic() +scale_x_discrete(label =c("Chicago","Park Forest", "Phoenix", "Riverdale", "Winnetka")) +labs(y ="Median Residential AV", x ="", title ="Median Residential Property Assessed Value - All Class 2 property Types")
Code
muni_breakeven_points %>%filter(clean_name %in%c("Chicago","Park Forest", "Phoenix", "Riverdale", "Winnetka")) |>ggplot(aes(y=nochange_av, x = clean_name)) +geom_col() +geom_text(aes(y=nochange_av +3000, label =round(nochange_av)) ) +scale_y_continuous(labels = scales::dollar) +theme_classic() +scale_x_discrete(label =c("Chicago", "Park Forest", "Phoenix", "Riverdale", "Winnetka")) +labs(y ="Breakeven Point - AV", x ="", title ="Residential Property AV Breakeven Point", caption ="Residential properties above these values would have their bills decrease if the GHE were eliminated (if they had claimed the GHE before)")
Code
#|include: false## Doesn't make bars / doesn't work. idk why ?? I don't need it, I just don't know why it doesn't work. muni_breakeven_points %>%filter(clean_name %in%c("Chicago","Park Forest", "Phoenix", "Riverdale", "Winnetka")) |>ggplot(aes(y=nochange_av_muni, x = clean_name)) +geom_col() +geom_text(aes(y=nochange_av_muni +3000, label =round(nochange_av_muni)) ) +scale_y_continuous(labels = scales::dollar) +theme_classic() +scale_x_discrete(label =c("Chicago", "Park Forest", "Phoenix", "Riverdale", "Winnetka")) +labs(y ="Breakeven Point - AV", x ="", title ="Residential Property AV Breakeven Point", caption ="Residential properties above these values would have their bills decrease if the GHE were eliminated (if they had claimed the GHE before)")
Code
muni_breakeven_points %>%# filter(nochange_av < 300000) %>%ggplot(aes(y=nochange_av, x = median_AV, label=clean_name )) +geom_abline(intercept =0, slope =1, lty =2, alpha = .4) +geom_point(aes(alpha = .5)) +# geom_smooth(method = "lm" )+geom_text(aes(y = (nochange_av-5000), x = (median_AV)), size =2)+theme_classic() +scale_x_continuous(labels = scales::dollar) +scale_y_continuous(labels = scales::dollar) +theme(legend.position ="none")+labs(y =" Breakeven Point", x ="Median AV - Class 2 Properties in Municipality", # title = "Some highly valued homes would have lower tax bills if the GHE were eliminated", #caption = "The breakeven point is the assessed values at which a major class 2 # property would not have their taxbill change if the GHE were eliminated." )
Code
muni_breakeven_points %>%filter(nochange_av <300000) %>%ggplot(aes(y=nochange_av, x = median_AV, label=clean_name )) +geom_abline(intercept =0, slope =1, lty =2, alpha = .4) +geom_point(aes(alpha = .5)) +geom_point(data = (muni_breakeven_points %>%filter(clean_name %in%c("Park Forest", "Chicago", "Winnetka", "Riverdale","Dolton"))), aes(y = nochange_av, x = median_AV, color ="red"), size =3) + ggrepel::geom_label_repel(data = (muni_breakeven_points %>%filter(clean_name %in%c("Park Forest", "Chicago", "Winnetka", "Riverdale", "Dolton"))), aes(y = (nochange_av), x = (median_AV)), size =3)+theme_classic() +theme(panel.background =element_blank()) +scale_x_continuous(labels = scales::dollar, expand =c(0,0), limits =c(0,170000) ) +scale_y_continuous(labels = scales::dollar, expand =c(0,0), limits =c(0,170000) ) +theme(legend.position ="none") +labs(title ="Municipalities' Median AV & Breakeven Point \nClass 2 Properties Only",y ="Breakeven Point", x ="Median AV", # caption = "The breakeven point is the assessed values at which a major class 2 property would not have their taxbill change if the # GHE were eliminated. Excludes outliers: University Park, Bedford Park, McCook, Hodgkins, and Rosemont. Class 2 properties that had# claimed the GHE would have lower taxbills even if the GHE were removed if the AV is above the breakeven point." ) +coord_fixed()#ggsave("breakevenpoint.svg", bg = "transparent")
How progressive are exemptions?
Figure 3. Ratio of Tax Bills to AV for 25th and 75th Percentile AV Class 2 Properties with $10,000 GHE
For the different exemption scenarios, we created new exemption variables for alternate exemption amounts that can be subtracted from a properties equalized AV.
ex. For exe_neg10 all exemption amounts are equal to zero.
For exe_0, This variable should be the same as the current tax system since we did not add or remove any exempt EAV to the PIN. If the EAV for a PIN is less than 10000 EAV and they did claim the general homestead exemption, then their exempt EAV is equal to their EAV.
If the exempt EAV allowable for the GHE was increased to 20,000 EAV, then the variable exe_plus10 is used.
Code
cook_pins <- cook_pins %>%left_join(class_dict, by =c("class"="class_code")) %>%mutate(has_HO_exe =ifelse( exe_homeowner >0, 1, 0),has_other_exe =ifelse(exe_senior + exe_freeze + exe_longtime_homeowner + exe_disabled + exe_vet_returning + exe_vet_dis_lt50 + exe_vet_dis_50_69 + exe_vet_dis_ge70 >0, 1, 0 )) %>%left_join(tax_codes) |>left_join(tc_muninames) %>%mutate(av = av_clerk) |>left_join(tax_code_change) |>left_join(tif_distribution) |>mutate(tax_code_distribution_pct =ifelse(is.na(tax_code_distribution_pct), 0, tax_code_distribution_pct),in_tif =ifelse(tax_code_num %in% tif_distribution$tax_code_num, 1, 0),has_HO_exe =ifelse(exe_homeowner >0, 1, 0),has_other_exe =ifelse(exe_senior + exe_freeze + exe_longtime_homeowner + exe_disabled + exe_vet_returning + exe_vet_dis_lt50 + exe_vet_dis_50_69 + exe_vet_dis_ge70 >0, 1, 0 ),all_exemptions =rowSums(across(starts_with("exe_"))),final_tax_to_tif = tax_bill_total * tax_code_distribution_pct,final_tax_to_dist = tax_bill_total-final_tax_to_tif,tif_increment_eav = final_tax_to_tif / tax_code_rate,nontif_eav_preexe = av_clerk*eq_factor - tif_increment_eav,nontif_eav_postexe = av_clerk*eq_factor - tif_increment_eav - all_exemptions,eav_postexemptions = av_clerk*eq_factor - all_exemptions,eav_preexemptions = av_clerk*eq_factor) |>## Create variables for alternate exemption amounts that can be subtracted from a properties equalized AVmutate(eav = eav_preexemptions,exe_neg10 =0,# exe_0 implies no additional or removed EAV. Current tax system. exe_0 =ifelse(eav <10000& exe_homeowner!=0, eav, ifelse(eav>10000& exe_homeowner!=0, 10000, 0 )), # no change in current exemptionsexe_plus10 =ifelse(eav <20000& exe_homeowner!=0, eav, ifelse(eav>20000& exe_homeowner!=0, 20000, 0 )),exe_plus20 =ifelse(eav <30000& exe_homeowner!=0, eav, ifelse(eav>30000& exe_homeowner!=0, 30000, 0 ) ),exe_plus30 =ifelse(eav <40000& exe_homeowner!=0, eav, ifelse(eav>40000& exe_homeowner!=0, 40000, 0) ),exe_plus40 =ifelse(eav <50000& exe_homeowner!=0, eav, ifelse(eav>50000& exe_homeowner!=0, 50000, 0) ),mil_home =ifelse(major_class_code ==2& av*10>1000000, 1, 0))# all pins in munis fully within cook county that are some form of single-family, detached homesingfam_pins <- cook_pins %>%filter(Option2 =="Single-Family")
Code
# Calculates tax rates for all exemption scenarios.scenario_calcs <- cook_pins %>%left_join(nicknames) %>%group_by(clean_name) %>%summarize(MuniLevy =sum(final_tax_to_dist, na.rm =TRUE), # amount billed by munis with current exemptions in placecurrent_nonTIF_EAV_post_exemps =sum(final_tax_to_dist/(tax_code_rate), na.rm =TRUE),current_TIF_increment_EAV =sum(final_tax_to_tif/(tax_code_rate), na.rm=TRUE), current_Exempt_EAV =sum(all_exemptions, na.rm=T), current_GHE =sum(exe_homeowner, na.rm=TRUE),Total_EAV =sum(all_exemptions+(final_tax_to_dist+final_tax_to_tif)/(tax_code_rate), na.rm =TRUE),exe_neg10 =sum(exe_neg10),exe_0 =sum(exe_0), # no change, for comparisonexe_plus10 =sum(exe_plus10),exe_plus20 =sum(exe_plus20),exe_plus30 =sum(exe_plus30),exe_plus40 =sum(exe_plus40),mil_home =sum(mil_home) ) %>%# remove all GHE (up to 10,000 EAV added back to base per PIN), # add exe_homeowner back to taxable basemutate(neg10_taxable_eav = Total_EAV - current_TIF_increment_EAV - current_Exempt_EAV + current_GHE, # adds GHE exempt EAV back to taxable base and decreases tax ratesplus10_taxable_eav = Total_EAV - current_TIF_increment_EAV - current_Exempt_EAV + current_GHE - exe_plus10, # will increase tax ratesplus20_taxable_eav = Total_EAV - current_TIF_increment_EAV - current_Exempt_EAV + current_GHE - exe_plus20,plus30_taxable_eav = Total_EAV - current_TIF_increment_EAV - current_Exempt_EAV + current_GHE - exe_plus30,plus40_taxable_eav = Total_EAV - current_TIF_increment_EAV - current_Exempt_EAV + current_GHE - exe_plus40,scenario_noexemptions_taxable_eav = Total_EAV - current_TIF_increment_EAV) %>%mutate(tr_neg10 = MuniLevy / neg10_taxable_eav,tr_nochange = MuniLevy / current_nonTIF_EAV_post_exemps,tr_plus10 = MuniLevy / plus10_taxable_eav,tr_plus20 = MuniLevy / plus20_taxable_eav,tr_plus30 = MuniLevy / plus30_taxable_eav,tr_plus40 = MuniLevy / plus40_taxable_eav, tax_rate_current = MuniLevy/current_nonTIF_EAV_post_exemps,taxrate_noexemps = MuniLevy /(Total_EAV - current_TIF_increment_EAV ),taxrate_noTIFs = MuniLevy / (Total_EAV - current_Exempt_EAV),taxrate_noTIFs_orExemps = MuniLevy / Total_EAV) %>%select(clean_name, MuniLevy, tr_neg10:taxrate_noTIFs_orExemps, everything())scenario_taxrates <- scenario_calcs %>%select(clean_name, MuniLevy, tr_neg10:taxrate_noTIFs_orExemps) scenario_taxrates
cook_pins %>%filter(av <300000) %>%# just to see the histogram betterggplot( aes(x=av)) +geom_histogram(bins =50) +theme_classic()+labs(title ="Cook County Class 2 Residential PIN Distribution of AV", x ="Assessed Value ($)", y="# of Pins", caption ="Dropped PINs with AVs over $300,000 for better visual of histogram bins.") +scale_x_continuous(label = scales::dollar) +scale_y_continuous(label = scales::comma)singfam_pins %>%filter(av <300000) %>%# just to see the histogram betterggplot( aes(x=av)) +geom_histogram(bins =50) +theme_classic()+labs(title ="Cook County Single-Family PIN Distribution of AV", x ="Assessed Value ($)", y="# of Pins", caption ="Dropped PINs with AVs over $300,000 for better visual of histogram bins." ) +scale_x_continuous(label = scales::dollar)+scale_y_continuous(label = scales::comma)
What would happen if the GHE value were increased?
Figure 4. Effect of Changing the GHE on Progressivity