/nyc-tax-inequity

mapping regressive housing policy

Primary LanguageTSQLGNU General Public License v3.0GPL-3.0

nyc-tax-inequity

This project is an attempt to visualize inequities in NYC's property tax system, primarily the regressive effect of capping annual property tax increases. These property tax caps, sold as a way of protecting fixed income homeowners in rapidly appreciating neighborhoods, instead overwhelmingly benefit already wealthy property owners.

https://papers.ssrn.com/sol3/papers.cfm?abstract_id=2377590
http://furmancenter.org/thestoop/entry/new-york-city-property-tax-reform
https://www.economist.com/finance-and-economics/2015/10/03/assessing-the-assessments

Explore the maps

3D map

poster [14MB pdf]
kepler.gl map [336MB html]
image only [8000 x 8000 PNG, 14KB])
geodata [509,818 row GeoJSON, 45MB compressed, 260MB uncompressed]

2D map

poster [37MB pdf]
kepler.gl map [430MB html]
image only [10000 x 10000 PNG, 34KB]
geodata [612,288 row GeoJSON, 60MB compressed, 320MB uncompressed]
Carto map

The interactive Carto map contains a superset of the columns used for the final map renderings for every class 1 building, so you can poke around and see where the numbers come from.

This is a 2D representation where the colors represent how many dollars the property tax cap saves the homeowner (which gets turned into the height dimension on the 3D map).

Hover over a BBL to see more metrics, which are explained below.

What am I looking at?

The data backing these maps is derived by combining John Krauss' scraped tax bills, which contains the estimated market value and actual taxes paid, with geodata from the NYC Department of City Planning's MapPluto 18v2.

We find the actual dollar value saved because of these tax caps for every class 1 property in 2017 by calculating what the homeowner would have paid in taxes if the assessed value were not capped and simply the estimated market value multiplied by the 6% assessment ratio for class 1 properties.

As an alternate view, we also calculate the property value you are effectively paying taxes on (the "effective market value").

For reference, this is a random Notice of Property Value from the NYC Department of Finance.

Here, you can see how taxes are calculated and how the 6% tax cap is applied.

Looking at this on the 2D map is not particularly revealing. The real impact comes from the 3D map, which plots the dollar value saved on the height dimension against market value decile on the color axis.

Here you can see that not only do higher value properties save more absolute dollars because of this policy (which is a rather trivial conclusion), but the massive disparity in how much they are saving.

Several properties benefit orders of magnitude more than most other properties, all these properties fall into the top 10 percent of most valuable homes (around $1 million+).

Background

NYC property tax law is regressive in a number of different ways, worsening (and in turn made worse by) the housing crisis.

Landlords receive huge tax breaks for (often non existent) renovations in the form of abatements and exemptions or by converting their buildings to co-ops or condos (which are heavily favored by the tax code).

Shiny new high rises and mansions are often massively undervalued by the city when there are no comparable buildings nearby, again subsidizing the richest developers and homeowners.

Residental rental buildings bear a disproportionately large percentage of the cities' tax burden, which is more or less passed completely on to tenants, while landlords capture the benefits of massively appreciating neighborhood property values.

We tackle these issues in the other parts of this project, and they are discussed at length in many other articles and studies.

Tax caps

For these maps, we narrowed the scope to one particularly inequitable part of NYC tax law: property tax caps.

Every year, the taxable value of an NYC apartment can increase a maximum of 6% (or 30% over five years). This means that if the building rapidly appreciates in value, the owner is shielded from the corresponding increase in property taxes through what is effectively a tax break from the government.

However, clearly, housing is not just a roof over our heads, it is a massively valuable investment vehicle.

The housing market in NYC is valued at over 1 trillion dollars, more or less because it has so much potential to appreciate and produce wealth for the exclusive few who enough capital to play.

So, in a situation where appreciation in the housing market massively benefits the richest of the rich, who are the main beneficiaries of this policy?

From tax bills to maps

All of these were run on Ubuntu 18.04 with PostgreSQL 10.9

Move into the directory of your choice (it can be anywhere, just change the relative paths accordingly):

$ mkdir rawdata
$ cd rawdata

Retrive the raw tax bills (~10.5 GB, 130M rows):

$ curl -O http://taxbills.nyc/rawdata.csv
$ xsv frequency rawdata.csv -s activityThrough
field,value,count
activityThrough,2017-06-02,28584244
activityThrough,2018-06-01,15532988
activityThrough,2015-06-05,15509053
activityThrough,2009-06-06,13890098
activityThrough,2016-06-03,11455436
activityThrough,2010-06-11,2429943
activityThrough,2009-11-20,2422271
activityThrough,2011-06-10,2416519
activityThrough,2011-11-18,2403325
activityThrough,2011-08-26,2333950

Create a Postgres table

psql -c "create database taxbills"

Make sure all your psql login settings work.

Insert the csv into a Postgres table:

$ psql -d taxbills -f ../postgres/load_data.sql

Filter only records from FY2017 since it has the most records, then pivot:

$ psql -d taxbills -f ../postgres/transform_data.sql

Save each borough as a separate CSV so they can be loaded into Carto's web interface:

$ psql -d taxbills -f ../postgres/export_data.sql

or, if you want to run it all at once:

$ ../postgres/run_all.sh

Download MapPLUTO data:

$ curl -o "#1mappluto.csv" https://common-data.carto.com/api/v2/sql?format=csv&q=select%20cartodb_id,%20the_geom,%20address,%20bbl,%20bldgarea,%20numbldgs,%20numfloors,%20ownername,%20unitsres,%20unitstotal,%20yearbuilt,%20zipcode%20from%20public.{mn,bx,bk,qn}mappluto

This command will download a separate csv for each borough, more amenable to Carto's API limits. Instead of directly linking to the data library in Carto, I use their common data API to select the relevant columns and reupload the smaller table.

There is a unified download for all 5 boroughs at https://common-data.carto.com/tables/nycpluto_all/public but it is too large to upload to import into a Carto student account, and also has a slightly different schema.

Download PUMA data:

$ curl -o puma.geojson https://data.cityofnewyork.us/api/geospatial/cwiz-gcty?method=export&format=GeoJSON

You can explore the resulting CSVs using your tool of choice (I find xsv, SQLite, or csvkit + postgres usually work well, in order of complexity / "power").

At this point, all of these lables were loaded into Carto's web interface for visual exploration, using the following query to get the main metrics.

SELECT
 *
FROM (
SELECT
    row_number() over() as cartodb_id,
    the_geom,
    the_geom_webmercator,
    bbl,
  	numbldgs,
    property_tax,
    market_value,
    property_tax_noexempt / .06 / (LEFT(rate, 7)::float / 100) as effective_market_value,
  	address,
  	ownername,
  	bldgarea,
  	rate,
  	market_value / GREATEST(numbldgs, 1) as market_value_per_building,
    to_char(100.0*property_tax_noexempt / market_value,'999D9999%') as effective_rate,
  	((.06 * market_value * LEFT(rate, 7)::float / 100) - property_tax_noexempt) as benefit,
    CASE
    	WHEN SUBSTRING(class, 2, 1) = '1' THEN (property_tax / market_value) / (.06 * LEFT(rate, 7)::float / 100)
    ELSE (property_tax / market_value) / (.45 * LEFT(rate, 7)::float / 100)
	END as div,
    SUBSTRING(class, 2) as class
FROM (
  
  SELECT
    pluto.the_geom as the_geom,
    pluto.the_geom_webmercator as the_geom_webmercator,
    pluto.bbl as bbl,
  	pluto.bldgarea as bldgarea,
  	pluto.numbldgs as numbldgs,
    pluto.address as address,
  	pluto.ownername as ownername,
  	tb.tax_before_exemptions_and_abatements as property_tax_noexempt,
  	tb.tax_before_abatements as property_tax_noabate,
    tb.annual_property_tax as property_tax,
    tb.estimated_market_value as market_value,
    tb.current_tax_rate as rate,
    tb.tax_class as class
  FROM mnmappluto as pluto
  INNER JOIN mntaxbills AS tb
    ON pluto.bbl = tb.bbl
  
  UNION ALL
  
  SELECT
    pluto.the_geom as the_geom,
    pluto.the_geom_webmercator as the_geom_webmercator,
    pluto.bbl as bbl,
  	pluto.bldgarea as bldgarea,
  	pluto.numbldgs as numbldgs,
    pluto.address as address,
  	pluto.ownername as ownername,
  	tb.tax_before_exemptions_and_abatements as property_tax_noexempt,
  	tb.tax_before_abatements as property_tax_noabate,
    tb.annual_property_tax as property_tax,
    tb.estimated_market_value as market_value,
    tb.current_tax_rate as rate,
    tb.tax_class as class
  FROM bxmappluto as pluto
  INNER JOIN bxtaxbills AS tb
    ON pluto.bbl = tb.bbl
  
  UNION ALL
  
  SELECT
    pluto.the_geom as the_geom,
    pluto.the_geom_webmercator as the_geom_webmercator,
    pluto.bbl as bbl,
  	pluto.bldgarea as bldgarea,
  	pluto.numbldgs as numbldgs,
    pluto.address as address,
  	pluto.ownername as ownername,
  	tb.tax_before_exemptions_and_abatements as property_tax_noexempt,
  	tb.tax_before_abatements as property_tax_noabate,
    tb.annual_property_tax as property_tax,
    tb.estimated_market_value as market_value,
    tb.current_tax_rate as rate,
    tb.tax_class as class
  FROM bkmappluto as pluto
  INNER JOIN bktaxbills AS tb
    ON pluto.bbl = tb.bbl
  
  UNION ALL
  
  SELECT
    pluto.the_geom as the_geom,
    pluto.the_geom_webmercator as the_geom_webmercator,
    pluto.bbl as bbl,
  	pluto.bldgarea as bldgarea,
  	pluto.numbldgs as numbldgs,
    pluto.address as address,
  	pluto.ownername as ownername,
  	tb.tax_before_exemptions_and_abatements as property_tax_noexempt,
  	tb.tax_before_abatements as property_tax_noabate,
    tb.annual_property_tax as property_tax,
    tb.estimated_market_value as market_value,
    tb.current_tax_rate as rate,
    tb.tax_class as class
  FROM bkmappluto as pluto
  INNER JOIN bktaxbills AS tb
    ON pluto.bbl = tb.bbl
  
  UNION ALL
  
  SELECT
    pluto.the_geom as the_geom,
    pluto.the_geom_webmercator as the_geom_webmercator,
    pluto.bbl as bbl,
  	pluto.bldgarea as bldgarea,
  	pluto.numbldgs as numbldgs,
    pluto.address as address,
  	pluto.ownername as ownername,
  	tb.tax_before_exemptions_and_abatements as property_tax_noexempt,
  	tb.tax_before_abatements as property_tax_noabate,
    tb.annual_property_tax as property_tax,
    tb.estimated_market_value as market_value,
    tb.current_tax_rate as rate,
    tb.tax_class as class
  FROM qnmappluto as pluto
  INNER JOIN qntaxbills AS tb
    ON pluto.bbl = tb.bbl

) as fulldata
WHERE property_tax > 0
  AND market_value > 0
  AND bldgarea > 0
  AND numbldgs > 0
) t
WHERE div <= 1
	AND benefit > 0
    AND class = '1 - small home, less than 4 families'

The gentrifying neighborhoods are from a Furman Center report.

"High value" neighborhoods:

SELECT *
FROM puma
WHERE puma::int
  IN (
    3810,
    4004,
    3807,
    4005,
    3806,
    4109
  )

"Gentrifying" neighborhoods:

SELECT *
FROM puma
WHERE puma::int
  IN (
    4001,
    3803,
    3809,
    4002,
    3804,
    3802,
    4003,
    4006,
    3801,
    3710,
    4101,
    4012,
    3705,
    4007,
    4011
  )

For the 3D rendering, the resultant table was exported as a GeoJSON and dumped into kepler.gl, with a solid black Mapbox basemap.

The output is rendered as a 10k x 10k resolution PNG.