Property Tax Fairness
From the Center for Municipal Finance

To generate the report, first download the raw data. This dataset is a raw public dataset we can use.

After downloading the dataset, please run the below R code, for example in RStudio. Remember to replace your output directory” with the path where you want to save the generated report.

library(tidyverse)
library(data.table)
library(readstata13)
library(cmfproperty)
extract_dir <- “extracts/”
raw_dir <- “raw data/”

sales1 <- read.dta13(paste0(raw_dir, “sales.dta”))
sales1 <- sales1 %>% select(bble, YEAR, TAX_CLASS_AT_TIME_OF_SALE, ADDRESS, SALE_PRICE)
sales2 <- fread(paste0(raw_dir, “NYC_Citywide_Annualized_Calendar_Sales_Update.csv”), colClasses = “character”)
sales2 <- sales2 %>% select(BBL, `SALE DATE`, `TAX CLASS AT TIME OF SALE`, ADDRESS, `SALE PRICE`) %>%
rename(bble = BBL, tmp = `SALE DATE`, TAX_CLASS_AT_TIME_OF_SALE = `TAX CLASS AT TIME OF SALE`, SALE_PRICE = `SALE PRICE`) %>%
separate(tmp, c(“Month”, “Day”, “YEAR”)) %>% select(-c(“Month”, “Day”)) %>% distinct()
sales <- rbind(sales1, sales2)
sales <- sales %>% distinct() %>% filter(SALE_PRICE > 100 & TAX_CLASS_AT_TIME_OF_SALE %in% c(1, 2)) %>% mutate(YEAR = as.numeric(YEAR))
fwrite(sales, paste0(raw_dir, “joined_NY_sales.csv”))

assessments1 <- read.dta13(paste0(raw_dir, “assessment.dta”))
names(assessments1) <- c(“BBLE”, “YEAR”, “AVTOT”, “cur_year_val”, “FULLVAL”)
assessments1 <- assessments1 %>% mutate(TAXCLASS = “1”,
YEAR = YEAR + 1) %>% select(-cur_year_val)
assessments1 <- assessments1 %>% filter(YEAR > 2003)

assessments2 <- fread(paste0(raw_dir, “Property_Valuation_and_Assessment_Data.csv”), colClasses = “character”)
assessments2 <- assessments2 %>% select(BBLE, YEAR, TAXCLASS, FULLVAL, AVTOT) %>%
separate(YEAR, c(“YEAR”, “toss”)) %>%
select(-toss) %>%
mutate_at(c(“YEAR”, “AVTOT”, “FULLVAL”), as.numeric)

assessments2 <- assessments2 %>% mutate(TAXCLASS = ifelse(TAXCLASS %in% c(“1”, “1A”, “1B”, “1C”, “1D”), “1”, TAXCLASS))
assessments2_1 <- assessments2 %>% filter(TAXCLASS == 1 & YEAR == 2018)

assessments2 <- assessments2 %>% mutate(TAXCLASS_original = TAXCLASS)
assessments2 <- assessments2 %>% mutate(TAXCLASS = ifelse(TAXCLASS %in% c(“2”, “2A”, “2B”, “2C”), “2”, TAXCLASS))
assessments2_2 <- assessments2 %>% filter(TAXCLASS == 2)

assessments1$TAXCLASS_original <- “1”
assessments2_1$TAXCLASS_original <- “1”

assessments <- bind_rows(assessments1, assessments2_1, assessments2_2)
fwrite(assessments, paste0(raw_dir, “joined_NY_assessments.csv”))

sales <- fread(paste0(raw_dir, “joined_NY_sales.csv”), colClasses = “character”) %>% mutate(YEAR = as.numeric(YEAR))
sales <- sales %>% filter(TAX_CLASS_AT_TIME_OF_SALE == 1)

assessments <- fread(paste0(raw_dir, “joined_NY_assessments.csv”), colClasses = “character”) %>% mutate(YEAR = as.numeric(YEAR))
assessments <- assessments %>% filter(TAXCLASS == 1)

joined <- assessments %>% full_join(sales, by = c(“BBLE” = “bble”, “YEAR”)) %>% distinct()
joined <- joined %>% select(-TAX_CLASS_AT_TIME_OF_SALE, -ADDRESS)
fwrite(joined, paste0(extract_dir, “classonefinal.csv”))

sales <- fread(paste0(raw_dir, “joined_NY_sales.csv”), colClasses = “character”) %>% mutate(YEAR = as.numeric(YEAR))
sales <- sales %>% filter(TAX_CLASS_AT_TIME_OF_SALE == 2) %>% filter(between(YEAR, 2010, 2018))

assessments <- fread(paste0(raw_dir, “joined_NY_assessments.csv”), colClasses = “character”) %>% mutate(YEAR = as.numeric(YEAR))
assessments <- assessments %>% filter(TAXCLASS == 2)

joined <- assessments %>% full_join(sales, by = c(“BBLE” = “bble”, “YEAR”)) %>% distinct()
joined <- joined %>% select(-TAX_CLASS_AT_TIME_OF_SALE, -ADDRESS)
fwrite(joined, paste0(extract_dir, “classtwofinal.csv”))

df <- fread(“extracts/classonefinal.csv”)

df <- df %>% mutate(SALE_YEAR = ifelse(!is.na(SALE_PRICE), YEAR, NA), TAX_YEAR = YEAR, ASSESSED_VALUE = AVTOT, MARKET_VALUE = FULLVAL)

df <- df %>% mutate_at(c(“ASSESSED_VALUE”, “SALE_PRICE”, “MARKET_VALUE”), as.numeric)

ratios <-cmfproperty::reformat_data(df,sale_col = “SALE_PRICE”,assessment_col = “ASSESSED_VALUE”,sale_year_col = “SALE_YEAR”)

cmfproperty::make_report(ratiosjurisdiction_name = “New York City”output_dir = your output directory”) 

Below is a pre-generated example report:

Report