IBM DATA SCIENCE PROFESSIONAL CERTIFICATE CAPSTONE PROJECT

Introduction

In this capstone stone project, I will be Analysing Hotels in Lagos, Nigeria. In recent years, Business Relationship between Nigeria and China has been growwing with Chinese companies and expertries taking over majority of infrastructural works in Africa's biggest economy. This has led to the influx of Chinese in high proportions to Nigeria leading to demands in Chinese foods and restaurants. In this work I intend to use the foursquare location data to explore chinese restaurants in Lagos which is the commercial center of Nigeria so as to provide recommendation. This work will demostrate in details how to make calls to the Foursquare API, construct a URL to send a request to the API to search for a specific type of venues and to explore a particular venue, geographical location, and to get trending venues around a location. I will also, show how to use the visualization library, Folium, to visualize the results.

FIRST WE IMPORT ALL REQUIRED LIBRARIES

In [1]:
import requests # library to handle requests
import pandas as pd # library for data analsysis
import numpy as np # library to handle data in a vectorized manner
import random # library for random number generation

!conda install -c conda-forge geopy --yes 
from geopy.geocoders import Nominatim # module to convert an address into latitude and longitude values

# libraries for displaying images
from IPython.display import Image 
from IPython.core.display import HTML 
    
# tranforming json file into a pandas dataframe library
from pandas.io.json import json_normalize

!conda install -c conda-forge folium=0.5.0 --yes
import folium # plotting library

print('Folium installed')
print('Libraries imported.')
Collecting package metadata (current_repodata.json): ...working... done
Solving environment: ...working... done

## Package Plan ##

  environment location: C:\Users\user\Anaconda3

  added / updated specs:
    - geopy


The following packages will be downloaded:

    package                    |            build
    ---------------------------|-----------------
    geopy-1.21.0               |             py_0          58 KB  conda-forge
    ------------------------------------------------------------
                                           Total:          58 KB

The following packages will be UPDATED:

  ca-certificates      anaconda::ca-certificates-2020.1.1-0 --> conda-forge::ca-certificates-2020.4.5.1-hecc5488_0
  conda                        anaconda::conda-4.8.3-py37_0 --> conda-forge::conda-4.8.3-py37hc8dfbb8_1
  geopy                                         1.20.0-py_0 --> 1.21.0-py_0

The following packages will be SUPERSEDED by a higher-priority channel:

  certifi               anaconda::certifi-2020.4.5.1-py37_0 --> conda-forge::certifi-2020.4.5.1-py37hc8dfbb8_0



Downloading and Extracting Packages

geopy-1.21.0         | 58 KB     |            |   0% 
geopy-1.21.0         | 58 KB     | ##7        |  27% 
geopy-1.21.0         | 58 KB     | ########2  |  82% 
geopy-1.21.0         | 58 KB     | ########## | 100% 
Preparing transaction: ...working... done
Verifying transaction: ...working... done
Executing transaction: ...working... done
Collecting package metadata (current_repodata.json): ...working... done
Solving environment: ...working... done

# All requested packages already installed.

Folium installed
Libraries imported.

Define Foursquare Credentials and Version

Foursquare Login Credentials can be obtained from www.foursquare.com
In [8]:
CLIENT_ID = 'HUIXUMHELPDOY0XYRWPT3XMBZLIM54IC1TUCM4RUWKFIKMZL' # your Foursquare ID
CLIENT_SECRET = '1QYPDRIYN41TVXGFBMFIMDUG1WLFW2SEDMPOYCJCP1NOCBJ1' # your Foursquare Secret
VERSION = '20200426'
LIMIT = 30
print('Your credentails:')
print('CLIENT_ID: ' + CLIENT_ID)
print('CLIENT_SECRET:' + CLIENT_SECRET)
Your credentails:
CLIENT_ID: HUIXUMHELPDOY0XYRWPT3XMBZLIM54IC1TUCM4RUWKFIKMZL
CLIENT_SECRET:1QYPDRIYN41TVXGFBMFIMDUG1WLFW2SEDMPOYCJCP1NOCBJ1

