library(rental)
library(cancensus)
library(tidyverse)
library(sf)

Listings Overview

As an example we read the August data for unfurnished listings for Vancouver, Calgary and Toronto.

region_names=c("Vancouver","Calgary","Toronto")
regions=list_census_regions('CA16', use_cache = TRUE) %>% filter(level=="CSD",name %in% region_names)
geo=get_census(dataset = 'CA16',regions=as_census_region_list(regions),geo_format='sf',level="Regions")

start_time="2019-01-01"
end_time="2019-02-01"

for (i in 1:nrow(geo)) {
  row=geo[i,]
  ls <- get_listings(start_time,end_time,row$geometry)
  summary=ls %>% as.data.frame %>% select("price","beds","size","furnished") %>% 
    filter(!is.na(size),size>0) %>%
    group_by(beds,furnished) %>% 
    summarize(median=paste0("$",format(median(price),big.mark=",")),median_ppsf=paste0("$",round(median(price/size, na.rm=TRUE),2),"/sf"), count=n()) %>% mutate(name=row$name)
  print(row$name)
  print(summary)
  #print(paste0(row$name," median price: $",format(median(ls$price),big.mark=",")))
}

#ls %>% filter(beds=="1")
g <- geo %>% filter(name=="Vancouver (CY)")

get_rental_summary <- function(g,start_time,end_time) {
   ls <- get_listings(start_time,end_time,g$geometry)
  summary=ls %>% as.data.frame %>% select("price","beds","size","furnished") %>% 
    filter(!is.na(size),size>0) %>%
    group_by(beds,furnished) %>% 
    summarize(median=median(price),median_ppsf=median(price/size, na.rm=TRUE), count=n())
}

start_time="2018-01-01"
end_time="2019-04-20"

plot_data <- get_rental_summary(g,"2019-03-20","2019-04-20") %>%
  mutate(Year="Jan 2019") %>%
  bind_rows(
    get_rental_summary(g,"2018-03-20","2018-04-20") %>%
  mutate(Year="Jan 2018")
  ) %>%
  filter(beds %in% c(1,2,3)) %>%
  group_by(furnished,beds) %>%
  select(median_ppsf,Year) %>%
  spread(key=Year,value=median_ppsf) %>%
  mutate(change=scales::percent(`Jan 2019`/`Jan 2018`-1)) %>%
  mutate_at(c("Jan 2019","Jan 2018"),function(x)paste0("$",round(x,2),"/sf"))



 plot_data %>% knitr::kable()

Vancouver over time

region_name="Vancouver"
regions=list_census_regions('CA16', use_cache = TRUE) %>% filter(level=="CSD",name == region_name)
geo=get_census(dataset = 'CA16',regions=as_census_region_list(regions),geo_format='sf',level="Regions")

start_time="2017-05-01"
end_time="2019-04-20"


ls <- get_listings(start_time,end_time,geo$geometry,filter = 'unfurnished',include_title = FALSE)
summary=ls %>% as.data.frame %>% select("price","beds") %>% group_by(beds) %>% summarize(median=paste0("$",format(median(price),big.mark=",")), count=n()) %>% mutate(name=region_name)
plot_data <- ls %>% mutate(year_month=strftime(post_date,"%Y-%m")) %>%
  mutate(beds=case_when(as.integer(beds)<=3 ~ beds,TRUE ~ "4+")) %>%
  filter(beds!="4+") %>%
  mutate(ppsf=price/size) %>%
  filter(!is.na(ppsf))
ggplot(plot_data,
       aes(x=year_month, y=ppsf)) +
  #geom_point(size=0.01,alpha=0.2) +
  geom_boxplot(outlier.shape = NA) +
    #scale_color_discrete(name="Bedrooms") +
  theme_bw() +
  scale_y_continuous(labels=function(d)paste0("$",round(d,1),"/sf"),limits = c(0,6)) +
  facet_wrap("beds") +
  theme(axis.text.x = element_text(angle = 60, hjust = 1)) +
  labs(title=paste0("City of Vancouver unfurnished listings by number of bedrooms"),x="Date",y="Asking Rent per sf")

#ggplot(nbhds, aes(fill=NAME)) +geom_sf()
plot_data %>% 
  st_set_geometry(NULL) %>%
  group_by(year_month,beds) %>%
  summarize_at(c("price","ppsf"),median,na.rm=TRUE) %>%
  filter(year_month %in% c("2018-03","2019-03")) %>%
  mutate(year=substr(year_month,1,4) %>% as.integer,
         month=substr(year_month,6,7)) %>%
  left_join((.) %>% mutate(year=year+1) %>% select(year,month,beds,price,ppsf),by=c("year","month","beds")) %>%
  mutate(change=price.x/price.y-1,
         changesf=ppsf.x/ppsf.y-1)

Fold in nbhd data

ls<-st_as_sf(ls) %>%
  st_join(
    st_read(file.path(getOption("custom_data_path"),"local_area_boundary_shp/local_area_boundary.shp")) %>% 
      st_transform(4326))
ls %>% filter(NAME %in% c("Downtown","West End")) %>%
  group_by(furnished,beds) %>%
  summarize(price=median(price))
ggplot(ls %>% 
         filter(as.integer(beds)<3, as.integer(beds)>0, price<6000, price>500),
       aes(x=post_date, y=price, color=beds,group=beds)) +
  #geom_point(size=0.01,alpha=0.2) +
  geom_smooth(method = "loess", se = FALSE) +
    scale_color_discrete(name="Bedrooms") +
    theme_bw() +
  facet_wrap("NAME") +
    labs(title=paste0("Listings (unfurnished) ",format(nrow(ls))),x="Date",y="Asking Rent")

#ggplot(nbhds, aes(fill=NAME)) +geom_sf()
ggplot(ls %>% 
         group_by(NAME) %>%
         filter(n()>5000) %>%
         ungroup %>%
         mutate(ppsf=price/size),
       aes(x=post_date, y=ppsf)) +
  #geom_point(size=0.01,alpha=0.2) +
  geom_smooth(method = "loess", se = FALSE) +
    #scale_color_discrete(name="Bedrooms") +
    theme_bw() +
  facet_wrap("NAME") +
    labs(title=paste0("Listings (unfurnished) ",format(nrow(ls))),x="Date",y="Asking Rent per sf")

#ggplot(nbhds, aes(fill=NAME)) +geom_sf()
area="Renfrew-Collingwood"
plot_data <- ls %>% filter(NAME==area,as.integer(beds)<3, price<5000) %>% mutate(beds=ifelse(as.integer(beds)>=5,"5+",beds))
  ggplot(plot_data, aes(x=post_date, y=price, color=beds,group=beds)) + 
  geom_point(size=0.01,alpha=0.2) +
  geom_smooth(method = "loess", se = FALSE) +
    scale_color_discrete(name="Bedrooms") +
    theme_bw() +
    labs(title=paste0(area," Listings (unfurnished) ",format(nrow(plot_data))),x="Date",y="Asking Rent")
plot_data <- ls %>% filter(as.integer(beds)<3,  price<6000, price>500) %>% mutate(ppsf=price/size) %>% filter(!is.na(ppsf) & ppsf<7.5)
  ggplot(plot_data, aes(x=post_date, y=ppsf, color=beds,group=beds)) + 
  geom_point(size=0.01,alpha=0.2) +
  geom_smooth(method = "loess", se = FALSE) +
    theme_bw()
plot_data <- ls %>% filter(as.integer(beds)<=2) %>% mutate(beds=ifelse(as.integer(beds)>=5,"5+",beds),
                           type=paste0(beds, ifelse(downtown," downtown","")))
# totals <- plot_data %>%
#   group_by(downtown,beds) %>% summarize(price,sub)
  ggplot(plot_data, aes(x=post_date, y=price, color=type,group=type)) + 
  geom_point(size=0.01,alpha=0.2) +
    scale_color_brewer(palette = "Paired") +
  geom_smooth(method = "loess", se = FALSE) +
    theme_bw()

