#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
Sample 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.