We are Using the Federal Palace Hotel which is the biggest hotel in Lagos Nigeria as our case study. So we assume we have a guest at the Hotel and he wants to know the Chinese Restaurants close by.

In order to define an instance of the geocoder, we need to define a user_agent. We will name our agent foursquare_agent, as shown below.

In [16]:
address = 'Oshodi Rd, Oshodi-Isolo, Lagos'

geolocator = Nominatim(user_agent="foursquare_agent")
location = geolocator.geocode(address)
latitude = location.latitude
longitude = location.longitude
print(latitude, longitude)
6.5581735 3.3466245

1. Search for Specific Location

https://api.foursquare.com/v2/venues/search?client_id=CLIENT_ID&client_secret=CLIENT_SECRET&ll=LATITUDE,LONGITUDE&v=VERSION&query=QUERY&radius=RADIUS&limit=LIMIT

Now, We Assume we Have Chinese Guest at this hotel and its lunch time, and they are desirous of knowing the closest chinese restaurants nearby. So, we define a query to search for Chinese Restaurants that is within 500 metres from the Federal Palace Hotel.

In [21]:
search_query = 'Market'
radius = 1000
print(search_query + ' .... OK!')
Market .... OK!

Define the corresponding URL

In [22]:
url = 'https://api.foursquare.com/v2/venues/search?client_id={}&client_secret={}&ll={},{}&v={}&query={}&radius={}&limit={}'.format(CLIENT_ID, CLIENT_SECRET, latitude, longitude, VERSION, search_query, radius, LIMIT)
url
Out[22]:
'https://api.foursquare.com/v2/venues/search?client_id=HUIXUMHELPDOY0XYRWPT3XMBZLIM54IC1TUCM4RUWKFIKMZL&client_secret=1QYPDRIYN41TVXGFBMFIMDUG1WLFW2SEDMPOYCJCP1NOCBJ1&ll=6.5581735,3.3466245&v=20200426&query=Market&radius=1000&limit=30'

Send the GET Request and examine the results. Here is the Main deal We make calls to the Foursquare API to get the restaurants nearby

In [23]:
results = requests.get(url).json()
results
Out[23]:
{'meta': {'code': 200, 'requestId': '5ea58d2178a484001bc7b781'},
 'response': {'venues': [{'id': '4e3c30a018a83d5b28329bc3',
    'name': 'Oshodi',
    'location': {'address': 'Oshodi area',
     'lat': 6.557100301726087,
     'lng': 3.351926647809719,
     'labeledLatLngs': [{'label': 'display',
       'lat': 6.557100301726087,
       'lng': 3.351926647809719}],
     'distance': 598,
     'cc': 'NG',
     'city': 'Lagos',
     'state': 'Lagos',
     'country': 'Nigeria',
     'formattedAddress': ['Oshodi area', 'Lagos', 'Lagos', 'Nigeria']},
    'categories': [{'id': '4bf58dd8d48988d1f7941735',
      'name': 'Flea Market',
      'pluralName': 'Flea Markets',
      'shortName': 'Flea Market',
      'icon': {'prefix': 'https://ss3.4sqi.net/img/categories_v2/shops/fleamarket_',
       'suffix': '.png'},
      'primary': True}],
    'referralId': 'v-1587907961',
    'hasPerk': False}]}}

Extracting the relevant part of JSON and transform it into a pandas dataframe

In [24]:
# assign relevant part of JSON to venues
venues = results['response']['venues']