Map

bg_color="#c0c0c0"
theme_opts<-list(theme(panel.grid.minor = element_blank(),
                       #panel.grid.major = element_blank(), #bug, not working
                       panel.grid.major = element_line(colour = bg_color),
                       panel.background = element_rect(fill = bg_color, colour = NA),
                       plot.background = element_rect(fill=bg_color, size=1,linetype="solid"),
                       axis.line = element_blank(),
                       axis.text.x = element_blank(),
                       axis.text.y = element_blank(),
                       axis.ticks = element_blank(),
                       axis.title.x = element_blank(),
                       axis.title.y = element_blank()))
library(sf)
library(ggplot2)

geo=get_census(dataset = 'CA16',regions=list(CMA="59933"),geo_format='sf',level="Regions")

ls <- get_listings(start_time,end_time,geo$geometry,beds=c(1),filter = 'unfurnished')
  summary=ls %>% as.data.frame %>% select("price","beds") %>% group_by(beds) %>% summarize(median=paste0("$",format(median(price),big.mark=","))) %>% mutate(name=row$name)

cts=get_census(dataset = 'CA16',regions=list(CMA="59933"),geo_format='sf',level="CSD")

min_listings=10

median_rent <- function(v){
  result <- ifelse(length(v)>min_listings, median(v),NA)
  return(result)
}

aggregate_listings <- aggregate(cts %>% select("GeoUID"),ls,function(x){x})

data <- aggregate(ls %>% select("price"),cts,median_rent)


cutoffs=as.integer(quantile(data$price, probs=seq(0,1,0.1), na.rm=TRUE))
labels=factor(as.character(seq(1,length(cutoffs)-1) %>% lapply(function(i){return(paste0(cutoffs[i]," - ",cutoffs[i+1]))})),order=TRUE)
colors=setNames(RColorBrewer::brewer.pal(length(labels),"RdYlBu"),labels)
data$discrete_price= data$price %>% cut(breaks=cutoffs, labels=labels)


ggplot() +
  geom_sf(data=cts, fill="#808080", size=0.1) +
  geom_sf(data=data, aes(fill = discrete_price), size=0.1) +
  scale_fill_brewer(palette='RdYlBu', direction=-1, na.value="#808080",name="Median Price") +
  labs(title="August Studio and 1 Bedroom Unfurnished Median Ask") +
  theme_opts

Rent distributions by municipality

library(ggbeeswarm)
library(gridExtra)

regions=as_census_region_list(search_census_regions("Vancouver",'CA16','CMA'))

geo=get_census(dataset = 'CA16',regions=regions,geo_format='sf',level="Regions")

ls <- get_listings(start_time,end_time,geo$geometry,beds=c(1),filter = 'unfurnished',sanity=c(400,4000))
  summary=ls %>% as.data.frame %>% select("price","beds") %>% group_by(beds) %>% summarize(median=paste0("$",format(median(price),big.mark=","))) %>% mutate(name=row$name)


geos=get_census(dataset = 'CA16',regions=regions,geo_format='sf',level="CSD") %>%
  st_join(ls) 

top_munis <- geos %>% group_by(name) %>% summarize(count=length(name)) %>% 
  top_n(5,count) %>% pull("name")

plot_data <- geos %>% filter(name %in% top_munis) %>%
  rename(Municipality=name)
title="Distribution of Unfurnished 1br Rents, September 2017"
p1 <- ggplot(plot_data) + 
  geom_density(aes(x=price, color=Municipality)) +
  labs(title=title)
p2 <- ggplot(plot_data, aes(Municipality, price))+ 
  geom_violin(aes(fill=Municipality )) + 
  #geom_beeswarm(pch = 1, col='white', cex=0.8, alpha=0.6) +
  labs(title=title)
grid.arrange(p1, p2, ncol=1)

Looking into Coquitlam

region_name="Coquitlam" #"Richmond"
regions=as_census_region_list(search_census_regions(region_name,'CA16','CSD') %>% filter(name==region_name))

