2021 Annual Rental Facility Occupancy Survey Facilities includes the average reported rent by bedroom count for Montgomery County facilities.
rents <- read_csv("2021_Rental_Facility_Occupancy_Survey_Results.csv")
names(rents) <- str_to_lower(str_replace_all(names(rents), ' ','_'))
glimpse(rents)
## Rows: 1,550
## Columns: 11
## $ community_name <chr> "8513 Flower", "Central"~
## $ community_address <chr> "8513 FLOWER AVE TAKOMA ~
## $ bedroom_types <chr> "1 bedroom", "2 bedroom"~
## $ average_rent_2016 <chr> NA, NA, NA, NA, "$ 875~
## $ average_rent_2017 <chr> NA, NA, "$ 650", NA, "~
## $ average_rent_2018 <chr> NA, "$ 2,624", "$ 65~
## $ average_rent_2019 <chr> "$ 800", "$ 2,637", ~
## $ average_rent_2020 <chr> "$ 895", "$ 2,644", ~
## $ average_rent_2021 <chr> "$ 895", "$ 2,653", ~
## $ `percent_change_from_previous_year_2020-2021` <dbl> 0.0000, 0.0032, 0.0000, ~
## $ geo_location <chr> NA, NA, NA, NA, NA, NA, ~
cat('\nnrows = ', nrow(rents),
'\nunique addresses = ', length(unique(rents$community_address)),
'\nunique community names', length(unique(rents$community_name)))
##
## nrows = 1550
## unique addresses = 706
## unique community names 703
Filter the data for the 2021 rent information.
#Select the name, address, bedroom type, 2021 average rent and the geo-location fields
rents_2021 <- rents %>% select(starts_with(c('comm', 'bed')), average_rent_2021, geo_location)
#Create an integer bedroom count field from the first character of bedroom_type, assign 0 to efficiency
rents_2021$bedroom_count <- str_sub(rents_2021$bedroom_types, 1,1) %>% str_replace('e', '0') %>% as.integer()
#Convert the average rent field to an integer and sort the data by bedroom count and average rent
rents_2021$average_rent_2021 <- as.integer(gsub("[$,]", "", rents_2021$average_rent_2021))
rents_2021 <- arrange(rents_2021, bedroom_count, average_rent_2021)
head(rents_2021)
## # A tibble: 6 x 6
## community_name community_addre~ bedroom_types average_rent_20~ geo_location
## <chr> <chr> <chr> <int> <chr>
## 1 7133 Carroll Ave~ 7133 CARROLL AV~ efficiency 500 <NA>
## 2 7118 Carroll Ave~ 7118 CARROLL AV~ efficiency 560 <NA>
## 3 7126 Carroll Ave~ 7126 CARROLL AV~ efficiency 577 <NA>
## 4 1001 University ~ 1001 UNIVERSITY~ efficiency 586 <NA>
## 5 Cambridge Apartm~ 676 HOUSTON AVE~ efficiency 645 <NA>
## 6 8608 Flower Aven~ 8608 FLOWER AVE~ efficiency 647 <NA>
## # ... with 1 more variable: bedroom_count <int>
#Look at the summary data for 2021 rents
summary(rents_2021)
## community_name community_address bedroom_types average_rent_2021
## Length:1550 Length:1550 Length:1550 Min. : 155
## Class :character Class :character Class :character 1st Qu.: 1127
## Mode :character Mode :character Mode :character Median : 1422
## Mean : 1601
## 3rd Qu.: 1778
## Max. :13798
## geo_location bedroom_count
## Length:1550 Min. :0.000
## Class :character 1st Qu.:1.000
## Mode :character Median :2.000
## Mean :1.565
## 3rd Qu.:2.000
## Max. :4.000
rents_2021 %>% group_by(bedroom_count) %>% summarise(min(average_rent_2021), max(average_rent_2021))
## # A tibble: 5 x 3
## bedroom_count `min(average_rent_2021)` `max(average_rent_2021)`
## <int> <int> <int>
## 1 0 500 8211
## 2 1 166 11442
## 3 2 155 13798
## 4 3 600 6391
## 5 4 1138 6325
Look at the spread of rents by bedroom counts.
rp <- ggplot(rents_2021,aes(x=bedroom_count, y=average_rent_2021, fill = factor(bedroom_count))) +
geom_boxplot() +
scale_x_continuous(n.breaks = 6)
rp
There are large numbers of high outliers. The highest rents are for apartments at a senior living facility in Kensington that provides meal, assisted living and medical services. The lowest rents are less than $200 a month for 1 or 2 bedroom apartments in Gaithersburg and Takoma Park. These may be subsidized housing or data entry errors.
According to the Montgomery County Department of Housing and Community Affairs annual report Montgomery County currently has more than 20,000 households who are severely housing cost burdened; they earn less than $31,000/ year and spend more than half their income on rent. And more than 80 percent of renters earning up to $70,000/year are housing cost burdened. https://www.montgomerycountymd.gov/DHCA/Resources/Files/director/publications/dhca_annual_report-fy20-21.pdf
HUD defines housing cost burden as over 30% of income spent on housing. https://www.huduser.gov/portal/pdredge/pdr-edge-featd-article-081417.html
#Filter for rents that are <= to 30% of a 70k income
affordable <- (70000 * .3) / 12
affordable_rents <- rents_2021 %>%
filter(average_rent_2021 <= affordable)
#Look at the summary data for affordable rents
summary(affordable_rents)
## community_name community_address bedroom_types average_rent_2021
## Length:1150 Length:1150 Length:1150 Min. : 155
## Class :character Class :character Class :character 1st Qu.:1047
## Mode :character Mode :character Mode :character Median :1273
## Mean :1257
## 3rd Qu.:1494
## Max. :1750
## geo_location bedroom_count
## Length:1150 Min. :0.00
## Class :character 1st Qu.:1.00
## Mode :character Median :1.00
## Mean :1.37
## 3rd Qu.:2.00
## Max. :4.00
affordable_rents %>% group_by(bedroom_count) %>% summarise(min(average_rent_2021), max(average_rent_2021))
## # A tibble: 5 x 3
## bedroom_count `min(average_rent_2021)` `max(average_rent_2021)`
## <int> <int> <int>
## 1 0 500 1743
## 2 1 166 1749
## 3 2 155 1746
## 4 3 600 1750
## 5 4 1138 1719
affordable_pct <- round(nrow(affordable_rents) / nrow(rents_2021), 2) * 100
The affordable threshold for for an income of $70,000/year is 1750 a month. 74 percent of the rents in the dataset are affordable. But only shows where an apartment is available at a particular rent, not how many affordable apartments there are.
rp <- ggplot(affordable_rents,aes(x=bedroom_count, y=average_rent_2021,
fill=factor(bedroom_count))) +
geom_boxplot() +
scale_x_continuous(n.breaks = 6)
rp
geo_loc <- affordable_rents %>% filter(is.na(geo_location)) %>% distinct(community_name, community_address, geo_location)
filter(geo_loc, is.na(community_address))
## # A tibble: 2 x 3
## community_name community_address geo_location
## <chr> <chr> <chr>
## 1 Boulevard Of Chevy Chase <NA> <NA>
## 2 Charter House <NA> <NA>
There are 424 facilities missing geo-location information, the majority can be geo-coded using the address field. Two facilities have missing addresses. The address for Charter House is 1316 Fenwick Lane Silver Spring, MD 20910. An obvious address doesn’t show up in a search for Boulevard of Chevy Chase.
affordable_rents[affordable_rents$community_name == 'Charter House', "community_address"] <-
'1316 Fenwick Lane Silver Spring, MD 20910'
The Moderately Priced Dwelling Units (MPDU) data set gives an inventory of affordable housing by county planning area. https://data.montgomerycountymd.gov/Consumer-Housing/Moderately-Priced-Dwelling-Units/pbh5-g4e5.
mpdu_in <- read_csv("Moderately_Priced_Dwelling_Units.csv")
names(mpdu_in) <- str_to_lower(str_replace_all(names(mpdu_in), ' ','_'))
glimpse(mpdu_in)
## Rows: 608
## Columns: 6
## $ planning_area <chr> "PA34 - Fairland", "N/A", "PA25 - Travilah", "PA36 - ~
## $ total_units <dbl> 102, 325, 131, 135, 1680, 64, 52, 293, 68, 71, 94, 0,~
## $ total_mpdu <dbl> 16, 41, 17, 19, 266, 8, 42, 44, 9, 15, 29, 60, 266, 1~
## $ total_workforce <dbl> NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, N~
## $ rental_indicator <chr> "For-Sale & Rental", "For-Sale & Rental", "For-Sale &~
## $ offering_date <chr> NA, "05/01/2006", "09/01/2016", NA, "01/01/2012", "03~
The rental_indicator is the same value for all rows and the total_workforce field has only 14 rows with values. There separate rows for offering_date that result in duplicate data for total-units and total_mpdu.
# select fields and rows total_mpdu values > 0
mpdu <- mpdu_in %>% select(planning_area, total_units, total_mpdu, offering_date) %>%
filter(total_mpdu !=0 & !is.na(total_mpdu)) %>%
arrange(planning_area, desc(offering_date))
summary(mpdu)
## planning_area total_units total_mpdu offering_date
## Length:456 Min. : 0.0 Min. : 2.00 Length:456
## Class :character 1st Qu.: 74.0 1st Qu.: 13.00 Class :character
## Mode :character Median : 180.0 Median : 28.00 Mode :character
## Mean : 270.4 Mean : 41.09
## 3rd Qu.: 341.0 3rd Qu.: 55.00
## Max. :2703.0 Max. :405.00
## NA's :13
# select distinct rows to eliminate duplicates by offering date
mpdu <- mpdu %>% distinct(planning_area, total_units, total_mpdu, .keep_all = TRUE)
There are 20 distinct planning_area values in the data, one of which is N/A. 12 rows have N/A in total_units.
mpdu %>% count(total_units == 0 | is.na(total_units)) # How many rows have total_units = 0 or null?
## # A tibble: 2 x 2
## `total_units == 0 | is.na(total_units)` n
## <lgl> <int>
## 1 FALSE 271
## 2 TRUE 31
mpdu$pct_mpdu <- if_else(mpdu$total_units == 0,0, mpdu$total_mpdu / mpdu$total_units)
mpdu %>% group_by(planning_area) %>%
summarise(n=n(), total_units = sum(total_units, na.rm=TRUE), total_mpdu=sum(total_mpdu, na.rm = TRUE),
pct_mpdu = sum(pct_mpdu, na.rm = TRUE)) %>%
kable() %>% kable_minimal()
| planning_area | n | total_units | total_mpdu | pct_mpdu |
|---|---|---|---|---|
| N/A | 88 | 19628 | 3777 | 11.1000016 |
| PA11 - Damascus | 5 | 590 | 95 | 0.7688291 |
| PA13 - Clarksburg | 20 | 8518 | 1176 | 2.5632427 |
| PA19 - Germantown | 24 | 4885 | 827 | 2.8690427 |
| PA20 - Gaithersburg & Vicinity | 14 | 3529 | 538 | 1.9453898 |
| PA21 - Gaithersburg | 1 | 0 | 57 | 0.0000000 |
| PA22 - Upper Rock Creek | 3 | 299 | 48 | 0.4952863 |
| PA23 - Olney | 7 | 820 | 165 | 1.3017286 |
| PA24 - Darnestown | 2 | 73 | 10 | 0.2761905 |
| PA25 - Travilah | 3 | 724 | 96 | 0.4465518 |
| PA26 - Rockville | 2 | 80 | 30 | 0.3000000 |
| PA27 - Aspen Hill | 6 | 333 | 55 | 1.0070358 |
| PA28 - Cloverly | 5 | 243 | 35 | 0.7370137 |
| PA29 - Potomac | 8 | 1294 | 228 | 0.9446885 |
| PA30 - North Bethesda | 21 | 6712 | 857 | 2.5475778 |
| PA31 - Kensington/Wheaton | 16 | 3571 | 498 | 2.4036664 |
| PA33 - White Oak | 2 | 128 | 20 | 0.3327616 |
| PA34 - Fairland | 9 | 896 | 141 | 1.1244892 |
| PA35 - Bethesda/Chevy Chase | 35 | 6782 | 971 | 5.9775355 |
| PA36 - Silver Spring | 31 | 6930 | 1084 | 5.0635589 |
total_pct_mpdu = (sum(mpdu$total_mpdu, na.rm = TRUE) / sum(mpdu$total_units, na.rm = TRUE)) * 100
cat('\nTotal PCT MPDU = ', total_pct_mpdu)
##
## Total PCT MPDU = 16.21564
There are some obvious problems with the data, Rockville certainly has more than 80 rental units, and PA21 - Gaithersburg must have more than 0 total_units if there are 57 mpdu.