#1. Import your data
data_all <- readRDS(here("google_poi_data.rds"))
#6. Map the collected POIs
# Convert the data to an sf object using XY coordinates
data_all_sf <- data_all %>%
rename(x = places.location.longitude, y = places.location.latitude) %>%
filter(!is.na(x) & !is.na(y)) %>%
st_as_sf(coords = c("x", "y"), crs = 4326)
City
Brookline, MA
Two places selected
School and Library
#2. Tidy your data
#Remove duplicated rows
data_all_sf %>% distinct(places.id)
data_sf_clean <- data_all_sf %>%
distinct(places.id, .keep_all = TRUE)
#show how the number of rows has changed after removing
glue("number of rows before: {nrow(data_all_sf)} -> after: {nrow(data_sf_clean)}")
## number of rows before: 349 -> after: 126
#Flatten/unnest list-columns
data_sf_clean_flat <- data_sf_clean %>%
mutate(places.types = map_chr(places.types, ~ paste(.x, collapse = ", ")))
#show how the number of rows has changed after removing
head(data_sf_clean$places.types)
## [[1]]
## [1] "university" "school" "point_of_interest"
## [4] "establishment"
##
## [[2]]
## [1] "library" "point_of_interest" "establishment"
##
## [[3]]
## [1] "school" "point_of_interest" "establishment"
##
## [[4]]
## [1] "school" "point_of_interest" "establishment"
##
## [[5]]
## [1] "preschool" "synagogue" "child_care_agency"
## [4] "place_of_worship" "school" "point_of_interest"
## [7] "establishment"
##
## [[6]]
## [1] "school" "point_of_interest" "establishment"
head(data_sf_clean_flat$places.types)
## [1] "university, school, point_of_interest, establishment"
## [2] "library, point_of_interest, establishment"
## [3] "school, point_of_interest, establishment"
## [4] "school, point_of_interest, establishment"
## [5] "preschool, synagogue, child_care_agency, place_of_worship, school, point_of_interest, establishment"
## [6] "school, point_of_interest, establishment"
#Handle missing values
poi_dropna <- data_sf_clean_flat %>%
drop_na(places.userRatingCount)
#report how many rows remain after this step
print(paste0("Before: ", nrow(data_sf_clean_flat)))
## [1] "Before: 126"
print(paste0("After: ", nrow(poi_dropna)))
## [1] "After: 59"
Reasoning
Removed NAs from column count of user ratings
as in this
case, school and library ratings are not highly dependent on reviews
such as restaurants and other recreational places.
# #Filter by location
# city boundary
brookline <- tigris::places("MA", progress_bar = FALSE) %>%
filter(NAME == 'Brookline') %>%
st_transform(4326)
# Converting poi_dropna into a sf object
poi_sf <- poi_dropna %>%
st_as_sf(coords=c("places.location.longitude", "places.location.latitude"),
crs = 4326)
# POIs within the city boundary
poi_sf_in <- poi_sf[st_within(poi_sf, brookline, sparse = FALSE), ]
#show how the number of rows has changed after removing
print(paste0("Before: ", nrow(poi_sf)))
## [1] "Before: 59"
print(paste0("After: ", nrow(poi_sf_in)))
## [1] "After: 48"
#3. Show your cleaned POI data
poi_table <- poi_sf_in %>%
head(10) %>%
kable("html", caption = "Sample POI Table") %>%
kable_styling(
bootstrap_options = c("striped", "hover", "condensed", "responsive"),
full_width = FALSE,
position = "center"
)
poi_table
places.id | places.types | places.formattedAddress | places.rating | places.userRatingCount | places.displayName.text | places.displayName.languageCode | geometry |
---|---|---|---|---|---|---|---|
ChIJtatu–h544kRSl8ua7iFIWc | university, school, point_of_interest, establishment | 928 Commonwealth Ave, Boston, MA 02215, USA | 4.6 | 9 | Boston University School of Hospitality Administration | en | POINT (-71.11742 42.351) |
ChIJm7xYFAB544kRj7LGdgWPGn4 | library, point_of_interest, establishment | 227 Babcock St, Brookline, MA 02446, USA | 4.0 | 1 | Little Free Library | en | POINT (-71.12115 42.35074) |
ChIJAR3qpOx544kRuw2kzW8igys | preschool, synagogue, child_care_agency, place_of_worship, school, point_of_interest, establishment | 1187 Beacon St, Brookline, MA 02446, USA | 4.9 | 107 | Temple Ohabei Shalom | en | POINT (-71.11451 42.34352) |
ChIJqdx0LeB544kR27gWRq_bRT0 | preschool, child_care_agency, school, point_of_interest, establishment | 1560 Beacon St, Brookline, MA 02446, USA | 5.0 | 6 | Top Tots Learning Center | en | POINT (-71.13186 42.34007) |
ChIJXY5XjbB544kRsPTCFs0Ovmk | school, point_of_interest, establishment | 1622-A Beacon St #304, Brookline, MA 02446, USA | 5.0 | 2 | First School of Mathematics | en | POINT (-71.13517 42.33977) |
ChIJU4zHX7B544kRl9ZLY7o52RQ | child_care_agency, school, point_of_interest, establishment | 1615 Beacon St, Brookline, MA 02446, USA | 3.7 | 37 | Russian School of Mathematics - RSM Brookline | en | POINT (-71.13462 42.33907) |
ChIJbV5vWRB544kRfib4WzF-fl4 | preschool, child_care_agency, school, point_of_interest, establishment | 152 Aspinwall Ave, Brookline, MA 02446, USA | 5.0 | 2 | Ms. Pamela’s NeighborSchool | en | POINT (-71.11685 42.33725) |
ChIJzz_nqgZ544kR8VHA53qyltk | preschool, child_care_agency, school, point_of_interest, establishment | 15 St Paul St, Brookline, MA 02446, USA | 5.0 | 3 | Pine Village Preschool - Brookline | en | POINT (-71.11817 42.33749) |
ChIJh0yvfb1544kRyhnq3oJyQPM | preschool, child_care_agency, school, point_of_interest, establishment | 110 Harvard St, Brookline, MA 02446, USA | 5.0 | 1 | Little Corner SchoolHouse | en | POINT (-71.12157 42.33663) |
ChIJASU3j71544kRXlD4EmBW0g4 | preschool, child_care_agency, school, point_of_interest, establishment | 138 Harvard St, Brookline, MA 02446, USA | 4.6 | 9 | Bright Horizons at Brookline | en | POINT (-71.12179 42.33759) |
# Visualize
tmap_mode("view")
library(tmap)
tm_shape(brookline) +
tm_borders() +
tm_shape(poi_sf_in) +
tm_dots(
shape = 21,
border.col = "black",
border.lwd = 0.5,
col = "places.rating",
palette = "magma",
size = "places.userRatingCount",
popup.vars = c(
"Name" = "places.displayName.text",
"Rating" = "places.rating",
"Rating Count" = "places.userRatingCount"
)
)
Since I worked with schools and libraries, the two categories are not very distinct from one another. As a result, it is difficult to identify a clear pattern. However, I do notice that more specialized schools—such as math schools or tennis academies—tend to have smaller circles on the map, reflecting lower rating counts. In contrast, public libraries and more commercial learning spaces, such as yoga or Tai Chi studios, receive significantly more ratings than traditional institutions like Brookline High School.
To allow for a fairer comparison of popularity across these POIs, I
calculated a weighted average based on ratings. The resulting average
score is 4.68
, which is relatively high. Given that
Brookline is a high-income area, I assume this reflects the fact that
schools and libraries are well maintained through tax revenues.
## weighted_avg
## 1 4.682637
Yes, they do tend to cluster in specific neighborhoods within the town. Having lived in Brookline, I can confirm that many of these services are concentrated around Coolidge Corner, which is one of the most popular shopping and dining areas. This neighborhood attracts a high volume of foot traffic, making it a natural hub for schools, libraries, and other learning or recreational spaces. The concentration of services here likely reflects both the demand generated by visitors and residents, as well as the area’s accessibility and central role in community life.
Personally, I would like to visit
The Public Library of Brookline -- Brookline Village Library
as it has a high rating (4.7
) and assuming schools are not
open to public as such so I would prefer accessing a public library
instead.