to_install <- c("sf","sp","rgdal","spatstat","ggmap","maptools")
install.packages(to_install)
lapply(to_install,library,character.only=TRUE)

library(tidyverse)
library(lubridate)
tenders <- read_csv("tabula-tender-bids-from-mar-2012-to-jan-2017.csv",col_names=c("centre","stall","area","trade","bid","month"))

First, some cleaning.

tenders %>%
  drop_na(centre) %>%
  filter(row_number() != 1135) %>%
  mutate(type=if_else(row_number()<1135,
         "cooked","lockup")) %>%
  mutate(bidNum = as.numeric(str_replace_all(bid,pattern="\\$|,",replacement = ""))) %>%
  mutate(date=dmy(paste0("01-",month))) %>%
  mutate(priceM2=bidNum/area)  %>% #-> intermediate
  drop_na(bidNum) -> tenders2;
NAs introduced by coercion 50 failed to parse.
#intermediate[is.na(intermediate$bidNum),] #50 entries have a date in the bid column
tenders2 %>%
  group_by(trade) %>%
  ggplot(aes(trade,priceM2)) + 
  geom_point(alpha=0.1,size=1.5,shape=3) +
  theme(axis.text.x = element_text(angle=45,hjust=1)) ;

Here we see the distribution of price per square meter for different food types.

tenders.sp %>%
  group_by(centre,lon,lat) %>%
  summarise(total_price_M2=sum(bidNum)/sum(area)) %>%
  ggplot() +
  geom_point(aes(lon,lat,size=total_price_M2),alpha=0.4) +
  geom_path(data=fortify(sg),aes(long,lat,group=group))
Regions defined for each Polygons

By summing the bid price for each stall and finding the average price per meter square for each hawker centre, we can see which land areas cost more for hawkers.

tenders2 %>%
  group_by(centre) %>%
  summarise(price=mean(priceM2))
tenders2 %>%
  filter(type=='cooked') %>%
  group_by(centre) %>%
  summarise(price=mean(priceM2))
# tenders2 %>%
#   group_by(centre) %>%
#   summarise(bids=n()) %>%
#   ggplot(aes(centre,bids)) + geom_bar(stat="identity")
library(sf)
centres <- st_read("hawker-centres-kml.kml")
Reading layer `HAWKERCENTRE' from data source `/Users/blossomtang/Documents/40.hass Making Maps/02221-Lab9/hawker-centres-kml.kml' using driver `KML'
Simple feature collection with 116 features and 2 fields
geometry type:  POINT
dimension:      XYZ
bbox:           xmin: 103.6974 ymin: 1.272716 xmax: 103.9882 ymax: 1.443882
epsg (SRID):    4326
proj4string:    +proj=longlat +datum=WGS84 +no_defs
#plot(centres);
centres <- centres %>%
  mutate(centreName = str_extract(Description,"(?<=<th>NAME<\\/th> <td>).*?(?=<\\/td>)")) %>% #regex to extract centre name from the html formatting stuff
  mutate(centreUpper = toupper(centreName)) %>%
  mutate(lon = st_coordinates(centres)[,1]) %>%
  mutate(lat = st_coordinates(centres)[,2])
tenders.sp <- left_join(tenders, centres, by=c("centre" = "centreUpper"))
centres.unique <- tenders %>%
  group_by(centre) %>%
  summarise(count=n()) %>%
  mutate(location = paste0(centre, ", Singapore"))
#install.packages("ggmap")
#library(ggmap)
#g <- geocode(centres.unique$location, output = "latlon", source = "google", sensor = F)
#centres <- bind_cols(centres.unique, g)
centres <- read_csv("centres-geocoded.csv")
Parsed with column specification:
cols(
  centre = col_character(),
  count = col_integer(),
  location = col_character(),
  lon = col_double(),
  lat = col_double()
)
tenders.sp <- left_join(tenders2, centres, by = c("centre" = "centre"))
library(ggplot2)
ggplot(tenders.sp, aes(x=lon,y=lat,size=priceM2,color=type)) + geom_point(alpha=0.3) + coord_fixed()

