R/zillow.R

Defines functions toCommonDF propertyDetails plot.ZillowComparables compToDataFrame getComps checkStatus

Documented in getComps

zestimate =
  #
  # v = zestimate("1292 Monterey Avenue", "94707", zillowId)
  #
  #
function(address, citystatezip, zillowId = getOption("ZillowId", stop("need zillow id")), ...)  
{
  reply = getForm("http://www.zillow.com/webservice/GetSearchResults.htm",
                  'zws-id' = zillowId, 
                  address = address,
                  citystatezip = citystatezip,
                  ...)

  doc = xmlParse(reply, asText = TRUE)

  checkStatus(doc)
  
  zpid = xmlValue(doc[["//result/zpid"]])
  est = doc[["//result/zestimate"]]  
  data.frame(amount = as.numeric(xmlValue(est[["amount"]])),
             low = as.numeric(xmlValue(est[["valuationRange"]][["low"]])),
             high = as.numeric(xmlValue(est[["valuationRange"]][["high"]])),
             valueChange30Day = as.numeric(xmlValue(est[["valueChange"]])),             
             row.names = zpid )
}

checkStatus =
function(doc)
{
  msg = doc[["//message"]]
  code = xmlValue(msg[["code"]])

  if(!is.null(msg[["limit-warning"]]))
    warning("reaching the call limit of your Zillow Id")
  
  if(code == "0")
    return(TRUE)

  i = match(code, zillowErrorTable[, 1])
  if(is.na(i)) {
     e = simpleError(gsub("^Error: ", "", xmlValue(msg[["text"]])))
     class(e) = c("ZillowError", class(e))
  } else {    
     e = simpleError(zillowErrorTable[i, 3])
     class(e) = c(zillowErrorTable[i, 2], class(e))
  }
  stop(e)
}

getComps =
function(id, zillowId = getOption("ZillowId", stop("need zillow id")), count = 25, ...)
{
     # if the caller gives us an address rather than a zpid, find the zpid.
  if(!is.numeric(id) && length(grep("[^[:digit:]]", id))) {
    id = rownames(zestimate(id[1], id[2], zillowId))[1]
  }

  txt = getForm("http://www.zillow.com/webservice/GetDeepComps.htm",
                 zpid = id, 'zws-id' = zillowId,  count = count, ...)

  doc = xmlParse(txt, asText = TRUE)

  checkStatus(doc)
  
  comps = doc["//response//comparables[.//links]"][[1]]

  principal = doc[["//response/properties/principal"]]
  ans = compToDataFrame(principal)  
  ans = rbind(ans, do.call("rbind",  c(xmlApply(comps, compToDataFrame))))
  rownames(ans) = c(xmlValue(principal[["zpid"]]), xmlSApply(comps, function(x) xmlValue(x[["zpid"]])))

  class(ans$lastSold) = c("POSIXt", "POSIXct")
  class(ans) = c("ZillowComparables", class(ans))
  
  ans
}

compToDataFrame =
function(node, deep = TRUE)
{
  est = node[["zestimate"]]
  add = xmlToList(node[["address"]])
  if(length(add$latitude) == 0)
    add$latitude = NA
  if(length(add$longitude) == 0)
    add$longitude = NA  
     
  ans =
   data.frame(amount = as.numeric(xmlValue(est[["amount"]])),
             low = as.numeric(xmlValue(est[["valuationRange"]][["low"]])),
             high = as.numeric(xmlValue(est[["valuationRange"]][["high"]])),
             valueChange30Day = as.numeric(xmlValue(est[["valueChange"]])),
             street = add[["street"]],
             zip = add$zipcode,
             latitude = as.numeric(add$latitude),
             longitude = as.numeric(add$longitude),             
             row.names = xmlValue(node[["zpid"]] ))
  if(deep) {
    sapply(c("taxAssessmentYear", "taxAssessment", "yearBuilt", "lotSizeSqFt", "finishedSqFt",
              "bathrooms", "bedrooms", "lastSoldPrice"),
            function(id) {
                   ans[1, id] <<- as.numeric(if(!is.null(node[[id]])) xmlValue(node[[id]]) else NA)
            })
    
    ans[1, "lastSold"] = if(!is.null(node[["lastSoldDate"]]))
                            as.POSIXct(strptime(xmlValue(node[["lastSoldDate"]]), "%m/%d/%Y"))
                         else
                            NA
  }
  ans[1,"score"] =  xmlGetAttr(node, "score", NA, as.numeric)

  ans
}
          