geo=get_census(dataset = 'CA16',regions=regions,geo_format='sf',level="Regions")

ls <- get_listings(start_time,end_time,geo$geometry,beds=c(1),filter = 'unfurnished',sanity=c(400,4000))
summary=ls %>% as.data.frame %>% 
  select("price","beds") %>% 
  group_by(beds) %>%
  summarize(median=paste0("$",format(median(price),big.mark=","))) %>% 
  mutate(name=row$name)

cutoffs=c(400,1350,4000)
labels=factor(as.character(seq(1,length(cutoffs)-1) %>% lapply(function(i){return(paste0(cutoffs[i]," - ",cutoffs[i+1]))})),order=TRUE)
colors=setNames(c("turquoise","purple"),labels)
ls$discrete_price= ls$price %>% cut(breaks=cutoffs, labels=labels)

#ls <- cbind(ls,st_coordinates(st_transform(ls,102002)$location))
ls <- cbind(ls,st_coordinates(ls$location))

library(ggmap)
base <- get_map(paste0(region_name,", Canada"), zoom=12, source = "stamen", maptype = "toner", 
    crop = T)

#ggplot() +
  ggmap(base) +
  #geom_sf(data=geo, fill="#808080", size=0.1) +
  #coord_sf(crs=st_crs(102002)) +
  geom_point(data=ls , aes(color = discrete_price, x=X, y=Y), shape=21, size=2) +
  scale_fill_manual(palette=colors) +
  labs(title="August Studio and 1 Bedroom Unfurnished Median Ask",color="Price") +
  theme_opts

Rent distributions over time

region_name="Vancouver" 
regions=as_census_region_list(search_census_regions(region_name,'CA16','CSD') %>% filter(name==region_name))

geo=get_census(dataset = 'CA16',regions=regions,geo_format='sf',level="Regions")

ls <- get_listings(start_time,end_time,geo$geometry,beds=c(1),filter = 'unfurnished',sanity=c(400,4000))

ls$year_month <- factor(substr(ls$post_date,0,7),ordered = TRUE)
#ls$year_month_day <- factor(substr(ls$post_date,0,10),ordered = TRUE)

#ls %>% group_by(year_month) %>% summarize(count=length(year_month)) %>% as.data.frame %>% select("year_month","count")
#ls %>% group_by(year_month_day) %>% summarize(count=length(year_month_day)) %>% as.data.frame %>% select("year_month_day","count")


plot_data <- ls %>% as.data.frame %>% select("price","year_month")
title="Distribution of Unfurnished 1br Rents, City of Vancouver"
p1 <- ggplot(plot_data) + 
  geom_density(aes(x=price, color=year_month)) +
  labs(title=title, color="Year-Month")
p2 <- ggplot(plot_data, aes(year_month, price))+ 
  geom_violin(aes(fill=year_month )) + 
  #geom_beeswarm(pch = 1, col='white', cex=0.8, alpha=0.6) +
  labs(title=title, fill="Year-Month", x="Year-Month")
grid.arrange(p1, p2, ncol=1)

Rent distributions by municipality

library(ggbeeswarm)
library(gridExtra)

region_names=c("Vancouver","Toronto","Victoria","Calgary")
regions= as_census_region_list(do.call(rbind,lapply(region_names,function(region_name){return((search_census_regions(region_name,'CA16','CSD') %>% filter(name==region_name)))})))

geo=get_census(dataset = 'CA16',regions=regions,geo_format='sf',level="Regions")

geometry=st_union(geo$geometry)

beds=2
ls <- get_listings(start_time,end_time,geometry,beds=c(beds),filter = 'unfurnished',sanity=c(400,5000))
  summary=ls %>% as.data.frame %>% select("price","beds") %>% group_by(beds) %>% summarize(median=paste0("$",format(median(price),big.mark=","))) %>% mutate(name=row$name)


geos=get_census(dataset = 'CA16',regions=regions,geo_format='sf',level="CSD") %>%
  st_join(ls) 


plot_data <- geos %>% as.data.frame %>% select("name","price") %>%
  rename(Municipality=name)