ggplot(tenders.sp,aes(x=lon,y=lat)) + geom_point() + geom_density2d() + coord_fixed()

ggplot(tenders.sp,aes(x=lon,y=lat)) + geom_point() + geom_hex() + coord_fixed()

tenders_count <- tenders.sp %>%
  group_by(trade) %>%
  summarise(count=n())
top_tenders <- head(tenders_count[order(tenders_count$count,decreasing=TRUE),],6)
ggplot(tenders.sp[tenders.sp$trade %in% top_tenders$trade,],aes(x=lon,y=lat)) + geom_point()  + facet_wrap(~ trade) + theme(plot.title = element_text(hjust=0.5),axis.text.x = element_text(size=4,angle=45,hjust=1)) + ggtitle("Locations of different trades of hawkers")

ggplot(tenders.sp,aes(x=lon,y=lat)) + geom_point()  + facet_wrap(~ type) + theme(plot.title = element_text(hjust=0.5),axis.text.x = element_text(size=4,angle=45,hjust=1)) + ggtitle("Locations of different types of hawkers")

ggplot(tenders.sp,aes(x=lon,y=lat)) + geom_point()  + facet_wrap(~ year(date)) + theme(plot.title = element_text(hjust=0.5),axis.text.x = element_text(size=4,angle=45,hjust=1)) + ggtitle("Locations of hawkers over time")

centres.sp <- tenders.sp %>%
#  filter(tenders.sp$lat > 0) %>%
  group_by(centre, lon, lat) %>%
  summarise(price=mean(priceM2,na.rm = T)) %>%
  replace_na(list(price=0))
coordinates(centres.sp) <- c('lon','lat')
centres.ppp <- unmark(as.ppp(centres.sp))
plot(centres.ppp)

sg <- readOGR(".","sg-all")
OGR data source with driver: ESRI Shapefile 
Source: ".", layer: "sg-all"
with 1 features
It has 13 fields
sg.window <- as.owin(sg)
centres.ppp <- centres.ppp[sg.window]
plot(Kest(centres.ppp))

plot(density(centres.ppp,0.02))

contour(density(centres.ppp,0.05))

pop <- as.im(readGDAL("sg-pop.tif"))
sg-pop.tif has GDAL driver GTiff 
and has 37 rows and 58 columns
plot(rhohat(centres.ppp,pop))

plot(rhohat(centres.ppp,pop,weights=centres.sp$price))

plot(pop)
plot(centres.ppp,add=T)

We observe that hawker centres are clustered densely in the CBD area and around Toa Payoh, even though areas like Bukit Panjang, Sengkang, and Jurong West are the areas with the highest population densities. This could be because only certain kinds of hawker centres are under NEA’s purview, so we don’t have a full list of hawker centres. Maybe areas with high populations have many coffeeshops and kopitiams inside malls instead of a hawker centre. Or perhaps we should be looking at a map of population during the day time, as people may live in these high population areas but work and eat more in the CBD.