# tranform venues into a dataframe
dataframe = json_normalize(venues)
dataframe.head()
Out[24]:
categories hasPerk id location.address location.cc location.city location.country location.distance location.formattedAddress location.labeledLatLngs location.lat location.lng location.state name referralId
0 [{'id': '4bf58dd8d48988d1f7941735', 'name': 'F... False 4e3c30a018a83d5b28329bc3 Oshodi area NG Lagos Nigeria 598 [Oshodi area, Lagos, Lagos, Nigeria] [{'label': 'display', 'lat': 6.557100301726087... 6.5571 3.351927 Lagos Oshodi v-1587907961

Filtering the Information of Interest

In [25]:
# keep only columns that include venue name, and anything that is associated with location
filtered_columns = ['name', 'categories'] + [col for col in dataframe.columns if col.startswith('location.')] + ['id']
dataframe_filtered = dataframe.loc[:, filtered_columns]

# function that extracts the category of the venue
def get_category_type(row):
    try:
        categories_list = row['categories']
    except:
        categories_list = row['venue.categories']
        
    if len(categories_list) == 0:
        return None
    else:
        return categories_list[0]['name']

# filter the category for each row
dataframe_filtered['categories'] = dataframe_filtered.apply(get_category_type, axis=1)

# clean column names by keeping only last term
dataframe_filtered.columns = [column.split('.')[-1] for column in dataframe_filtered.columns]

dataframe_filtered
Out[25]:
name categories address cc city country distance formattedAddress labeledLatLngs lat lng state id
0 Oshodi Flea Market Oshodi area NG Lagos Nigeria 598 [Oshodi area, Lagos, Lagos, Nigeria] [{'label': 'display', 'lat': 6.557100301726087... 6.5571 3.351927 Lagos 4e3c30a018a83d5b28329bc3

Its Time to Visualize all the Chinese Restaurants in the neighbourhood of the Federal Palace Hotel

In [26]:
dataframe_filtered.name
Out[26]:
0    Oshodi
Name: name, dtype: object
In [27]:
venues_map = folium.Map(location=[latitude, longitude], zoom_start=13) # generate map of chinese restaurants aroung Federal Palace Hotel

# add a red circle marker to represent the Federal Palce Hotel
folium.features.CircleMarker(
    [latitude, longitude],
    radius=10,
    color='red',
    popup='Federal Palace Hotel',
    fill = True,
    fill_color = 'red',
    fill_opacity = 0.6
).add_to(venues_map)

# add the Chinese restaurants as blue circle markers
for lat, lng, label in zip(dataframe_filtered.lat, dataframe_filtered.lng, dataframe_filtered.categories):
    folium.features.CircleMarker(
        [lat, lng],
        radius=5,
        color='blue',
        popup=label,
        fill = True,
        fill_color='blue',
        fill_opacity=0.6
    ).add_to(venues_map)

# display map
venues_map
Out[27]:

2. Explore a Given Venue

https://api.foursquare.com/v2/venues/VENUE_ID?client_id=CLIENT_ID&client_secret=CLIENT_SECRET&v=VERSION

A. Let's explore the closest Italian restaurant -- Airforce Base Restaurant

In [28]:
venue_id = '4bd060aeb221c9b62b84d3d0' # ID of Airforce Baase Restaurant
url = 'https://api.foursquare.com/v2/venues/{}?client_id={}&client_secret={}&v={}'.format(venue_id, CLIENT_ID, CLIENT_SECRET, VERSION)
url
Out[28]:
'https://api.foursquare.com/v2/venues/4bd060aeb221c9b62b84d3d0?client_id=HUIXUMHELPDOY0XYRWPT3XMBZLIM54IC1TUCM4RUWKFIKMZL&client_secret=1QYPDRIYN41TVXGFBMFIMDUG1WLFW2SEDMPOYCJCP1NOCBJ1&v=20200426'

Send GET request for result

In [29]:
result = requests.get(url).json()
print(result['response']['venue'].keys())
result['response']['venue']
dict_keys(['id', 'name', 'contact', 'location', 'canonicalUrl', 'categories', 'verified', 'stats', 'price', 'likes', 'dislike', 'ok', 'allowMenuUrlEdit', 'beenHere', 'specials', 'photos', 'reasons', 'hereNow', 'createdAt', 'tips', 'shortUrl', 'timeZone', 'listed', 'seasonalHours', 'pageUpdates', 'inbox', 'attributes'])
Out[29]:
{'id': '4bd060aeb221c9b62b84d3d0',
 'name': 'Air force base restaurant',
 'contact': {},
 'location': {'address': '1 kofo abayomi street victoria island',
  'crossStreet': 'Ozumba mbadiwe street',
  'lat': 6.435896382094165,
  'lng': 3.4115885783539035,
  'labeledLatLngs': [{'label': 'display',
    'lat': 6.435896382094165,
    'lng': 3.4115885783539035}],
  'cc': 'NG',
  'city': 'Lagos',
  'state': 'Lagos',
  'country': 'Nigeria',
  'formattedAddress': ['1 kofo abayomi street victoria island (Ozumba mbadiwe street)',
   'Lagos',
   'Lagos',
   'Nigeria']},
 'canonicalUrl': 'https://foursquare.com/v/air-force-base-restaurant/4bd060aeb221c9b62b84d3d0',
 'categories': [{'id': '4bf58dd8d48988d1c8941735',
   'name': 'African Restaurant',
   'pluralName': 'African Restaurants',
   'shortName': 'African',
   'icon': {'prefix': 'https://ss3.4sqi.net/img/categories_v2/food/african_',
    'suffix': '.png'},
   'primary': True}],
 'verified': False,
 'stats': {'tipCount': 1},
 'price': {'tier': 2, 'message': 'Moderate', 'currency': '$'},
 'likes': {'count': 0, 'groups': []},
 'dislike': False,
 'ok': False,
 'allowMenuUrlEdit': True,
 'beenHere': {'count': 0,
  'unconfirmedCount': 0,
  'marked': False,
  'lastCheckinExpiredAt': 0},
 'specials': {'count': 0, 'items': []},
 'photos': {'count': 0, 'groups': []},
 'reasons': {'count': 0, 'items': []},
 'hereNow': {'count': 0, 'summary': 'Nobody here', 'groups': []},
 'createdAt': 1271947438,
 'tips': {'count': 1,
  'groups': [{'type': 'others',
    'name': 'All tips',
    'count': 1,
    'items': [{'id': '51bb120ae4b090ea9e9f6de5',
      'createdAt': 1371214346,
      'text': 'The barbeque fish is wow',
      'type': 'user',
      'canonicalUrl': 'https://foursquare.com/item/51bb120ae4b090ea9e9f6de5',
      'lang': 'en',
      'likes': {'count': 7,
       'groups': [{'type': 'others',
         'count': 7,
         'items': [{'id': '98049839',
           'firstName': 'Yakubu',
           'lastName': 'Y',
           'photo': {'prefix': 'https://fastly.4sqi.net/img/user/',
            'suffix': '/98049839-G1N2BNRB1EJSDKBK.jpg'}},
          {'id': '75797813',
           'firstName': 'Hilary Sunmon Olamilekan',
           'lastName': 'H',
           'photo': {'prefix': 'https://fastly.4sqi.net/img/user/',
            'suffix': '/75797813-QVZPBZ03DK4MWBR1.jpg'}},
          {'id': '77087986',
           'firstName': 'Gentility',
           'lastName': 'D',
           'photo': {'prefix': 'https://fastly.4sqi.net/img/user/',
            'suffix': '/77087986-44TL3U5O0SMG4QHP.jpg'}},
          {'id': '13239680',
           'firstName': 'Adams',
           'lastName': 'a',
           'photo': {'prefix': 'https://fastly.4sqi.net/img/user/',
            'suffix': '/blank_boy.png',
            'default': True}}]}],
       'summary': '7 likes'},
      'logView': True,
      'agreeCount': 7,
      'disagreeCount': 0,
      'todo': {'count': 1},
      'user': {'id': '10546132',
       'firstName': 'khadeejah',
       'photo': {'prefix': 'https://fastly.4sqi.net/img/user/',
        'suffix': '/10546132_Yxo0vwI0_N8UODRVAb91Uk9d6NmCq8YFbe17Gxpi7C81k1yg-o75o--T86EIrf2fAm17aWPhV.jpg'}}}]}]},
 'shortUrl': 'http://4sq.com/aJ4Smm',
 'timeZone': 'Africa/Lagos',
 'listed': {'count': 0,
  'groups': [{'type': 'others',
    'name': 'Lists from other people',
    'count': 0,
    'items': []}]},
 'seasonalHours': [],
 'pageUpdates': {'count': 0, 'items': []},
 'inbox': {'count': 0, 'items': []},
 'attributes': {'groups': [{'type': 'price',
    'name': 'Price',
    'summary': '$$',
    'count': 1,
    'items': [{'displayName': 'Price',
      'displayValue': '$$',
      'priceTier': 2}]}]}}

A. Getting the overall rating for the Airforce Base Restaurants

In [30]:
try:
    print(result['response']['venue']['rating'])
except:
    print('This venue has not been rated yet.')
This venue has not been rated yet.

B. Getting the overall rating for the New China Restaurant

In [31]:
venue_id = '51741aa0498efc18be344c96' # ID of New China Restaurant
url = 'https://api.foursquare.com/v2/venues/{}?client_id={}&client_secret={}&v={}'.format(venue_id, CLIENT_ID, CLIENT_SECRET, VERSION)

result = requests.get(url).json()
try:
    print(result['response']['venue']['rating'])
except:
    print('This venue has no rating yet.')
This venue has no rating yet.

C. Getting the overall rating for Saffron Restaurant

In [32]:
venue_id = '4e778af91f6e072f1497d5e' # ID of Saffron Restaurant
url = 'https://api.foursquare.com/v2/venues/{}?client_id={}&client_secret={}&v={}'.format(venue_id, CLIENT_ID, CLIENT_SECRET, VERSION)

result = requests.get(url).json()
try:
    print(result['response']['venue']['rating'])
except:
    print('This venue has no rating yet.')
This venue has no rating yet.

4. Explore a location

https://api.foursquare.com/v2/venues/explore?client_id=CLIENT_ID&client_secret=CLIENT_SECRET&ll=LATITUDE,LONGITUDE&v=VERSION&limit=LIMIT

We first define the Longitude and Latitude of the Hotel

In [40]:
latitude = 6.5581735
longitude = 3.3466245
 

Define URL

In [41]:
url = 'https://api.foursquare.com/v2/venues/explore?client_id={}&client_secret={}&ll={},{}&v={}&radius={}&limit={}'.format(CLIENT_ID, CLIENT_SECRET, latitude, longitude, VERSION, radius, LIMIT)
url
Out[41]:
'https://api.foursquare.com/v2/venues/explore?client_id=HUIXUMHELPDOY0XYRWPT3XMBZLIM54IC1TUCM4RUWKFIKMZL&client_secret=1QYPDRIYN41TVXGFBMFIMDUG1WLFW2SEDMPOYCJCP1NOCBJ1&ll=6.5581735,3.3466245&v=20200426&radius=1000&limit=30'

Send GET request and examine results

In [42]:
import requests
In [43]:
results = requests.get(url).json()
'There are {} around The Federal Palace Hotel.'.format(len(results['response']['groups'][0]['items']))
Out[43]:
'There are 5 around The Federal Palace Hotel.'

Get relevant part of JSON

In [44]:
items = results['response']['groups'][0]['items']
items[0]
Out[44]:
{'reasons': {'count': 0,
  'items': [{'summary': 'This spot is popular',
    'type': 'general',
    'reasonName': 'globalInteractionReason'}]},
 'venue': {'id': '4cdcf9274006a1435f5ce2b2',
  'name': 'Oshodi',
  'location': {'lat': 6.552606017715056,
   'lng': 3.344266121035946,
   'labeledLatLngs': [{'label': 'display',
     'lat': 6.552606017715056,
     'lng': 3.344266121035946}],
   'distance': 672,
   'cc': 'NG',
   'city': 'Lagos',
   'state': 'Lagos',
   'country': 'Nigeria',
   'formattedAddress': ['Lagos', 'Lagos', 'Nigeria']},
  'categories': [{'id': '4bf58dd8d48988d163941735',
    'name': 'Park',
    'pluralName': 'Parks',
    'shortName': 'Park',
    'icon': {'prefix': 'https://ss3.4sqi.net/img/categories_v2/parks_outdoors/park_',
     'suffix': '.png'},
    'primary': True}],
  'photos': {'count': 0, 'groups': []}},
 'referralId': 'e-0-4cdcf9274006a1435f5ce2b2-0'}

Process JSON and convert it to a clean dataframe

In [51]:
dataframe = json_normalize(items) # flatten JSON

# filter columns
filtered_columns = ['venue.name', 'venue.categories'] + [col for col in dataframe.columns if col.startswith('venue.location.')] + ['venue.id']
dataframe_filtered = dataframe.loc[:, filtered_columns]

# filter the category for each row
dataframe_filtered['venue.categories'] = dataframe_filtered.apply(get_category_type, axis=1)

# clean columns
dataframe_filtered.columns = [col.split('.')[-1] for col in dataframe_filtered.columns]

dataframe_filtered.head()
Out[51]:
name categories address cc city country distance formattedAddress labeledLatLngs lat lng postalCode state id
0 Oshodi Park NaN NG Lagos Nigeria 672 [Lagos, Lagos, Nigeria] [{'label': 'display', 'lat': 6.552606017715056... 6.552606 3.344266 NaN Lagos 4cdcf9274006a1435f5ce2b2
1 The Arena (Nigerian Army Shopping Arena) Shopping Mall Igbehin Adun Street NG Oshodi Nigeria 735 [Igbehin Adun Street, Oshodi 23401, Lagos, Nig... [{'label': 'display', 'lat': 6.564727036463472... 6.564727 3.347498 23401 Lagos 5064306ee4b0c3cdacb2e8fa
2 Sizzlers Fast Food Restaurant Church Street NG Oshodi Nigeria 743 [Church Street, Oshodi, Lagos, Nigeria] [{'label': 'display', 'lat': 6.564729900259279... 6.564730 3.347902 NaN Lagos 4ee4e5cf77c8e893188ddffd
3 Oshodi Bus Stop Bus Station NaN NG Lagos Nigeria 747 [Lagos, Lagos, Nigeria] [{'label': 'display', 'lat': 6.555838731803156... 6.555839 3.352965 NaN Lagos 4c3f7b7ee26920a14f815be7
4 Oshodi Flea Market Oshodi area NG Lagos Nigeria 598 [Oshodi area, Lagos, Lagos, Nigeria] [{'label': 'display', 'lat': 6.557100301726087... 6.557100 3.351927 NaN Lagos 4e3c30a018a83d5b28329bc3

Let's visualize these items on the map around our location

In [46]:
venues_map = folium.Map(location=[latitude, longitude], zoom_start=15) # generate map centred around the federal palace hotel


# add Ecco as a red circle mark
folium.features.CircleMarker(
    [latitude, longitude],
    radius=10,
    popup='Ecco',
    fill=True,
    color='red',
    fill_color='red',
    fill_opacity=0.6
    ).add_to(venues_map)


# add popular spots to the map as blue circle markers
for lat, lng, label in zip(dataframe_filtered.lat, dataframe_filtered.lng, dataframe_filtered.categories):
    folium.features.CircleMarker(
        [lat, lng],
        radius=5,
        popup=label,
        fill=True,
        color='blue',
        fill_color='blue',
        fill_opacity=0.6
        ).add_to(venues_map)

# display map
venues_map
Out[46]:

https://api.foursquare.com/v2/venues/trending?client_id=CLIENT_ID&client_secret=CLIENT_SECRET&ll=LATITUDE,LONGITUDE&v=VERSION

In [47]:
# define URL
url = 'https://api.foursquare.com/v2/venues/trending?client_id={}&client_secret={}&ll={},{}&v={}'.format(CLIENT_ID, CLIENT_SECRET, latitude, longitude, VERSION)

# send GET request and get trending venues
results = requests.get(url).json()
results
Out[47]:
{'meta': {'code': 200, 'requestId': '5ea58eaabe61c9001b0a8ec8'},
 'response': {'venues': []}}
In [48]:
if len(results['response']['venues']) == 0:
    trending_venues_df = 'No trending venues are available at the moment!'
    
else:
    trending_venues = results['response']['venues']
    trending_venues_df = json_normalize(trending_venues)

    # filter columns
    columns_filtered = ['name', 'categories'] + ['location.distance', 'location.city', 'location.postalCode', 'location.state', 'location.country', 'location.lat', 'location.lng']
    trending_venues_df = trending_venues_df.loc[:, columns_filtered]

    # filter the category for each row
    trending_venues_df['categories'] = trending_venues_df.apply(get_category_type, axis=1)
In [49]:
# display trending venues
trending_venues_df
Out[49]:
'No trending venues are available at the moment!'
In [50]:
# display map
venues_map
Out[50]: