tidycensus::census_api_key(Sys.getenv("census_api"))
## To install your API key for use in future sessions, run this function with `install = TRUE`.

The city I’ve chosen for completing Mini Assignment 1 is Camden NJ. The city has a population of 70,996 and is part of the Camden County.

#### Tract polygons for the Yelp query
tract <- suppressMessages(
  get_acs(geography = "tract", # or "block group", "county", "state" etc. 
          state = "NJ",
          county = c("Camden"), 
          variables = c(hhincome = 'B19019_001'),
          year = 2021,
          survey = "acs5", # American Community Survey 5-year estimate
          geometry = TRUE, # returns sf objects
          output = "wide") # wide vs. long
)

camden <- tigris::places('NJ') %>% 
  filter(NAME == 'Camden') 
## Retrieving data for the year 2022
tract_camden <- tract[camden,]
# View the data
message(sprintf("nrow: %s, ncol: %s", nrow(tract_camden), ncol(tract_camden)))
## nrow: 33, ncol: 5
tract_camden %>% head() %>% knitr::kable() # Ignore this kable() function. This function is for neatly displaying tables on HTML document.
GEOID NAME hhincomeE hhincomeM geometry
1 34007604700 Census Tract 6047, Camden County, New Jersey 108068 16423 MULTIPOLYGON (((-75.09476 3…
6 34007601300 Census Tract 6013, Camden County, New Jersey 27042 6252 MULTIPOLYGON (((-75.10599 3…
7 34007601101 Census Tract 6011.01, Camden County, New Jersey 28641 7919 MULTIPOLYGON (((-75.09513 3…
9 34007601600 Census Tract 6016, Camden County, New Jersey 25988 5688 MULTIPOLYGON (((-75.1163 39…
10 34007601400 Census Tract 6014, Camden County, New Jersey 39549 12845 MULTIPOLYGON (((-75.1074 39…
11 34007610500 Census Tract 6105, Camden County, New Jersey 67246 14772 MULTIPOLYGON (((-75.12386 3…
# Retaining only those I want.
# Notice that select function can also change names when it selects columns.
tract_camden <- tract_camden %>% 
  select(GEOID, 
         hhincome = hhincomeE) # New name = old name

tmap_mode("view")
## tmap mode set to interactive viewing
tm_shape(tract_camden) + tm_borders(lwd = 2) + 
  tm_shape(camden) + tm_polygons(col = 'red', alpha = 0.4)
# Function: Get tract-wise radius
get_r <- function(poly, epsg_id){
  #---------------------
  # Takes: a single POLYGON or LINESTRTING
  # Outputs: distance between the centroid of the boundingbox and a corner of the bounding box
  #---------------------
  
  # Get bounding box of a given polygon
  bb <- st_bbox(poly)
  # Get lat & long coordinates of any one corner of the bounding box.
  bb_corner <- st_point(c(bb[1], bb[2])) %>% st_sfc(crs = epsg_id)
  # Get centroid of the bb
  bb_center_x <- (bb[3]+bb[1])/2
  bb_center_y <- (bb[4]+bb[2])/2
  bb_center <- st_point(c(bb_center_x, bb_center_y)) %>% st_sfc(crs = epsg_id) %>% st_sf()
    
  # Get the distance between bb_p and c
  r <- st_distance(bb_corner, bb_center)
  # Multiply 1.1 to make the circle a bit larger than the Census Tract.
  # See the Yelp explanation of their radius parameter to see why we do this.
  bb_center$radius <- r*1.1
  return(bb_center)
}
## Using a loop -----------------------------------------------------------------
# Creating an empty vector of NA. 
# Results will fill this vector
epsg_id <- 4326

r4all_loop <- vector("list", nrow(tract_camden))

# Starting a for-loop

for (i in 1:nrow(tract_camden)){
  r4all_loop[[i]] <- tract_camden %>% 
    st_transform(crs = epsg_id) %>% 
    st_geometry() %>% 
    .[[i]] %>% 
    get_r(epsg_id = epsg_id)
}

r4all_loop <- bind_rows(r4all_loop)


# Using a functional -----------------------------------------------------------
# We use a functional (sapply) to apply this custom function to each Census Tract.
r4all_apply <- tract_camden %>%
  st_geometry() %>% 
  st_transform(crs = epsg_id) %>% 
  lapply(., function(x) get_r(x, epsg_id = epsg_id))

r4all_apply <- bind_rows(r4all_apply)

# Are these two identical?
identical(r4all_apply, r4all_loop)
## [1] TRUE
# Appending X Y coordinates as seprate columns
ready_4_yelp <- r4all_apply %>% 
  mutate(x = st_coordinates(.)[,1],
         y = st_coordinates(.)[,2])
tmap_mode('view')
## tmap mode set to interactive viewing
ready_4_yelp %>% 
  # Draw a buffer centered at the centroid of Tract polygons.
  # Radius of the buffer is the radius we just calculated using loop
  st_buffer(., dist = .$radius) %>% 
  # Display this buffer in red
  tm_shape(.) + tm_polygons(alpha = 0.5, col = 'red') +
  # Display the original polygon in blue
  tm_shape(tract_camden) + tm_borders(col= 'blue')
# Get bb for Camden
camden_bb <- st_bbox(camden)

# Find coordinates for the four sides of the bb
west <- camden_bb[1]
east <- camden_bb[3]
south <- camden_bb[2]
north <- camden_bb[4]

# Break the bb into a grid
fishnet_n <- 4
steps <- abs(west - east)/fishnet_n


# Fishnet points
fish_x <- seq(from = west, to = east, by = steps)
fish_y <- seq(from = south, to = north, by = steps)

fishnet <- expand.grid(fish_x, fish_y) %>% 
  rename(x = Var1, y = Var2) %>% 
  st_as_sf(coords = c('x', 'y'), crs = 4326)

# Visualize it
tm_shape(fishnet %>% st_buffer(dist = units::set_units(steps, "degree"))) + 
  tm_polygons(alpha = 0.1, col = 'red') +
  tm_shape(camden) + tm_borders(lwd = 3)
which_tract <- 2
test <- business_search(api_key = Sys.getenv('yelp_api'), # like we did for census, store your api key
                        categories = 'restaurants', # return only restaurant businesses
                        latitude = ready_4_yelp$y[which_tract],
                        longitude = ready_4_yelp$x[which_tract],
                        offset = 0, # 1st page, 1st obs
                        radius = round(ready_4_yelp$radius[which_tract]), # radius requires integer value
                        limit = 50) # how many business per page
## No encoding supplied: defaulting to UTF-8.
lapply(test, head)
## $businesses
##                       id                          alias                    name
## 1 QUM6YFcm1pa-zC04ywYzSA old-san-juan-restaurant-camden Old San Juan Restaurant
## 2 K1ABvJZtgcdYHyH83fkFRA           donkeys-place-camden          Donkey's Place
## 3 Oq1LlUCCGXwA2Lt3HuZAog       la-ingrata-camden-camden       La Ingrata Camden
## 4 EaXVdMGDs3_SPt5A4mQ5fg          corinnes-place-camden         Corinne's Place
## 5 a0991ioc-HZrPIIw87a5Yg            el-taco-loco-camden            El Taco Loco
## 6 ssr49VxAlvrBo7F7izQvXA           the-pub-pennsauken-2                 The Pub
##                                                              image_url
## 1 https://s3-media1.fl.yelpcdn.com/bphoto/F5uDuP5NdgE2oaP90QdRQg/o.jpg
## 2 https://s3-media2.fl.yelpcdn.com/bphoto/zoPOpN7Rxkub62lHS2XPQg/o.jpg
## 3 https://s3-media2.fl.yelpcdn.com/bphoto/v7sEt93Wo9L2S5iyhN2JOQ/o.jpg
## 4 https://s3-media1.fl.yelpcdn.com/bphoto/r2cmUcR4mDZwfHQJEZF_9w/o.jpg
## 5 https://s3-media2.fl.yelpcdn.com/bphoto/baERENzYYmZdXcM1dnBK8g/o.jpg
## 6 https://s3-media1.fl.yelpcdn.com/bphoto/xSr5ItsGYn3pyjvQpJQzDg/o.jpg
##   is_closed
## 1     FALSE
## 2     FALSE
## 3     FALSE
## 4     FALSE
## 5     FALSE
## 6     FALSE
##                                                                                                                                                                                           url
## 1 https://www.yelp.com/biz/old-san-juan-restaurant-camden?adjust_creative=IIcoxsHZylAK38wuHdFFOA&utm_campaign=yelp_api_v3&utm_medium=api_v3_business_search&utm_source=IIcoxsHZylAK38wuHdFFOA
## 2           https://www.yelp.com/biz/donkeys-place-camden?adjust_creative=IIcoxsHZylAK38wuHdFFOA&utm_campaign=yelp_api_v3&utm_medium=api_v3_business_search&utm_source=IIcoxsHZylAK38wuHdFFOA
## 3       https://www.yelp.com/biz/la-ingrata-camden-camden?adjust_creative=IIcoxsHZylAK38wuHdFFOA&utm_campaign=yelp_api_v3&utm_medium=api_v3_business_search&utm_source=IIcoxsHZylAK38wuHdFFOA
## 4          https://www.yelp.com/biz/corinnes-place-camden?adjust_creative=IIcoxsHZylAK38wuHdFFOA&utm_campaign=yelp_api_v3&utm_medium=api_v3_business_search&utm_source=IIcoxsHZylAK38wuHdFFOA
## 5            https://www.yelp.com/biz/el-taco-loco-camden?adjust_creative=IIcoxsHZylAK38wuHdFFOA&utm_campaign=yelp_api_v3&utm_medium=api_v3_business_search&utm_source=IIcoxsHZylAK38wuHdFFOA
## 6           https://www.yelp.com/biz/the-pub-pennsauken-2?adjust_creative=IIcoxsHZylAK38wuHdFFOA&utm_campaign=yelp_api_v3&utm_medium=api_v3_business_search&utm_source=IIcoxsHZylAK38wuHdFFOA
##   review_count                                                  categories
## 1           71            latin, puertorican, Latin American, Puerto Rican
## 2          386                                  cheesesteaks, Cheesesteaks
## 3           13 tacos, pizza, newmexican, Tacos, Pizza, New Mexican Cuisine
## 4          169      soulfood, southern, bbq, Soul Food, Southern, Barbeque
## 5           45                                            mexican, Mexican
## 6          480      pubs, steak, tradamerican, Pubs, Steakhouses, American
##   rating coordinates.latitude coordinates.longitude     transactions price
## 1    4.0             39.94306             -75.08890 pickup, delivery    $$
## 2    4.4             39.93154             -75.10233 pickup, delivery    $$
## 3    4.7             39.94560             -75.09548 pickup, delivery  <NA>
## 4    4.3             39.93151             -75.10173 pickup, delivery    $$
## 5    4.0             39.94602             -75.09102 pickup, delivery     $
## 6    3.1             39.93291             -75.08001         delivery   $$$
##   location.address1 location.address2 location.address3 location.city
## 1   217 Marlton Ave              <NA>              <NA>        Camden
## 2   1223 Haddon Ave                                            Camden
## 3   1999 Federal St              <NA>                          Camden
## 4   1254 Haddon Ave                                            Camden
## 5   2311 Federal St                                            Camden
## 6   7600 Kaighn Ave                                <NA>    Pennsauken
##   location.zip_code location.country location.state
## 1             08105               US             NJ
## 2             08103               US             NJ
## 3             08105               US             NJ
## 4             08103               US             NJ
## 5             08100               US             NJ
## 6             08109               US             NJ
##                location.display_address        phone  display_phone  distance
## 1     217 Marlton Ave, Camden, NJ 08105 +18569631200 (856) 963-1200  473.1607
## 2     1223 Haddon Ave, Camden, NJ 08103 +18569662616 (856) 966-2616 1307.7844
## 3     1999 Federal St, Camden, NJ 08105 +18562033424 (856) 203-3424  472.8572
## 4     1254 Haddon Ave, Camden, NJ 08103 +18565414894 (856) 541-4894 1281.7983
## 5     2311 Federal St, Camden, NJ 08100 +18569642636 (856) 964-2636  572.3932
## 6 7600 Kaighn Ave, Pennsauken, NJ 08109 +18566656440 (856) 665-6440 1521.7327
##                                                                                                                                                             business_hours
## 1  FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, 1100, 1100, 1100, 1100, 1100, 1100, 1100, 2100, 2100, 2100, 2100, 2100, 2100, 2100, 0, 1, 2, 3, 4, 5, 6, REGULAR, TRUE
## 2                                              FALSE, FALSE, FALSE, FALSE, FALSE, 0700, 1000, 1000, 1000, 0700, 1800, 1800, 1800, 1800, 1800, 0, 1, 2, 3, 4, REGULAR, TRUE
## 3  FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, 1000, 1000, 1000, 1000, 1000, 1000, 1000, 2100, 2100, 2100, 2100, 2100, 2100, 2100, 0, 1, 2, 3, 4, 5, 6, REGULAR, TRUE
## 4                        FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, 1100, 1100, 1100, 1100, 1100, 1200, 1930, 1930, 1930, 1930, 1930, 1930, 1, 2, 3, 4, 5, 6, REGULAR, TRUE
## 5  FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, 1000, 1000, 1000, 1000, 1000, 1000, 0900, 2130, 2130, 2130, 2130, 2130, 2130, 2100, 0, 1, 2, 3, 4, 5, 6, REGULAR, TRUE
## 6 FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, 1600, 1600, 1600, 1600, 1600, 1500, 1300, 2100, 2100, 2100, 2100, 2200, 2200, 2100, 0, 1, 2, 3, 4, 5, 6, REGULAR, FALSE
##   attributes.business_temp_closed                    attributes.menu_url
## 1                              NA https://oldsanjuancamden.com/menu.html
## 2                              NA            http://donkeystoo.net/menu/
## 3                              NA       https://laingratacamden.com/menu
## 4                              NA        https://corinnesplace.com/menu/
## 5                              NA           https://mytacoloco.com/menu/
## 6                              NA         https://www.thepubnj.com/menus
##   attributes.open24_hours attributes.waitlist_reservation
## 1                      NA                              NA
## 2                      NA                              NA
## 3                      NA                              NA
## 4                      NA                              NA
## 5                      NA                              NA
## 6                      NA                              NA
## 
## $total
## [1] 87
## 
## $region
## $region$center
## $region$center$longitude
## [1] -75.09401
## 
## $region$center$latitude
## [1] 39.94141
names(test)
## [1] "businesses" "total"      "region"
paste0("is it a data.frame?: ", is.data.frame(test$businesses), ", ",
       " how many rows?: ", nrow(test$businesses), ", ",
       " how many columns?: ", ncol(test$businesses))
## [1] "is it a data.frame?: TRUE,  how many rows?: 50,  how many columns?: 18"
# FUNCTION
get_yelp <- function(tract, category){
  # ----------------------------------
  # Gets one row of tract information (1,) and category name (str),
  # Outputs a list of business data.frame
  Sys.sleep(1)
  n <- 1
  # First request --------------------------------------------------------------
  resp <- business_search(api_key = Sys.getenv("yelp_api"), 
                          categories = category, 
                          latitude = tract$y, 
                          longitude = tract$x, 
                          offset = (n - 1) * 50, # = 0 when n = 1
                          radius = round(tract$radius), 
                          limit = 50)
  # Calculate how many requests are needed in total
  required_n <- ceiling(resp$total/50)
  
  # out is where the results will be appended to.
  out <- vector("list", required_n)
  
  # Store the business information to nth slot in out
  out[[n]] <- resp$businesses
  
  # Change the name of the elements to the total required_n
  # This is to know if there are more than 1000 businesses,
  # we know how many.
  names(out)[n] <- required_n
  
  # Throw error if more than 1000
  if (resp$total >= 1000)
  {
    # glue formats string by inserting {n} with what's currently stored in object n.
    print(glue::glue("{n}th row has >= 1000 businesses."))
    # Stop before going into the loop because we need to
    # break down Census Tract to something smaller.
    return(out)
  } 
  else 
  {
    # add 1 to n
    n <- n + 1
    
    # Now we know required_n -----------------------------------------------------
    # Starting a loop
    while(n <= required_n){
      resp <- business_search(api_key = Sys.getenv("yelp_api"), 
                              categories = category, 
                              latitude = tract$y, 
                              longitude = tract$x, 
                              offset = (n - 1) * 50, 
                              radius = round(tract$radius), 
                              limit = 50)
      
      out[[n]] <- resp$businesses
      
      n <- n + 1
    } #<< end of while loop
    
    # Merge all elements in the list into a single data frame
    out <- out %>% bind_rows()
    
    return(out)
  }
}
# Apply the function for the first Census Tract
yelp_first_tract <- get_yelp(ready_4_yelp[1,], "restaurants") %>% 
  as_tibble()
## No encoding supplied: defaulting to UTF-8.
## No encoding supplied: defaulting to UTF-8.
# Print
yelp_first_tract %>% print
## # A tibble: 89 × 18
##    id       alias name  image_url is_closed url   review_count categories rating
##    <chr>    <chr> <chr> <chr>     <lgl>     <chr>        <int> <list>      <dbl>
##  1 roZZJ1i… phil… Phil… https://… FALSE     http…           89 <df>          4.1
##  2 cm2FwmH… comm… Comm… https://… FALSE     http…           59 <df>          4.7
##  3 B6Lvq3s… zepp… Zepp… https://… FALSE     http…          348 <df>          4.1
##  4 f6BnRbe… oakl… Oakl… https://… FALSE     http…           70 <df>          3.5
##  5 Fy5mHS-… pho-… Pho … https://… FALSE     http…           98 <df>          4.4
##  6 cOhXlwk… amat… Amat… https://… FALSE     http…           61 <df>          3.9
##  7 eAShb5F… li-b… Li B… https://… FALSE     http…          189 <df>          4.7
##  8 9JUi2YQ… jala… Jala… https://… FALSE     http…          185 <df>          3.8
##  9 P7oAQxD… lesb… Lesb… https://… FALSE     http…          113 <df>          4.7
## 10 4zmaWmJ… aunt… Aunt… https://… FALSE     http…          136 <df>          3.8
## # ℹ 79 more rows
## # ℹ 9 more variables: coordinates <df[,2]>, transactions <list>, price <chr>,
## #   location <df[,8]>, phone <chr>, display_phone <chr>, distance <dbl>,
## #   business_hours <list>, attributes <df[,4]>
# Prepare a collector
yelp_all_list <- vector("list", nrow(ready_4_yelp))

# Looping through all Census Tracts
for (row in 1:nrow(ready_4_yelp)){
  yelp_all_list[[row]] <- suppressMessages(get_yelp(ready_4_yelp[row,], "restaurants"))
  print(paste0("Current row: ", row))
}
## [1] "Current row: 1"
## [1] "Current row: 2"
## [1] "Current row: 3"
## [1] "Current row: 4"
## [1] "Current row: 5"
## [1] "Current row: 6"
## [1] "Current row: 7"
## [1] "Current row: 8"
## [1] "Current row: 9"
## [1] "Current row: 10"
## [1] "Current row: 11"
## [1] "Current row: 12"
## [1] "Current row: 13"
## [1] "Current row: 14"
## [1] "Current row: 15"
## [1] "Current row: 16"
## [1] "Current row: 17"
## [1] "Current row: 18"
## [1] "Current row: 19"
## [1] "Current row: 20"
## [1] "Current row: 21"
## [1] "Current row: 22"
## [1] "Current row: 23"
## [1] "Current row: 24"
## [1] "Current row: 25"
## [1] "Current row: 26"
## [1] "Current row: 27"
## [1] "Current row: 28"
## [1] "Current row: 29"
## [1] "Current row: 30"
## [1] "Current row: 31"
## [1] "Current row: 32"
## [1] "Current row: 33"
# Collapsing the list into a data.frame
yelp_all <- yelp_all_list %>% bind_rows() %>% as_tibble()

# print
yelp_all %>% print(width=1000)
## # A tibble: 2,286 × 18
##    id                     alias                              
##    <chr>                  <chr>                              
##  1 roZZJ1iZv96f6sl3tC4Fyw phillys-phatties-oaklyn-2          
##  2 cm2FwmHNxg9LCzOFdwEJSQ common-grounds-coffeehouse-oaklyn  
##  3 B6Lvq3sOYwhKxPzadDka9g zeppoli-collingswood               
##  4 f6BnRbe4L4soMkcghCPBTg oaklyn-manor-bar-and-grill-oaklyn  
##  5 Fy5mHS-qcXvh1UUI7oE9dA pho-asia-audubon-5                 
##  6 cOhXlwkb4zaRRJp_P3zEVg amato-bros-oaklyn-2                
##  7 eAShb5FJSTdx4wsGHT_6SQ li-beirut-collingswood             
##  8 9JUi2YQzzV3PfOBzvQPrMQ jalapenos-grill-2-oaklyn           
##  9 P7oAQxDu8rapFn8QY0nmyA lesbiveggies-audubon               
## 10 4zmaWmJx_24wjqE8px3uXA aunt-bertas-kitchen-haddon-township
##    name                      
##    <chr>                     
##  1 Philly's Phatties         
##  2 Common Grounds Coffeehouse
##  3 Zeppoli                   
##  4 Oaklyn Manor Bar & Grill  
##  5 Pho Asia                  
##  6 Amato Bros                
##  7 Li Beirut                 
##  8 Jalapenos Grill 2         
##  9 LesbiVeggies              
## 10 Aunt Berta's Kitchen      
##    image_url                                                           
##    <chr>                                                               
##  1 https://s3-media4.fl.yelpcdn.com/bphoto/C-Trca-nMGV1Nh_zxJO4jA/o.jpg
##  2 https://s3-media3.fl.yelpcdn.com/bphoto/Mz_cL2E-o0hzB6a_HO8U3A/o.jpg
##  3 https://s3-media1.fl.yelpcdn.com/bphoto/M8B3I8eQm2yRj0nWqRcCBw/o.jpg
##  4 https://s3-media2.fl.yelpcdn.com/bphoto/Qm0JzCPU8B3M0ncRc6wPJw/o.jpg
##  5 https://s3-media2.fl.yelpcdn.com/bphoto/0wkQfYaWcPhrbd2N_OISmA/o.jpg
##  6 https://s3-media2.fl.yelpcdn.com/bphoto/S_XosDQoH8bHlJIvH9Kp7w/o.jpg
##  7 https://s3-media3.fl.yelpcdn.com/bphoto/P8qfYZsrCAJ_ICAczOQN7A/o.jpg
##  8 https://s3-media3.fl.yelpcdn.com/bphoto/GWu9tom06hlJodI8UbiAZQ/o.jpg
##  9 https://s3-media4.fl.yelpcdn.com/bphoto/Fh6esnQLNZ3sdU9PWeyCJw/o.jpg
## 10 https://s3-media1.fl.yelpcdn.com/bphoto/G13cTyv8aJpy9t2tGpGnoA/o.jpg
##    is_closed
##    <lgl>    
##  1 FALSE    
##  2 FALSE    
##  3 FALSE    
##  4 FALSE    
##  5 FALSE    
##  6 FALSE    
##  7 FALSE    
##  8 FALSE    
##  9 FALSE    
## 10 FALSE    
##    url                                                                          
##    <chr>                                                                        
##  1 https://www.yelp.com/biz/phillys-phatties-oaklyn-2?adjust_creative=IIcoxsHZy…
##  2 https://www.yelp.com/biz/common-grounds-coffeehouse-oaklyn?adjust_creative=I…
##  3 https://www.yelp.com/biz/zeppoli-collingswood?adjust_creative=IIcoxsHZylAK38…
##  4 https://www.yelp.com/biz/oaklyn-manor-bar-and-grill-oaklyn?adjust_creative=I…
##  5 https://www.yelp.com/biz/pho-asia-audubon-5?adjust_creative=IIcoxsHZylAK38wu…
##  6 https://www.yelp.com/biz/amato-bros-oaklyn-2?adjust_creative=IIcoxsHZylAK38w…
##  7 https://www.yelp.com/biz/li-beirut-collingswood?adjust_creative=IIcoxsHZylAK…
##  8 https://www.yelp.com/biz/jalapenos-grill-2-oaklyn?adjust_creative=IIcoxsHZyl…
##  9 https://www.yelp.com/biz/lesbiveggies-audubon?adjust_creative=IIcoxsHZylAK38…
## 10 https://www.yelp.com/biz/aunt-bertas-kitchen-haddon-township?adjust_creative…
##    review_count categories   rating coordinates$latitude $longitude transactions
##           <int> <list>        <dbl>                <dbl>      <dbl> <list>      
##  1           89 <df [3 × 2]>    4.1                 39.9      -75.1 <chr [2]>   
##  2           59 <df [3 × 2]>    4.7                 39.9      -75.1 <chr [1]>   
##  3          348 <df [2 × 2]>    4.1                 39.9      -75.1 <chr [1]>   
##  4           70 <df [2 × 2]>    3.5                 39.9      -75.1 <chr [0]>   
##  5           98 <df [3 × 2]>    4.4                 39.9      -75.1 <chr [2]>   
##  6           61 <df [3 × 2]>    3.9                 39.9      -75.1 <chr [2]>   
##  7          189 <df [3 × 2]>    4.7                 39.9      -75.1 <chr [2]>   
##  8          185 <df [1 × 2]>    3.8                 39.9      -75.1 <chr [2]>   
##  9          113 <df [3 × 2]>    4.7                 39.9      -75.1 <chr [2]>   
## 10          136 <df [3 × 2]>    3.8                 39.9      -75.1 <chr [1]>   
##    price location$address1      $address2 $address3 $city             $zip_code
##    <chr> <chr>                  <chr>     <chr>     <chr>             <chr>    
##  1 $     215 W Clinton Ave      ""        ""        "Oaklyn"          08107    
##  2 $     205 W Clinton Ave      ""        ""        "Oaklyn"          08107    
##  3 $$$   618 Collings Ave       ""        ""        "Collingswood"    08107    
##  4 $$    198 W Clinton Ave      ""        ""        "Oaklyn"          08107    
##  5 $$    130 E Black Horse Pike "Ste 308" ""        "Audubon"         08106    
##  6 <NA>  612 White Horse Pike   ""        <NA>      "Oaklyn"          08107    
##  7 $$$   619 W Collings Ave     ""        <NA>      "Collingswood"    08107    
##  8 $$    900 White Horse Pike   ""        ""        "Oaklyn"          08107    
##  9 <NA>  112 W Merchant St      ""        <NA>      "Audubon "        08106    
## 10 $$    639 White Horse Pike   ""        ""        "Haddon Township" 08107    
##    $country $state $display_address phone        display_phone  distance
##    <chr>    <chr>  <list>           <chr>        <chr>             <dbl>
##  1 US       NJ     <chr [2]>        +18568585500 (856) 858-5500     166.
##  2 US       NJ     <chr [2]>        +18568540088 (856) 854-0088     203.
##  3 US       NJ     <chr [2]>        +18568542670 (856) 854-2670    1163.
##  4 US       NJ     <chr [2]>        +18568545433 (856) 854-5433     256.
##  5 US       NJ     <chr [3]>        +18564326862 (856) 432-6862    1104.
##  6 US       NJ     <chr [2]>        +18568549300 (856) 854-9300     625.
##  7 US       NJ     <chr [2]>        +18564772105 (856) 477-2105    1206.
##  8 US       NJ     <chr [2]>        +18568331146 (856) 833-1146     741.
##  9 US       NJ     <chr [2]>        +18563238458 (856) 323-8458    1265.
## 10 US       NJ     <chr [2]>        +18568587009 (856) 858-7009     677.
##    business_hours attributes$business_temp_closed
##    <list>         <lgl>                          
##  1 <df [1 × 3]>   NA                             
##  2 <df [1 × 3]>   NA                             
##  3 <df [1 × 3]>   NA                             
##  4 <df [1 × 3]>   NA                             
##  5 <df [1 × 3]>   NA                             
##  6 <df [1 × 3]>   NA                             
##  7 <df [1 × 3]>   NA                             
##  8 <df [1 × 3]>   NA                             
##  9 <df [1 × 3]>   NA                             
## 10 <df [1 × 3]>   NA                             
##    $menu_url                                                      $open24_hours
##    <chr>                                                          <lgl>        
##  1 ""                                                             NA           
##  2  <NA>                                                          NA           
##  3 "http://www.zeppolirestaurant.com/zeppoli_menu.pdf"            NA           
##  4 "http://www.oaklynmanor.com/?page_id=1482"                     NA           
##  5 "http://www.pho-asia.com"                                      NA           
##  6  <NA>                                                          NA           
##  7 "https://libeirutnj.com/eat"                                   NA           
##  8 "http://www.thejalapenosgrill.com/#!menu/cfpj"                 NA           
##  9 "https://www.lesbiveggies.com/menu-basil"                      NA           
## 10 "https://auntbertaskitchen.com/menu?location=auntbertasoaklyn" NA           
##    $waitlist_reservation
##    <lgl>                
##  1 NA                   
##  2 NA                   
##  3 NA                   
##  4 NA                   
##  5 NA                   
##  6 NA                   
##  7 NA                   
##  8 NA                   
##  9 NA                   
## 10 FALSE                
## # ℹ 2,276 more rows
saveRDS(yelp_all, here('yelp_data.rds'))
# Extract coordinates
yelp_sf <- yelp_all %>% 
  mutate(x = .$coordinates$longitude,
         y = .$coordinates$latitude) %>% 
  filter(!is.na(x) & !is.na(y)) %>% 
  st_as_sf(coords = c("x", "y"), crs = 4326)

# Map
tm_shape(yelp_sf) +
  tm_dots(col = "review_count", style="quantile")
which_tract <- 2
test <- business_search(api_key = Sys.getenv('yelp_api'), # like we did for census, store your api key
                        categories = 'fitness', # return only fitness businesses
                        latitude = ready_4_yelp$y[which_tract],
                        longitude = ready_4_yelp$x[which_tract],
                        offset = 0, # 1st page, 1st obs
                        radius = round(ready_4_yelp$radius[which_tract]), # radius requires integer value
                        limit = 50) # how many business per page
## No encoding supplied: defaulting to UTF-8.
lapply(test, head)
## $businesses
##                       id                                      alias
## 1 aWx3pL5aHKAevBvEfSwsZQ      merritt-massage-and-yoga-philadelphia
## 2 e-91D-BZPjWihH1iqJ-aZg            heart-fitness-studio-burlington
## 3 jlquY8tYYVMVBgOnOfRAkQ                      lynda-lippin-new-york
## 4 Fao_fMq3rerOZq6MWZ8F1Q              soul-dreams-studio-ocean-city
## 5 CylaJq3fKo-3PFpD9Mj2tA       krystique-personal-fitness-havertown
## 6 azfZtIk5rCd3IzRrtZbnSw cranaleith-spiritual-center-philadelphia-2
##                          name
## 1    Merritt Massage and Yoga
## 2        Heart Fitness Studio
## 3                Lynda Lippin
## 4          Soul Dreams Studio
## 5  Krystique Personal Fitness
## 6 Cranaleith Spiritual Center
##                                                              image_url
## 1 https://s3-media2.fl.yelpcdn.com/bphoto/1yjV_emPpVboqoyP8-4yEQ/o.jpg
## 2 https://s3-media1.fl.yelpcdn.com/bphoto/X_e8pq4aBSw0S0iNJlSE9A/o.jpg
## 3 https://s3-media4.fl.yelpcdn.com/bphoto/1aLtfEa7Vp3JqzhQikDaew/o.jpg
## 4 https://s3-media4.fl.yelpcdn.com/bphoto/Se1T8Z09BQ7rWbb0qZirgQ/o.jpg
## 5 https://s3-media4.fl.yelpcdn.com/bphoto/Q_2czPi4-ENFaWVzcb53Sg/o.jpg
## 6 https://s3-media3.fl.yelpcdn.com/bphoto/M50jJyyhuqyqc46-LtAa4w/o.jpg
##   is_closed
## 1     FALSE
## 2     FALSE
## 3     FALSE
## 4     FALSE
## 5     FALSE
## 6     FALSE
##                                                                                                                                                                                                       url
## 1      https://www.yelp.com/biz/merritt-massage-and-yoga-philadelphia?adjust_creative=IIcoxsHZylAK38wuHdFFOA&utm_campaign=yelp_api_v3&utm_medium=api_v3_business_search&utm_source=IIcoxsHZylAK38wuHdFFOA
## 2            https://www.yelp.com/biz/heart-fitness-studio-burlington?adjust_creative=IIcoxsHZylAK38wuHdFFOA&utm_campaign=yelp_api_v3&utm_medium=api_v3_business_search&utm_source=IIcoxsHZylAK38wuHdFFOA
## 3                      https://www.yelp.com/biz/lynda-lippin-new-york?adjust_creative=IIcoxsHZylAK38wuHdFFOA&utm_campaign=yelp_api_v3&utm_medium=api_v3_business_search&utm_source=IIcoxsHZylAK38wuHdFFOA
## 4              https://www.yelp.com/biz/soul-dreams-studio-ocean-city?adjust_creative=IIcoxsHZylAK38wuHdFFOA&utm_campaign=yelp_api_v3&utm_medium=api_v3_business_search&utm_source=IIcoxsHZylAK38wuHdFFOA
## 5       https://www.yelp.com/biz/krystique-personal-fitness-havertown?adjust_creative=IIcoxsHZylAK38wuHdFFOA&utm_campaign=yelp_api_v3&utm_medium=api_v3_business_search&utm_source=IIcoxsHZylAK38wuHdFFOA
## 6 https://www.yelp.com/biz/cranaleith-spiritual-center-philadelphia-2?adjust_creative=IIcoxsHZylAK38wuHdFFOA&utm_campaign=yelp_api_v3&utm_medium=api_v3_business_search&utm_source=IIcoxsHZylAK38wuHdFFOA
##   review_count
## 1            9
## 2            4
## 3            4
## 4            3
## 5            0
## 6            1
##                                                                                                               categories
## 1                                                             yoga, reiki, massage_therapy, Yoga, Reiki, Massage Therapy
## 2                                                                 nutritionists, healthtrainers, Nutritionists, Trainers
## 3                                                               healthtrainers, pilates, reiki, Trainers, Pilates, Reiki
## 4                                                     reiki, psychic_astrology, yoga, Reiki, Supernatural Readings, Yoga
## 5                                                                        healthtrainers, lifecoach, Trainers, Life Coach
## 6 religiousorgs, nonprofit, meditationcenters, Religious Organizations, Community Service/Non-Profit, Meditation Centers
##   rating coordinates.latitude coordinates.longitude transactions
## 1    4.9             40.08366             -75.23292         NULL
## 2    5.0             40.07621             -74.85810         NULL
## 3    5.0             40.71759             -74.00993         NULL
## 4    5.0             39.27966             -74.57484         NULL
## 5    0.0             39.97817             -75.31511         NULL
## 6    5.0             40.12647             -75.00762         NULL
##        location.address1 location.address2 location.address3 location.city
## 1 200 W Northwestern Ave                    The Cedars House  Philadelphia
## 2            438 High St              <NA>                     Burlington 
## 3                   <NA>              <NA>              <NA>      New York
## 4         801 Asbury Ave           Ste 311                      Ocean City
## 5                   <NA>              <NA>              <NA>     Havertown
## 6       13475 Proctor Rd              <NA>                    Philadelphia
##   location.zip_code location.country location.state
## 1             19118               US             PA
## 2             08016               US             NJ
## 3             10038               US             NY
## 4             08226               US             NJ
## 5             19083               US             PA
## 6             19116               US             PA
##                                           location.display_address        phone
## 1 200 W Northwestern Ave, The Cedars House, Philadelphia, PA 19118 +12153172412
## 2                               438 High St, Burlington , NJ 08016 +18565770432
## 3                                               New York, NY 10038 +13474626932
## 4                    801 Asbury Ave, Ste 311, Ocean City, NJ 08226 +16094424266
## 5                                              Havertown, PA 19083 +14844772414
## 6                         13475 Proctor Rd, Philadelphia, PA 19116 +12159346206
##    display_phone  distance
## 1 (215) 317-2412  19752.75
## 2 (856) 577-0432  25067.58
## 3 (347) 462-6932 126067.74
## 4 (609) 442-4266  85978.20
## 5 (484) 477-2414  19230.73
## 6 (215) 934-6206  21853.03
##                                                                                                                                                            business_hours
## 1                                             FALSE, FALSE, FALSE, FALSE, FALSE, 0900, 0900, 0900, 0900, 0900, 1500, 1500, 1500, 1500, 1700, 0, 1, 2, 3, 4, REGULAR, TRUE
## 2                       FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, 0600, 0600, 0600, 0600, 0600, 1000, 1900, 1900, 1900, 1900, 1600, 1200, 0, 1, 2, 3, 4, 5, REGULAR, TRUE
## 3                                            FALSE, FALSE, FALSE, FALSE, FALSE, 1000, 1000, 1000, 1000, 1000, 1830, 1800, 1800, 1400, 1600, 0, 1, 2, 3, 6, REGULAR, FALSE
## 4 FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, 0600, 0800, 0830, 0800, 0800, 0800, 0800, 2000, 2030, 1930, 2100, 2000, 1730, 1900, 0, 1, 2, 3, 4, 5, 6, REGULAR, TRUE
## 5                       FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, 0900, 0900, 0900, 0900, 0900, 0900, 1400, 1400, 1400, 1400, 1400, 1400, 0, 1, 2, 3, 4, 5, REGULAR, TRUE
## 6                                             FALSE, FALSE, FALSE, FALSE, FALSE, 0900, 0900, 0900, 0900, 0900, 1700, 1700, 1700, 1700, 1700, 0, 1, 2, 3, 4, REGULAR, TRUE
##   attributes.business_temp_closed attributes.menu_url
## 1                              NA                  NA
## 2                              NA                  NA
## 3                              NA                  NA
## 4                              NA                  NA
## 5                              NA                  NA
## 6                              NA                  NA
##   attributes.waitlist_reservation attributes.open24_hours
## 1                           FALSE                      NA
## 2                              NA                      NA
## 3                              NA                      NA
## 4                              NA                      NA
## 5                              NA                      NA
## 6                              NA                      NA
## 
## $total
## [1] 28
## 
## $region
## $region$center
## $region$center$longitude
## [1] -75.09401
## 
## $region$center$latitude
## [1] 39.94141
# See what's inside
names(test)
## [1] "businesses" "total"      "region"
# Business
paste0("is it a data.frame?: ", is.data.frame(test$businesses), ", ",
       " how many rows?: ", nrow(test$businesses), ", ",
       " how many columns?: ", ncol(test$businesses))
## [1] "is it a data.frame?: TRUE,  how many rows?: 28,  how many columns?: 17"
# FUNCTION
get_yelp <- function(tract, category){
  # ----------------------------------
  # Gets one row of tract information (1,) and category name (str),
  # Outputs a list of business data.frame
  Sys.sleep(1)
  n <- 1
  # First request --------------------------------------------------------------
  resp <- business_search(api_key = Sys.getenv("yelp_api"), 
                          categories = category, 
                          latitude = tract$y, 
                          longitude = tract$x, 
                          offset = (n - 1) * 50, # = 0 when n = 1
                          radius = round(tract$radius), 
                          limit = 50)
  # Calculate how many requests are needed in total
  required_n <- ceiling(resp$total/50)
  
  # out is where the results will be appended to.
  out <- vector("list", required_n)
  
  # Store the business information to nth slot in out
  out[[n]] <- resp$businesses
  
  # Change the name of the elements to the total required_n
  # This is to know if there are more than 1000 businesses,
  # we know how many.
  names(out)[n] <- required_n
  
  # Throw error if more than 1000
  if (resp$total >= 1000)
  {
    # glue formats string by inserting {n} with what's currently stored in object n.
    print(glue::glue("{n}th row has >= 1000 businesses."))
    # Stop before going into the loop because we need to
    # break down Census Tract to something smaller.
    return(out)
  } 
  else 
  {
    # add 1 to n
    n <- n + 1
    
    # Now we know required_n -----------------------------------------------------
    # Starting a loop
    while(n <= required_n){
      resp <- business_search(api_key = Sys.getenv("yelp_api"), 
                              categories = category, 
                              latitude = tract$y, 
                              longitude = tract$x, 
                              offset = (n - 1) * 50, 
                              radius = round(tract$radius), 
                              limit = 50)
      
      out[[n]] <- resp$businesses
      
      n <- n + 1
    } #<< end of while loop
    
    # Merge all elements in the list into a single data frame
    out <- out %>% bind_rows()
    
    return(out)
  }
}
# Apply the function for the first Census Tract
yelp_first_tract <- get_yelp(ready_4_yelp[1,], "fitness") %>% 
  as_tibble()
## No encoding supplied: defaulting to UTF-8.
## No encoding supplied: defaulting to UTF-8.
# Print
yelp_first_tract %>% print
## # A tibble: 51 × 17
##    id       alias name  image_url is_closed url   review_count categories rating
##    <chr>    <chr> <chr> <chr>     <lgl>     <chr>        <int> <list>      <dbl>
##  1 tFh0DrD… plat… Plat… https://… FALSE     http…           12 <df>          5  
##  2 ZNSvj37… aqua… Aqua… https://… FALSE     http…           10 <df>          5  
##  3 fnn5-IN… live… Live… https://… FALSE     http…           11 <df>          4.9
##  4 wy6rrNz… mari… Mari… https://… FALSE     http…            8 <df>          5  
##  5 13Cwpg4… bell… Bell… https://… FALSE     http…            6 <df>          4.8
##  6 JbYTmYZ… live… Live… https://… FALSE     http…            5 <df>          5  
##  7 q0v8D8C… sky-… Sky … https://… FALSE     http…            0 <df>          0  
##  8 aWx3pL5… merr… Merr… https://… FALSE     http…            9 <df>          4.9
##  9 TUtXhzL… team… Team… https://… FALSE     http…            6 <df>          5  
## 10 v9Gfu5_… suga… Suga… https://… FALSE     http…            5 <df>          5  
## # ℹ 41 more rows
## # ℹ 8 more variables: coordinates <df[,2]>, transactions <list>,
## #   location <df[,8]>, phone <chr>, display_phone <chr>, distance <dbl>,
## #   business_hours <list>, attributes <df[,4]>
# Prepare a collector
yelp_all_list <- vector("list", nrow(ready_4_yelp))

print(ready_4_yelp)
## Simple feature collection with 33 features and 3 fields
## Geometry type: POINT
## Dimension:     XY
## Bounding box:  xmin: -75.12625 ymin: 39.88701 xmax: -75.05242 ymax: 39.96879
## Geodetic CRS:  WGS 84
## First 10 features:
##           radius                   geometry         x        y
## 1  1212.3850 [m] POINT (-75.08425 39.90025) -75.08425 39.90025
## 2  1318.5396 [m] POINT (-75.09401 39.94141) -75.09401 39.94141
## 3  1061.0002 [m] POINT (-75.08649 39.94906) -75.08649 39.94906
## 4   672.5410 [m] POINT (-75.11078 39.92648) -75.11078 39.92648
## 5  1540.8691 [m] POINT (-75.09447 39.93046) -75.09447 39.93046
## 6  3951.6865 [m] POINT (-75.08563 39.96879) -75.08563 39.96879
## 7  1179.7179 [m] POINT (-75.09787 39.92418) -75.09787 39.92418
## 8  1824.9822 [m]  POINT (-75.07389 39.9134) -75.07389 39.91340
## 9   906.9278 [m] POINT (-75.10914 39.91987) -75.10914 39.91987
## 10 1400.0774 [m] POINT (-75.08995 39.96038) -75.08995 39.96038
str(ready_4_yelp)
## Classes 'sf' and 'data.frame':   33 obs. of  4 variables:
##  $ radius  : Units: [m] num [1:33, 1] 1212 1319 1061 673 1541 ...
##  $ geometry:sfc_POINT of length 33; first list element:  'XY' Named num  -75.1 39.9
##   ..- attr(*, "names")= chr [1:2] "xmax" "ymax"
##  $ x       : num  -75.1 -75.1 -75.1 -75.1 -75.1 ...
##  $ y       : num  39.9 39.9 39.9 39.9 39.9 ...
##  - attr(*, "sf_column")= chr "geometry"
##  - attr(*, "agr")= Factor w/ 3 levels "constant","aggregate",..: NA NA NA
##   ..- attr(*, "names")= chr [1:3] "radius" "x" "y"
print(nrow(ready_4_yelp))
## [1] 33
# Looping through all Census Tracts
for (row in 1:nrow(ready_4_yelp)){
  yelp_all_list[[row]] <- suppressMessages(get_yelp(ready_4_yelp[row,], "fitness"))
  print(paste0("Current row: ", row))
}
## [1] "Current row: 1"
## [1] "Current row: 2"
## [1] "Current row: 3"
## [1] "Current row: 4"
## [1] "Current row: 5"
## [1] "Current row: 6"
## [1] "Current row: 7"
## [1] "Current row: 8"
## [1] "Current row: 9"
## [1] "Current row: 10"
## [1] "Current row: 11"
## [1] "Current row: 12"
## [1] "Current row: 13"
## [1] "Current row: 14"
## [1] "Current row: 15"
## [1] "Current row: 16"
## [1] "Current row: 17"
## [1] "Current row: 18"
## [1] "Current row: 19"
## [1] "Current row: 20"
## [1] "Current row: 21"
## [1] "Current row: 22"
## [1] "Current row: 23"
## [1] "Current row: 24"
## [1] "Current row: 25"
## [1] "Current row: 26"
## [1] "Current row: 27"
## [1] "Current row: 28"
## [1] "Current row: 29"
## [1] "Current row: 30"
## [1] "Current row: 31"
## [1] "Current row: 32"
## [1] "Current row: 33"
# Collapsing the list into a data.frame
yelp_all <- yelp_all_list %>% bind_rows() %>% as_tibble()

# print
yelp_all %>% print(width=1000)
## # A tibble: 954 × 17
##    id                    
##    <chr>                 
##  1 tFh0DrDMTom4B3pHpDpRNQ
##  2 ZNSvj373MyzTbYJl3iNt7g
##  3 fnn5-INgblboZ1zTt3Ej0g
##  4 wy6rrNzm19QlghyZpoE77Q
##  5 13Cwpg4A2M5uxKXczDbkZQ
##  6 JbYTmYZCLt92MxnCwGvKeA
##  7 q0v8D8CTZkHzAtSmupmhWg
##  8 aWx3pL5aHKAevBvEfSwsZQ
##  9 TUtXhzLZhTjE9Kgo2vo1fQ
## 10 v9Gfu5_cxnG4_DPTWbHtPQ
##    alias                                                        
##    <chr>                                                        
##  1 platoon-fitness-philadelphia                                 
##  2 aqua-vida-philadelphia-3                                     
##  3 live-more-adventures-hoboken-3                               
##  4 mariehilfertyfitness-philadelphia                            
##  5 bells-bodies-fitness-haddonfield-2                           
##  6 live-in-joy-yoga-and-wellness-audubon                        
##  7 sky-fitness-oaklyn                                           
##  8 merritt-massage-and-yoga-philadelphia                        
##  9 team-taino-martial-arts-and-fitness-west-collingswood-heights
## 10 sugar-space-wellness-haddonfield                             
##    name                             
##    <chr>                            
##  1 Platoon Fitness                  
##  2 Aqua Vida                        
##  3 Live More Adventures             
##  4 MarieHilfertyFitness             
##  5 Bell's Bodies Fitness            
##  6 Live in Joy Yoga & Wellness      
##  7 Sky Fitness                      
##  8 Merritt Massage and Yoga         
##  9 Team Taino Martial Arts & Fitness
## 10 Sugar Space Wellness             
##    image_url                                                           
##    <chr>                                                               
##  1 https://s3-media4.fl.yelpcdn.com/bphoto/ZdZ9i2SPfHt6PWKzdzjXiw/o.jpg
##  2 https://s3-media2.fl.yelpcdn.com/bphoto/lM6qZlDv-7sG35I67nXWqg/o.jpg
##  3 https://s3-media1.fl.yelpcdn.com/bphoto/_ysGXAgQu3OJKEOmOWLYCg/o.jpg
##  4 https://s3-media3.fl.yelpcdn.com/bphoto/4T8eKfYpBDAQgO7nml-eIg/o.jpg
##  5 https://s3-media4.fl.yelpcdn.com/bphoto/VPpVSqofiCHK6oq29ZuQbg/o.jpg
##  6 https://s3-media2.fl.yelpcdn.com/bphoto/Q49QmBXqh5meyckbrjEpXg/o.jpg
##  7 https://s3-media2.fl.yelpcdn.com/bphoto/GNDsAe6VfZvMQC9I4E6-Eg/o.jpg
##  8 https://s3-media2.fl.yelpcdn.com/bphoto/1yjV_emPpVboqoyP8-4yEQ/o.jpg
##  9 https://s3-media3.fl.yelpcdn.com/bphoto/yvvrJpvY2Dv7y76NOwcgvw/o.jpg
## 10 https://s3-media1.fl.yelpcdn.com/bphoto/ZqE1dPOEu8O9W3LOFlx8vQ/o.jpg
##    is_closed
##    <lgl>    
##  1 FALSE    
##  2 FALSE    
##  3 FALSE    
##  4 FALSE    
##  5 FALSE    
##  6 FALSE    
##  7 FALSE    
##  8 FALSE    
##  9 FALSE    
## 10 FALSE    
##    url                                                                          
##    <chr>                                                                        
##  1 https://www.yelp.com/biz/platoon-fitness-philadelphia?adjust_creative=IIcoxs…
##  2 https://www.yelp.com/biz/aqua-vida-philadelphia-3?adjust_creative=IIcoxsHZyl…
##  3 https://www.yelp.com/biz/live-more-adventures-hoboken-3?adjust_creative=IIco…
##  4 https://www.yelp.com/biz/mariehilfertyfitness-philadelphia?adjust_creative=I…
##  5 https://www.yelp.com/biz/bells-bodies-fitness-haddonfield-2?adjust_creative=…
##  6 https://www.yelp.com/biz/live-in-joy-yoga-and-wellness-audubon?adjust_creati…
##  7 https://www.yelp.com/biz/sky-fitness-oaklyn?adjust_creative=IIcoxsHZylAK38wu…
##  8 https://www.yelp.com/biz/merritt-massage-and-yoga-philadelphia?adjust_creati…
##  9 https://www.yelp.com/biz/team-taino-martial-arts-and-fitness-west-collingswo…
## 10 https://www.yelp.com/biz/sugar-space-wellness-haddonfield?adjust_creative=II…
##    review_count categories   rating coordinates$latitude $longitude transactions
##           <int> <list>        <dbl>                <dbl>      <dbl> <list>      
##  1           12 <df [3 × 2]>    5                   39.9      -75.2 <list [0]>  
##  2           10 <df [3 × 2]>    5                   40.0      -75.2 <list [0]>  
##  3           11 <df [3 × 2]>    4.9                 40.7      -74.0 <list [0]>  
##  4            8 <df [2 × 2]>    5                   40.0      -75.2 <list [0]>  
##  5            6 <df [2 × 2]>    4.8                 39.9      -75.0 <list [0]>  
##  6            5 <df [3 × 2]>    5                   39.9      -75.1 <list [0]>  
##  7            0 <df [1 × 2]>    0                   39.9      -75.1 <list [0]>  
##  8            9 <df [3 × 2]>    4.9                 40.1      -75.2 <list [0]>  
##  9            6 <df [3 × 2]>    5                   39.9      -75.1 <list [0]>  
## 10            5 <df [3 × 2]>    5                   39.9      -75.0 <list [0]>  
##    location$address1      $address2 $address3          $city                    
##    <chr>                  <chr>     <chr>              <chr>                    
##  1 716 Walnut St          ""        ""                 Philadelphia             
##  2 2929 Walnut St         ""        ""                 Philadelphia             
##  3 <NA>                   <NA>      ""                 Hoboken                  
##  4 <NA>                   <NA>      ""                 Philadelphia             
##  5 114 Ellis St           ""        ""                 Haddonfield              
##  6 118 W Merchant St      <NA>      ""                 Audubon                  
##  7 <NA>                   <NA>      ""                 Oaklyn                   
##  8 200 W Northwestern Ave ""        "The Cedars House" Philadelphia             
##  9 450 Black Horse Pike   <NA>      ""                 West Collingswood Heights
## 10 258 Kings Hwy E        ""        ""                 Haddonfield              
##    $zip_code $country $state $display_address phone        display_phone 
##    <chr>     <chr>    <chr>  <list>           <chr>        <chr>         
##  1 19106     US       PA     <chr [2]>        +12157528666 (215) 752-8666
##  2 19104     US       PA     <chr [2]>        +14844301650 (484) 430-1650
##  3 07030     US       NJ     <chr [1]>        +18023473562 (802) 347-3562
##  4 19103     US       PA     <chr [1]>        +12152063523 (215) 206-3523
##  5 08033     US       NJ     <chr [2]>        +18562879340 (856) 287-9340
##  6 08106     US       NJ     <chr [2]>        +18565461006 (856) 546-1006
##  7 08107     US       NJ     <chr [1]>        +18563205188 (856) 320-5188
##  8 19118     US       PA     <chr [3]>        +12153172412 (215) 317-2412
##  9 08059     US       NJ     <chr [2]>        +18564269141 (856) 426-9141
## 10 08033     US       NJ     <chr [2]>        +18567330655 (856) 733-0655
##    distance business_hours attributes$business_temp_closed
##       <dbl> <list>         <lgl>                          
##  1   7935.  <df [1 × 3]>   NA                             
##  2  10240.  <df [1 × 3]>   NA                             
##  3 129751.  <df [1 × 3]>   NA                             
##  4   9720.  <df [1 × 3]>   NA                             
##  5   4724.  <df [1 × 3]>   NA                             
##  6   1263.  <df [1 × 3]>   NA                             
##  7     83.3 <df [1 × 3]>   NA                             
##  8  24007.  <df [1 × 3]>   NA                             
##  9    918.  <df [1 × 3]>   NA                             
## 10   4627.  <df [1 × 3]>   NA                             
##    $menu_url                                                 
##    <chr>                                                     
##  1 https://platoonfitness.com/group-classes-on-the-main-line/
##  2 <NA>                                                      
##  3 <NA>                                                      
##  4 <NA>                                                      
##  5 <NA>                                                      
##  6 <NA>                                                      
##  7 <NA>                                                      
##  8 <NA>                                                      
##  9 <NA>                                                      
## 10 https://sugarspacewellness.com/services/                  
##    $waitlist_reservation $open24_hours
##    <lgl>                 <lgl>        
##  1 NA                    NA           
##  2 NA                    NA           
##  3 NA                    NA           
##  4 NA                    NA           
##  5 NA                    NA           
##  6 NA                    NA           
##  7 NA                    NA           
##  8 FALSE                 NA           
##  9 NA                    NA           
## 10 NA                    NA           
## # ℹ 944 more rows
saveRDS(yelp_all, here('yelp_data.rds'))
# Extract coordinates
yelp_sf <- yelp_all %>% 
  mutate(x = .$coordinates$longitude,
         y = .$coordinates$latitude) %>% 
  filter(!is.na(x) & !is.na(y)) %>% 
  st_as_sf(coords = c("x", "y"), crs = 4326)

# Map
tm_shape(yelp_sf) +
  tm_dots(col = "review_count", style="pretty")

Which city did you choose? The city I chose for the assignment is Camnden,New Jersey which is the center of the Camden County and has a population of 70996 as per the census in 2022.

How many businesses are there in total? The businesses selected for this analysis comprise Restaurants and Fitness Studios, which encompass subcategories such as yoga studios, general fitness centers, and martial arts studios. The total number of businesses, spanning both restaurant and fitness sectors, amounts to 3240 within the designated area.

How many businesses are there for each business category? The number of restaurant businesses in the studied area are=2286 The number of fitness businesses in the studied area are=954

Upon visual inspection, can you see any noticeable spatial patterns to the way they are distributed across the city (e.g., clustering of businesses at some parts of the city)? A spatial analysis of the distribution of restaurants and fitness studios in Camden reveals a high concentration along both banks of the Delaware River, which serves as a central axis for business activity. These areas, characterized by higher population density and increased foot traffic, present more economically viable locations for such establishments. Strategically situating fitness studios and restaurants in these high-demand zones aligns with patterns of urban growth and consumer behavior, optimizing accessibility and revenue potential.

Are there any other interesting findings? As anticipated in any metropolitan setting, the riverfront area exhibits the highest concentration of service-oriented businesses, including restaurants, fitness studios, and gyms. This can be attributed to several factors: the elevated population density in these regions, the aesthetic appeal that enhances the attractiveness of service-based industries, and the superior accessibility provided by diverse transportation options. The combination of these elements makes riverfront areas a strategic hub for businesses that rely on high foot traffic and consumer engagement.