title=paste0("Distribution of Unfurnished ",beds,"br Rents, August 2017")
p1 <- ggplot(plot_data) + 
  geom_density(aes(x=price, color=Municipality)) +
  labs(title=title)
p2 <- ggplot(plot_data, aes(Municipality, price))+ 
  geom_violin(aes(fill=Municipality )) + 
  #geom_beeswarm(pch = 1, col='white', cex=0.8, alpha=0.6) +
  labs(title=title)
grid.arrange(p1, p2, ncol=1)

Checking Specific area

#geo=sf::read_sf("../data/custom_region.geojson")
geo=sf::read_sf("../data/victoria_stainsbury.geojson")



ls <- get_listings("2017-06-01","2017-09-01",geo$geometry,filter = 'unfurnished')
  summary=ls %>% as.data.frame %>% select("price","beds") %>% group_by(beds) %>% summarize(median=paste0("$",format(median(price),big.mark=","))) %>% mutate(name=row$name)

  ggplot(ls, aes(beds, price))+ 
  geom_violin(aes(fill=beds )) + 
  #geom_beeswarm(pch = 1, col='white', cex=0.8, alpha=0.6) +
  labs(title="June-August 1br unfurnished Custom Region")
cutoffs=c(400,1050,4000)
labels=factor(as.character(seq(1,length(cutoffs)-1) %>% lapply(function(i){return(paste0(cutoffs[i]," - ",cutoffs[i+1]))})),order=TRUE)
colors=setNames(c("turquoise","purple"),labels)
ls$discrete_price= ls$price %>% cut(breaks=cutoffs, labels=labels)
ls <- cbind(ls,st_coordinates(ls$location))

base <- get_map(location=c(-122.84594535827637, 49.18422801616818), zoom=15, source = "stamen", maptype = "toner", 
    crop = T)

#ggplot() +
  ggmap(base) +
  #geom_sf(data=geo, fill="#808080", size=0.1) +
  #coord_sf(crs=st_crs(102002)) +
  geom_point(data=ls , aes(color = discrete_price, x=X, y=Y), shape=21, size=4) +
  scale_fill_manual(palette=colors) +
  geom_polygon(data= fortify(as(geo,"Spatial")), aes(x=long, y=lat), fill=NA, size=0.5,color='blue') +
  labs(title="August 1 Bedroom Unfurnished Median Ask",color="Price") +
  theme_opts
my_theme <- list(
  theme_minimal(),
  theme(axis.text = element_blank(), 
        axis.title = element_blank(), 
        axis.ticks = element_blank(),
        panel.grid.major = element_line(colour = "white"), 
        panel.grid.minor = element_line(colour = "white"),
        panel.background = element_blank(), 
        axis.line = element_blank())
)

Vancouver rent map

library(tidyverse)
library(cancensus)
library(rental)
library(sf)
regions=list_census_regions('CA16', use_cache = TRUE) %>% filter(level=="CMA",name == "Vancouver")
geo=get_census(dataset = 'CA16',regions=as_census_region_list(regions),geo_format='sf',level="CT")

start_time="2017-09-01"
end_time="2017-12-07"

ppsf_formatter <- function(x){return(paste0("$",round(x,2),"/sf"))}

ls <- get_listings(start_time,end_time,st_union(geo$geometry),beds=c('0','1','2'),filter = 'unfurnished') %>%
  mutate(ppsf=price/size)

geo_listings <- st_join(geo, filter(ls,!is.na(ppsf))) %>% 
  group_by(GeoUID) %>% 
  summarize(ppsf=median(ppsf),count=n())

ggplot(geo_listings %>%  mutate(ppsf=ifelse(count>=5,ppsf,NA)) %>% st_as_sf) +
  geom_sf(aes(fill=ppsf),size=NA) +
  scale_fill_viridis_c(name="Asking Rent/sf", option="magma") +
  labs(title="October 1 through Dec 7 Asking Rent/sf") +
  my_theme


mountainMath/rental-listings documentation built on June 5, 2019, 5:10 a.m.