LS0tCnRpdGxlOiAiUiBOb3RlYm9vayIKb3V0cHV0OiBodG1sX25vdGVib29rCi0tLQoKYGBge3J9CnRvX2luc3RhbGwgPC0gYygic2YiLCJzcCIsInJnZGFsIiwic3BhdHN0YXQiLCJnZ21hcCIsIm1hcHRvb2xzIikKaW5zdGFsbC5wYWNrYWdlcyh0b19pbnN0YWxsKQpsYXBwbHkodG9faW5zdGFsbCxsaWJyYXJ5LGNoYXJhY3Rlci5vbmx5PVRSVUUpCgpsaWJyYXJ5KHRpZHl2ZXJzZSkKbGlicmFyeShsdWJyaWRhdGUpCnRlbmRlcnMgPC0gcmVhZF9jc3YoInRhYnVsYS10ZW5kZXItYmlkcy1mcm9tLW1hci0yMDEyLXRvLWphbi0yMDE3LmNzdiIsY29sX25hbWVzPWMoImNlbnRyZSIsInN0YWxsIiwiYXJlYSIsInRyYWRlIiwiYmlkIiwibW9udGgiKSkKCgpgYGAKRmlyc3QsIHNvbWUgY2xlYW5pbmcuCmBgYHtyfQp0ZW5kZXJzICU+JQogIGRyb3BfbmEoY2VudHJlKSAlPiUKICBmaWx0ZXIocm93X251bWJlcigpICE9IDExMzUpICU+JQogIG11dGF0ZSh0eXBlPWlmX2Vsc2Uocm93X251bWJlcigpPDExMzUsCiAgICAgICAgICJjb29rZWQiLCJsb2NrdXAiKSkgJT4lCiAgbXV0YXRlKGJpZE51bSA9IGFzLm51bWVyaWMoc3RyX3JlcGxhY2VfYWxsKGJpZCxwYXR0ZXJuPSJcXCR8LCIscmVwbGFjZW1lbnQgPSAiIikpKSAlPiUKICBtdXRhdGUoZGF0ZT1kbXkocGFzdGUwKCIwMS0iLG1vbnRoKSkpICU+JQogIG11dGF0ZShwcmljZU0yPWJpZE51bS9hcmVhKSAgJT4lICMtPiBpbnRlcm1lZGlhdGUKICBkcm9wX25hKGJpZE51bSkgLT4gdGVuZGVyczI7CiNpbnRlcm1lZGlhdGVbaXMubmEoaW50ZXJtZWRpYXRlJGJpZE51bSksXSAjNTAgZW50cmllcyBoYXZlIGEgZGF0ZSBpbiB0aGUgYmlkIGNvbHVtbgp0ZW5kZXJzMiAlPiUKICBncm91cF9ieSh0cmFkZSkgJT4lCiAgZ2dwbG90KGFlcyh0cmFkZSxwcmljZU0yKSkgKyAKICBnZW9tX3BvaW50KGFscGhhPTAuMSxzaXplPTEuNSxzaGFwZT0zKSArCiAgdGhlbWUoYXhpcy50ZXh0LnggPSBlbGVtZW50X3RleHQoYW5nbGU9NDUsaGp1c3Q9MSkpIDsKYGBgCkhlcmUgd2Ugc2VlIHRoZSBkaXN0cmlidXRpb24gb2YgcHJpY2UgcGVyIHNxdWFyZSBtZXRlciBmb3IgZGlmZmVyZW50IGZvb2QgdHlwZXMuCmBgYHtyfQp0ZW5kZXJzLnNwICU+JQogIGdyb3VwX2J5KGNlbnRyZSxsb24sbGF0KSAlPiUKICBzdW1tYXJpc2UodG90YWxfcHJpY2VfTTI9c3VtKGJpZE51bSkvc3VtKGFyZWEpKSAlPiUKICBnZ3Bsb3QoKSArCiAgZ2VvbV9wb2ludChhZXMobG9uLGxhdCxzaXplPXRvdGFsX3ByaWNlX00yKSxhbHBoYT0wLjQpICsKICBnZW9tX3BhdGgoZGF0YT1mb3J0aWZ5KHNnKSxhZXMobG9uZyxsYXQsZ3JvdXA9Z3JvdXApKQogIGdncGxvdChhZXMoY2VudHJlLHRvdGFsX3ByaWNlX00yKSkgKyAKICBnZW9tX3BvaW50KCkgKwogIHRoZW1lKGF4aXMudGV4dC54ID0gZWxlbWVudF90ZXh0KGFuZ2xlPTQ1LGhqdXN0PTEpKSAKYGBgCkJ5IHN1bW1pbmcgdGhlIGJpZCBwcmljZSBmb3IgZWFjaCBzdGFsbCBhbmQgZmluZGluZyB0aGUgYXZlcmFnZSBwcmljZSBwZXIgbWV0ZXIgc3F1YXJlIGZvciBlYWNoIGhhd2tlciBjZW50cmUsIHdlIGNhbiBzZWUgd2hpY2ggbGFuZCBhcmVhcyBjb3N0IG1vcmUgZm9yIGhhd2tlcnMuCgpgYGB7cn0KdGVuZGVyczIgJT4lCiAgZ3JvdXBfYnkoY2VudHJlKSAlPiUKICBzdW1tYXJpc2UocHJpY2U9bWVhbihwcmljZU0yKSkKCnRlbmRlcnMyICU+JQogIGZpbHRlcih0eXBlPT0nY29va2VkJykgJT4lCiAgZ3JvdXBfYnkoY2VudHJlKSAlPiUKICBzdW1tYXJpc2UocHJpY2U9bWVhbihwcmljZU0yKSkKCiMgdGVuZGVyczIgJT4lCiMgICBncm91cF9ieShjZW50cmUpICU+JQojICAgc3VtbWFyaXNlKGJpZHM9bigpKSAlPiUKIyAgIGdncGxvdChhZXMoY2VudHJlLGJpZHMpKSArIGdlb21fYmFyKHN0YXQ9ImlkZW50aXR5IikKCmxpYnJhcnkoc2YpCmNlbnRyZXMgPC0gc3RfcmVhZCgiaGF3a2VyLWNlbnRyZXMta21sLmttbCIpCiNwbG90KGNlbnRyZXMpOwoKY2VudHJlcyA8LSBjZW50cmVzICU+JQogIG11dGF0ZShjZW50cmVOYW1lID0gc3RyX2V4dHJhY3QoRGVzY3JpcHRpb24sIig/PD08dGg+TkFNRTxcXC90aD4gPHRkPikuKj8oPz08XFwvdGQ+KSIpKSAlPiUgI3JlZ2V4IHRvIGV4dHJhY3QgY2VudHJlIG5hbWUgZnJvbSB0aGUgaHRtbCBmb3JtYXR0aW5nIHN0dWZmCiAgbXV0YXRlKGNlbnRyZVVwcGVyID0gdG91cHBlcihjZW50cmVOYW1lKSkgJT4lCiAgbXV0YXRlKGxvbiA9IHN0X2Nvb3JkaW5hdGVzKGNlbnRyZXMpWywxXSkgJT4lCiAgbXV0YXRlKGxhdCA9IHN0X2Nvb3JkaW5hdGVzKGNlbnRyZXMpWywyXSkKCnRlbmRlcnMuc3AgPC0gbGVmdF9qb2luKHRlbmRlcnMsIGNlbnRyZXMsIGJ5PWMoImNlbnRyZSIgPSAiY2VudHJlVXBwZXIiKSkKCmNlbnRyZXMudW5pcXVlIDwtIHRlbmRlcnMgJT4lCiAgZ3JvdXBfYnkoY2VudHJlKSAlPiUKICBzdW1tYXJpc2UoY291bnQ9bigpKSAlPiUKICBtdXRhdGUobG9jYXRpb24gPSBwYXN0ZTAoY2VudHJlLCAiLCBTaW5nYXBvcmUiKSkKCiNpbnN0YWxsLnBhY2thZ2VzKCJnZ21hcCIpCiNsaWJyYXJ5KGdnbWFwKQojZyA8LSBnZW9jb2RlKGNlbnRyZXMudW5pcXVlJGxvY2F0aW9uLCBvdXRwdXQgPSAibGF0bG9uIiwgc291cmNlID0gImdvb2dsZSIsIHNlbnNvciA9IEYpCiNjZW50cmVzIDwtIGJpbmRfY29scyhjZW50cmVzLnVuaXF1ZSwgZykKY2VudHJlcyA8LSByZWFkX2NzdigiY2VudHJlcy1nZW9jb2RlZC5jc3YiKQp0ZW5kZXJzLnNwIDwtIGxlZnRfam9pbih0ZW5kZXJzMiwgY2VudHJlcywgYnkgPSBjKCJjZW50cmUiID0gImNlbnRyZSIpKQoKbGlicmFyeShnZ3Bsb3QyKQpnZ3Bsb3QodGVuZGVycy5zcCwgYWVzKHg9bG9uLHk9bGF0LHNpemU9cHJpY2VNMixjb2xvcj10eXBlKSkgKyBnZW9tX3BvaW50KGFscGhhPTAuMykgKyBjb29yZF9maXhlZCgpCmdncGxvdCh0ZW5kZXJzLnNwLGFlcyh4PWxvbix5PWxhdCkpICsgZ2VvbV9wb2ludCgpICsgZ2VvbV9kZW5zaXR5MmQoKSArIGNvb3JkX2ZpeGVkKCkKZ2dwbG90KHRlbmRlcnMuc3AsYWVzKHg9bG9uLHk9bGF0KSkgKyBnZW9tX3BvaW50KCkgKyBnZW9tX2hleCgpICsgY29vcmRfZml4ZWQoKQoKCmBgYApgYGB7cn0KdGVuZGVyc19jb3VudCA8LSB0ZW5kZXJzLnNwICU+JQogIGdyb3VwX2J5KHRyYWRlKSAlPiUKICBzdW1tYXJpc2UoY291bnQ9bigpKQp0b3BfdGVuZGVycyA8LSBoZWFkKHRlbmRlcnNfY291bnRbb3JkZXIodGVuZGVyc19jb3VudCRjb3VudCxkZWNyZWFzaW5nPVRSVUUpLF0sNikKZ2dwbG90KHRlbmRlcnMuc3BbdGVuZGVycy5zcCR0cmFkZSAlaW4lIHRvcF90ZW5kZXJzJHRyYWRlLF0sYWVzKHg9bG9uLHk9bGF0KSkgKyBnZW9tX3BvaW50KCkgICsgZmFjZXRfd3JhcCh+IHRyYWRlKSArIHRoZW1lKHBsb3QudGl0bGUgPSBlbGVtZW50X3RleHQoaGp1c3Q9MC41KSxheGlzLnRleHQueCA9IGVsZW1lbnRfdGV4dChzaXplPTQsYW5nbGU9NDUsaGp1c3Q9MSkpICsgZ2d0aXRsZSgiTG9jYXRpb25zIG9mIGRpZmZlcmVudCB0cmFkZXMgb2YgaGF3a2VycyIpCmBgYApgYGB7cn0KZ2dwbG90KHRlbmRlcnMuc3AsYWVzKHg9bG9uLHk9bGF0KSkgKyBnZW9tX3BvaW50KCkgICsgZmFjZXRfd3JhcCh+IHR5cGUpICsgdGhlbWUocGxvdC50aXRsZSA9IGVsZW1lbnRfdGV4dChoanVzdD0wLjUpLGF4aXMudGV4dC54ID0gZWxlbWVudF90ZXh0KHNpemU9NCxhbmdsZT00NSxoanVzdD0xKSkgKyBnZ3RpdGxlKCJMb2NhdGlvbnMgb2YgZGlmZmVyZW50IHR5cGVzIG9mIGhhd2tlcnMiKQoKYGBgCmBgYHtyfQpnZ3Bsb3QodGVuZGVycy5zcCxhZXMoeD1sb24seT1sYXQpKSArIGdlb21fcG9pbnQoKSAgKyBmYWNldF93cmFwKH4geWVhcihkYXRlKSkgKyB0aGVtZShwbG90LnRpdGxlID0gZWxlbWVudF90ZXh0KGhqdXN0PTAuNSksYXhpcy50ZXh0LnggPSBlbGVtZW50X3RleHQoc2l6ZT00LGFuZ2xlPTQ1LGhqdXN0PTEpKSArIGdndGl0bGUoIkxvY2F0aW9ucyBvZiBoYXdrZXJzIG92ZXIgdGltZSIpCmBgYApgYGB7cn0KY2VudHJlcy5zcCA8LSB0ZW5kZXJzLnNwICU+JQojICBmaWx0ZXIodGVuZGVycy5zcCRsYXQgPiAwKSAlPiUKICBncm91cF9ieShjZW50cmUsIGxvbiwgbGF0KSAlPiUKICBzdW1tYXJpc2UocHJpY2U9bWVhbihwcmljZU0yLG5hLnJtID0gVCkpICU+JQogIHJlcGxhY2VfbmEobGlzdChwcmljZT0wKSkKCmNvb3JkaW5hdGVzKGNlbnRyZXMuc3ApIDwtIGMoJ2xvbicsJ2xhdCcpCmNlbnRyZXMucHBwIDwtIHVubWFyayhhcy5wcHAoY2VudHJlcy5zcCkpCnBsb3QoY2VudHJlcy5wcHApCgpzZyA8LSByZWFkT0dSKCIuIiwic2ctYWxsIikKc2cud2luZG93IDwtIGFzLm93aW4oc2cpCmNlbnRyZXMucHBwIDwtIGNlbnRyZXMucHBwW3NnLndpbmRvd10KCnBsb3QoS2VzdChjZW50cmVzLnBwcCkpCnBsb3QoZGVuc2l0eShjZW50cmVzLnBwcCwwLjAyKSkKY29udG91cihkZW5zaXR5KGNlbnRyZXMucHBwLDAuMDUpKQoKcG9wIDwtIGFzLmltKHJlYWRHREFMKCJzZy1wb3AudGlmIikpCnBsb3QocmhvaGF0KGNlbnRyZXMucHBwLHBvcCkpCnBsb3QocmhvaGF0KGNlbnRyZXMucHBwLHBvcCx3ZWlnaHRzPWNlbnRyZXMuc3AkcHJpY2UpKQoKcGxvdChwb3ApCnBsb3QoY2VudHJlcy5wcHAsYWRkPVQpCgpgYGAKV2Ugb2JzZXJ2ZSB0aGF0IGhhd2tlciBjZW50cmVzIGFyZSBjbHVzdGVyZWQgZGVuc2VseSBpbiB0aGUgQ0JEIGFyZWEgYW5kIGFyb3VuZCBUb2EgUGF5b2gsIGV2ZW4gdGhvdWdoIGFyZWFzIGxpa2UgQnVraXQgUGFuamFuZywgU2VuZ2thbmcsIGFuZCBKdXJvbmcgV2VzdCBhcmUgdGhlIGFyZWFzIHdpdGggdGhlIGhpZ2hlc3QgcG9wdWxhdGlvbiBkZW5zaXRpZXMuIFRoaXMgY291bGQgYmUgYmVjYXVzZSBvbmx5IGNlcnRhaW4ga2luZHMgb2YgaGF3a2VyIGNlbnRyZXMgYXJlIHVuZGVyIE5FQSdzIHB1cnZpZXcsIHNvIHdlIGRvbid0IGhhdmUgYSBmdWxsIGxpc3Qgb2YgaGF3a2VyIGNlbnRyZXMuIE1heWJlIGFyZWFzIHdpdGggaGlnaCBwb3B1bGF0aW9ucyBoYXZlIG1hbnkgY29mZmVlc2hvcHMgYW5kIGtvcGl0aWFtcyBpbnNpZGUgbWFsbHMgaW5zdGVhZCBvZiBhIGhhd2tlciBjZW50cmUuIE9yIHBlcmhhcHMgd2Ugc2hvdWxkIGJlIGxvb2tpbmcgYXQgYSBtYXAgb2YgcG9wdWxhdGlvbiBkdXJpbmcgdGhlIGRheSB0aW1lLCBhcyBwZW9wbGUgbWF5IGxpdmUgaW4gdGhlc2UgaGlnaCBwb3B1bGF0aW9uIGFyZWFzIGJ1dCB3b3JrIGFuZCBlYXQgbW9yZSBpbiB0aGUgQ0JELgo=