plot.ZillowComparables =
function(x, threshold = NA, ...)
{
  opts = par(no.readonly = TRUE)
#  i = match(c("cin", "cra", "csi", "cxy", "din"), names(opts))
  on.exit(par(opts))
#  par(mfrow = c(1, 1))
  par(mfrow = c(1, 2))

#  boxplot(v$amount, horizontal = TRUE)
#  points(v$amount[1], 1, col = "red")

  bedrooms = ordered(x$bedrooms)
  bathrooms = ordered(x$bathrooms)  
  plot(x$finishedSqFt, x$amount, col = rainbow(length(levels(bathrooms)))[bathrooms],
        ylim = c(min(x$low), max(x$high)),
        xlab = "Sq Ft.", ylab = "Price")
  text(x$finishedSqFt, x$amount, as.character(bedrooms))
  x$score[1] = 1.0
  score = cut(x$score, 6)
  cols = rainbow(length(levels(score)))
  sapply(seq(length = nrow(x)),
          function(i) lines(rep(x$finishedSqFt[i], 2), c(x$low[i], x$high[i]),
                            col = if(i == 1) "blue" else cols[score[i]],
                            lwd = if(i == 1) 2 else 1))


  if(!is.na(threshold))
    abline(h = threshold, col = "yellow", lty = 2)

  plot(amount/finishedSqFt ~ lotSizeSqFt, x,
            col = c("red", rep("blue", nrow(x) - 1)))
  
  invisible(x)
}



propertyDetails =
function(zpid, zillowId = getOption("ZillowId", stop("need zillow id")), asDataFrame = length(zpid) > 1, ...)
{
    if(length(zpid) > 1) {
        tmp = lapply(zpid, propertyDetails, zillowId, ...)
        return(toCommonDF(lapply(tmp, function(x) x$editedFacts)))
    }
    
  if(grepl("[a-z]", zpid) && length(zpid) > 1) 
     zpid = rownames(zestimate(zpid[1], zpid[2], zilowId = zillowId, ...))
    
  txt = getForm("http://www.zillow.com/webservice/GetUpdatedPropertyDetails.htm", 'zws-id' = zillowId, zpid = zpid, ...)
  doc = xmlParse(txt, asText = TRUE)
  code = xpathSApply(doc, "//message/code", xmlValue)
  if(as.integer(code)/500 >= 1) {
      e = simpleError(gsub("Error: ", "", xpathSApply(doc, "//message/text", xmlValue)))
      class(e) = c(switch(code,
                     "500" = "ZillowIDError",
                     "501" = "ZillowProtectedDataError",
                     "502" = "ZillowNoPropertyDataError"),
                     "ZillowError", class(e))
      stop(e)
  }
  els = getNodeSet(doc, "//response")
  xmlToList(els[[1]])
}




toCommonDF =
function(vals)
{
    vals = vals[!sapply(vals, is.null)]
    vars = sapply(vals, function(x) names(x)[sapply(x, length) == 1])
    vars = unique(unlist(vars))
    tmp = lapply(vals, function(x) {
        v = structure(rep(NA, length(vars)), names = vars)
        w = names(x) %in% vars
                          v[names(x)[w]] = x[w]
                          as.data.frame(v, stringsAsFactors = FALSE)
    })
    
    ans = do.call(rbind, tmp)

    ans[] = lapply(ans, type.convert)
    ans
}
duncantl/Zillow documentation built on Nov. 23, 2023, 4:29 p.m.