Comparing house prices, rents, other prices and incomes

HERE IS A GIF COMPARING house prices, rents, other prices and incomes:

fhfa hpi gif

And a static version with code.

In order for it to work, you’re going to need a data file with columns corresponding to the date and the data as described in the footnote (available from FHFA, BLS, and BEA). The data should be laid out like so, and I’ve saved them in a .xlsx file called data/fhfa hpi and income2.xlsx. Columns B through E correspond to the raw data, while columns F through I, with the .100 suffix are the data normalized so that January 1991 = 100.

fhfa data

Code and plot

library(tweenr)
library(ggplot2)
library(scales)
library(viridis)
library(animation)
library(dplyr)
library(readxl)
library(data.table)
library("tidyr")

#read in data:
mydata<-read_excel("data/fhfa hpi and income2.xlsx",sheet="data")
mydata$date<-as.Date(mydata$date, format="%m/%d/%Y")

# tidy up the data
mydata %>% gather("var","value",-date) %>%data.table() ->mydata2

#create labels for plot
ind.list<-unique(mydata2$var)[5:8]

mydata2[ , id:= ifelse(
  var == "hpi.100", "House Prices",
  ifelse( 
    var == "noshelter.100","Other Prices",
    ifelse( var== "percapita.100","Income",
            ifelse(var=="rent.100","Rent","other"))))
  ]

# Create caption
mycaption<- "@lenkiefer Source: House Prices: FHFA purchase-only house price index. Rent: U.S. Bureau of Labor Statistics (BLS) consumer price index rent of primary residence. Other Prices: BLS consumer price index all items less shelter. Income: U.S. Bureau of Economic Analysis per capita disposable personal income (Table 2.6.). All are seasonally adjusted."

# Wrap caption 120 characters:
mycaption <- paste0(strwrap(mycaption, 120), sep="", collapse="\n")
d.list<-unique(mydata2$date)
i<-length(d.list)
# Make plot:
ggplot(data=mydata2[var %in% ind.list ,],aes(x=date,y=value,color=id))+geom_line(size=0.95,alpha=0.82)+
  theme_minimal()+theme(legend.position="none")+scale_y_log10(limits=c(100,250),breaks=seq(100,250,50))+
  scale_x_date(limits=c(min(mydata$date),max(mydata$date)+150),date_labels="%Y",
               breaks=seq(as.Date("1990-01-01"),as.Date("2020-01-01"),"5 year"))+
  scale_color_viridis(discrete=T,option="C",end=0.85)+
  labs(y="Index: Jan, 1991 = 100, log scale",x="",
       title="Comparing house prices to rent, income and other prices",
       caption= mycaption)+theme(plot.caption=element_text(hjust=0,size=10))+
  geom_text(data=mydata2[var %in% ind.list & date==d.list[i],],aes(label=id,color=id),size=4,alpha=0.9)
## Warning: Removed 4 rows containing missing values (geom_path).
## Warning: Removed 4 rows containing missing values (geom_text).

Updated with another gif

Ratios

We can also add a gif comparing the ratios of house prices to other prices (Real House Price), Owner’s equivalent rent of primary residence (Price to Rent) and per capita disposable income (Price to Income).

In this chart instead of using general rents, we use the owner’s equivalent rent of the primary residece. See this FAQ from BLS on the differences in the rent series.

fhfa hpi gif

Add animation to graphs

To add the animation to the gifs we simply use the animation package and loop through the data. The code below creates the animated gifs.

# Make the first gif:
oopt = ani.options(interval = 0.05)
saveGIF({for (i in 1:N-1) {
  g<-
    ggplot(data=mydata2[var %in% ind.list & date<=d.list[i],],aes(x=date,y=value,color=id))+geom_line(size=0.95,alpha=0.82)+
    theme_minimal()+theme(legend.position="none")+scale_y_log10(limits=c(100,250),breaks=seq(100,250,50))+
    scale_x_date(limits=c(min(mydata$date),max(mydata$date)+150),date_labels="%Y",
                 breaks=seq(as.Date("1990-01-01"),as.Date("2020-01-01"),"5 year"))+
    scale_color_viridis(discrete=T,option="C",end=0.85)+
    labs(y="Index: Jan, 1991 = 100, log scale",x="",
         title="Comparing house prices to rent, income and other prices",
         caption= mycaption)+theme(plot.caption=element_text(hjust=0,size=10))+
    geom_text(data=mydata2[var %in% ind.list & date==d.list[i],],aes(label=id,color=id),size=4,alpha=0.9)
    print(g)
  print(i)
  ani.pause()
}
  
  for (i2 in 1:20) {
    print(g)
    print(i2)
    ani.pause()
  }
},movie.name="house prices vs other prices nov 2016.gif",ani.width = 650, ani.height = 450)

# Make the second gif (ratios)

#Adjust the caption for the second gif

mycaption2<- "@lenkiefer Source: House Prices: FHFA purchase-only house price index. Rent: U.S. Bureau of Labor Statistics (BLS) consumer price index owner's equivalent rent of primary residence. Other Prices: BLS consumer price index all items less shelter. Income: U.S. Bureau of Economic Analysis per capita disposable personal income (Table 2.6.). All are seasonally adjusted."

mycaption2 <- paste0(strwrap(mycaption2, 120), sep="", collapse="\n")

oopt = ani.options(interval = 0.05)
saveGIF({for (i in 1:(N-1)) {
  g<-
  ggplot(data=mydata[date<=d.list[i]],aes(x=date,y=hpi.100/oer.100))+
  geom_line(color=viridis(10)[4])+
  scale_y_continuous(limits=c(0.8,1.6),breaks=seq(0.8,1.6,.1))+
    #scale_x_date(limits=c(min(mydata$date),max(mydata$date)+150),date_labels="%Y",                 breaks=seq(as.Date("1990-01-01"),as.Date("2020-01-01"),"5 year"))+
  geom_line(aes(y=hpi.100/noshelter.100),color=viridis(10)[2])+
  geom_line(aes(y=hpi.100/percapita.100),color=viridis(10)[6])+
  annotate("text",size=3,x=d.list[i],y=mydata[date==d.list[i],]$hpi.100/mydata[date==d.list[i],]$oer.100,label="Price\nto\nRent",color=viridis(10)[4])+
  annotate("text",size=3,x=d.list[i],y=mydata[date==d.list[i],]$hpi.100/mydata[date==d.list[i],]$noshelter.100,label="Real\nPrice",color=viridis(10)[2])+
  annotate("text",size=3,x=d.list[i],y=mydata[date==d.list[i],]$hpi.100/mydata[date==d.list[i],]$percapita.100,label="Price\nto\nIncome",color=viridis(10)[6])+
  theme_minimal()+theme(plot.caption=element_text(hjust=0,size=11))+
  labs(subtitle="House prices relative to other prices, rents, and per capita disposable income",
       y="Index: Jan, 1991=1",x="",caption= mycaption2,title="House price ratios")
  print(g)
  print(i)
  ani.pause()
}

for (i2 in 1:20) {
  print(g)
  print(i2)
  ani.pause()
}
},movie.name="house price ratios vs other prices nov 2016.gif",ani.width = 650, ani.height = 450)

 Share!