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.