L’une des traditions les plus réputées de Lyon est sans doute sa gastronomie. Depuis 1935 et grâce à Curnonsky, célèbre critique culinaire, la ville porte le titre de « capitale mondiale de la gastronomie ». Dès le XIXe siècle, quand on vient à Lyon, on veut « manger » : d’abord chez les Mères, ces cuisinières d’excellence qui ont contribué à faire de la cuisine lyonnaise une véritable institution puis le paysage gastronomique lyonnais s’est diversifié et s’ouvre dorénavant à de nouvelles tendances (1), il convient donc d’étudier les possibilités offertes par les emplacements de nouveaux restaurants français.
Dans ce projet, l’idée est de trouver un emplacement optimal pour un restaurant de spécialités françaises à Lyon, basé sur des algorithmes d'apprentissage automatiques tirés du cours "The Battle of Neighborhoods: Coursera Capstone Project" (2).
Comme il y a beaucoup de restaurants à Lyon, nous allons essayer de détecter les emplacements en fonction de la définition de facteurs qui vont influencer notre décision :
1- Les endroits qui ne sont pas déjà bondés de restaurants.
2- Les zones avec peu ou pas de restaurants français à proximité.
3- Proches si possible du centre-ville, en supposant que les deux premières conditions soient remplies.
Les sources de données suivantes seront nécessaires pour extraire / générer les informations requises:
1.- Les centres des zones candidates seront générés par algorithme et les adresses approximatives des centres de ces zones seront obtenues à l'aide de l'un des paquetages Geopy Geocoders. (3)
2-Le nombre de restaurants, leur type et leur emplacement dans chaque quartier seront obtenus à l'aide de l'API Foursquare. (4)
Les données seront utilisées dans les scénarios suivants:
1- pour peupler la densité de tous les restaurants et restaurants français à partir des données extraites.
2- pour identifier les zones peu encombrées et peu compétitives
3- pour calculer les distances entre les restaurants concurrents.
La zone cible sera celle qui se trouve vers le centre-ville, où les attractions touristiques sont plus nombreuses comparées aux autres lieux. Créez une grille de cellules couvrant la zone d’intérêt qui est approximativement. Kilomètres 12x12 centrés autour du centre-ville de Lyon.
import requests
from geopy.geocoders import Nominatim
address = '10 rue Marc Antoine Petit, 69002 Lyon, France'
geolocator = Nominatim(user_agent="lyon_explorer")
location = geolocator.geocode(address)
lat = location.latitude
lng = location.longitude
lyon_center = [lat, lng]
print('Coordinate of {}: {}'.format(address, lyon_center), ' location : ', location)
Nous créons une grille de zones candidates équidistantes, centrées autour du centre-ville et à environ 6 km de Lyon et calculons les distances dont nous avons besoin pour créer notre grille d'emplacements dans un système de coordonnées cartésiennes 2D qui nous permet de calculer des distances en mètres.
Ensuite, nous projetterons ces coordonnées en degrés de latitude / longitude à afficher sur les cartes avec Mapbox et Folium (5).
#!pip install shapely
import shapely.geometry
#!pip install pyproj
import pyproj
import math
def lonlat_to_xy(lon, lat):
proj_latlon = pyproj.Proj(proj='latlong',datum='WGS84')
proj_xy = pyproj.Proj(proj="utm", zone=33, datum='WGS84')
xy = pyproj.transform(proj_latlon, proj_xy, lon, lat)
return xy[0], xy[1]
def xy_to_lonlat(x, y):
proj_latlon = pyproj.Proj(proj='latlong',datum='WGS84')
proj_xy = pyproj.Proj(proj="utm", zone=33, datum='WGS84')
lonlat = pyproj.transform(proj_xy, proj_latlon, x, y)
return lonlat[0], lonlat[1]
def calc_xy_distance(x1, y1, x2, y2):
dx = x2 - x1
dy = y2 - y1
return math.sqrt(dx*dx + dy*dy)
print('Vérification de la transformation de coordonnées')
print('-------------------------------')
print('Lyon center longitude={}, latitude={}'.format(lyon_center[1], lyon_center[0]))
x, y = lonlat_to_xy(lyon_center[1], lyon_center[0])
print('Lyon center UTM X={}, Y={}'.format(x, y))
lo, la = xy_to_lonlat(x, y)
print('Lyon center longitude={}, latitude={}'.format(lo, la))
Créons une grille hexagonale de cellules: nous décalons toutes les lignes et ajustons l'espacement des lignes verticales de manière à ce que chaque centre de cellule soit à égale distance de tous ses voisins.
lyon_center_x, lyon_center_y = lonlat_to_xy(lyon_center[1], lyon_center[0]) # City center in Cartesian coordinates
k = math.sqrt(3) / 2 # Vertical offset for hexagonal grid cells
x_min = lyon_center_x - 6000
x_step = 600
y_min = lyon_center_y - 6000 - (int(21/k)*k*600 - 12000)/2
y_step = 600 * k
latitudes = []
longitudes = []
distances_from_center = []
xs = []
ys = []
for i in range(0, int(21/k)):
y = y_min + i * y_step
x_offset = 300 if i%2==0 else 0
for j in range(0, 21):
x = x_min + j * x_step + x_offset
distance_from_center = calc_xy_distance(lyon_center_x, lyon_center_y, x, y)
if (distance_from_center <= 6001):
lon, lat = xy_to_lonlat(x, y)
latitudes.append(lat)
longitudes.append(lon)
distances_from_center.append(distance_from_center)
xs.append(x)
ys.append(y)
print(len(latitudes), 'Centres de quartier candidats générés.')
Visualisons les données dont nous disposons jusqu’à présent: emplacement du centre-ville et centres de quartier candidats:
import folium
tileset = r'https://api.mapbox.com'
attribution = (r'Map data © <a href="http://openstreetmap.org">OpenStreetMap</a>'
' contributors, Imagery © <a href="http://mapbox.com">MapBox</a>')
map_lyon = folium.Map(location=lyon_center, zoom_start=12, tiles=tileset, attr=attribution)
folium.Marker(lyon_center, popup='City of Lyon').add_to(map_lyon)
for lat, lon in zip(latitudes, longitudes):
#folium.CircleMarker([lat, lon], radius=2, color='blue', fill=True, fill_color='blue', fill_opacity=1).add_to(map_lyon)
folium.Circle([lat, lon], radius=300, color='purple', fill=False).add_to(map_lyon)
#folium.Marker([lat, lon]).add_to(map_lyon)
map_lyon
A ce stade, nous avons maintenant les coordonnées des centres de quartiers / zones à évaluer, à égale distance (la distance entre chaque point et ses voisins est exactement la même) et à environ 6 km de Lyon.
def get_address(lat, lng):
#print('entering get address')
try:
#address = '{},{}'.format(lat, lng)
address = [lat, lng]
geolocator = Nominatim(user_agent="lyon_explorer")
location = geolocator.geocode(address)
#print(location[0])
return location[0]
except:
return 'nothing found'
addr = get_address(lyon_center[0], lyon_center[1])
print('Reverse geocoding check')
print('-----------------------')
print('Address of [{}, {}] is: {}'.format(lyon_center[0], lyon_center[1], addr))
print(type(location[0]))
print('Obtaining location addresses: ', end='')
addresses = []
for lat, lon in zip(latitudes, longitudes):
address = get_address(lat, lon)
if address is None:
address = 'NO ADDRESS'
address = address.replace(', France', '') # We don't need country part of address
addresses.append(address)
print(' .', end='')
print(' done.')
import pandas as pd
df_locations = pd.DataFrame({'Address': addresses,
'Latitude': latitudes,
'Longitude': longitudes,
'X': xs,
'Y': ys,
'Distance from center': distances_from_center})
df_locations.head()
df_locations.shape
df_locations.to_pickle('./Dataset/lyon_locations.pkl')
Nous avons utilisé l’API Foursquare pour explorer le nombre de restaurants disponibles dans un rayon de 300 m du centre de Lyon et nous avons limité la recherche aux catégories d’aliments afin de récupérer les résultats des restaurants, ainsi que leur latitude et leur longitude.
client_id = 'xxxxxx'
client_secret = 'xxxx'
VERSION = 'xxxxx'
Nous utilisons l’API Foursquare pour explorer le nombre de restaurants disponibles à moins de 6 km du centre de Lyon et limitons la recherche à tous les locaux associés à la catégorie de restaurants et plus particulièrement ceux qui correspondent à la cuisine française (Brasserie,Bouchons, Estaminet, etc).
food_category = '4d4b7105d754a06374d81259' # 'Food' Catégorie de restaurant
french_restaurant_categories = ['52e81612bcbc57f1066b79f1', '52e81612bcbc57f1066b79f4', '4bf58dd8d48988d16c941735',
'52e81612bcbc57f1066b79f2', '52e81612bcbc57f1066b7a09', '4bf58dd8d48988d120951735',
'4bf58dd8d48988d10c941735', '57558b36e4b065ecebd306b6', '57558b36e4b065ecebd306b8',
'57558b36e4b065ecebd306bc', '57558b36e4b065ecebd306b0','57558b36e4b065ecebd306c5',
'57558b36e4b065ecebd306c0', '57558b36e4b065ecebd306cb', '57558b36e4b065ecebd306ce',
'57558b36e4b065ecebd306d1', '57558b36e4b065ecebd306b4', '57558b36e4b065ecebd306b2',
'57558b35e4b065ecebd306ad', '57558b36e4b065ecebd306d4', '57558b36e4b065ecebd306d7',
'57558b36e4b065ecebd306da', '57558b36e4b065ecebd306ba', '4bf58dd8d48988d155941735'] # 'Food' Catégorie de restaurants français
def is_restaurant(categories, specific_filter=None):
restaurant_words = ['bouchons', 'brasserie', 'restaurant', 'café', 'lyonesse']
restaurant = False
specific = False
for c in categories:
category_name = c[0].lower()
category_id = c[1]
for r in restaurant_words:
if r in category_name:
restaurant = True
if 'fast food' in category_name:
restaurant = False
if not(specific_filter is None) and (category_id in specific_filter):
specific = True
restaurant = True
return restaurant, specific
def get_categories(categories):
return [(cat['name'], cat['id']) for cat in categories]
def format_address(location):
address = ', '.join(location['formattedAddress'])
address = address.replace(', France', '')
address = address.replace(', France', '')
return address
def get_venues_near_location(lat, lon, category, client_id, client_secret, radius=500, limit=1000):
version = '20180724'
url = 'https://api.foursquare.com/v2/venues/explore?client_id={}&client_secret={}&v={}&ll={},{}&categoryId={}&radius={}&limit={}'.format(
client_id, client_secret, version, lat, lon, category, radius, limit)
try:
results = requests.get(url).json()['response']['groups'][0]['items']
venues = [(item['venue']['id'],
item['venue']['name'],
get_categories(item['venue']['categories']),
(item['venue']['location']['lat'], item['venue']['location']['lng']),
format_address(item['venue']['location']),
item['venue']['location']['distance']) for item in results]
except:
venues = []
return venues
# Passons maintenant aux emplacements de nos quartiers et trouvons des restaurants à proximité. nous allons également maintenir un dictionnaire de tous les restaurants trouvés et de tous les restaurants français trouvés
import pickle
def get_restaurants(lats, lons):
restaurants = {}
french_restaurants = {}
location_restaurants = []
print('Obtention de sites autour des sites candidats:', end='')
for lat, lon in zip(lats, lons):
# En utilisant rayon = 350 mts, assurez-vous que nous avons des recouvrements / une couverture complète afin que nous ne manquions aucun restaurant (nous utilisons des dictionnaires pour supprimer tout doublon résultant de chevauchements de zones).
venues = get_venues_near_location(lat, lon, food_category, client_id, client_secret, radius=350, limit=100)
area_restaurants = []
for venue in venues:
venue_id = venue[0]
venue_name = venue[1]
venue_categories = venue[2]
venue_latlon = venue[3]
venue_address = venue[4]
venue_distance = venue[5]
is_res, is_french = is_restaurant(venue_categories, specific_filter=french_restaurant_categories)
if is_res:
x, y = lonlat_to_xy(venue_latlon[1], venue_latlon[0])
restaurant = (venue_id, venue_name, venue_latlon[0], venue_latlon[1], venue_address, venue_distance, is_french, x, y)
if venue_distance<=300:
area_restaurants.append(restaurant)
restaurants[venue_id] = restaurant
if is_french:
french_restaurants[venue_id] = restaurant
location_restaurants.append(area_restaurants)
print(' .', end='')
print(' done.')
return restaurants, french_restaurants, location_restaurants
# Essayez de charger à partir du système de fichiers local au cas où nous l'avions déjà fait
restaurants = {}
french_restaurants = {}
location_restaurants = []
loaded = False
try:
with open('/Dataset/restaurants_350.pkl', 'rb') as f:
restaurants = pickle.load(f)
print('Restaurant data loaded.')
with open('/Dataset/french_restaurants_350.pkl', 'rb') as f:
french_restaurants = pickle.load(f)
print('french Restaurant data loaded.')
with open('/Dataset/location_restaurants_350.pkl', 'rb') as f:
location_restaurants = pickle.load(f)
print('location Restaurant data loaded.')
loaded = True
except:
print('Données du restaurant chargées.')
pass
# Si le chargement échoue, utilisez l'API Foursquare pour obtenir les données.
if not loaded:
restaurants, french_restaurants, location_restaurants = get_restaurants(latitudes, longitudes)
import numpy as np
print('**Les résultats**',)
print('Nombre total de Restaurants:', len(restaurants))
print('Nombre total de Restaurants Français:', len(french_restaurants))
print('Pourcentage de Restaurants Français: {:.2f}%'.format(len(french_restaurants) / len(restaurants) * 100))
print('Nombre moyen de Restaurants dans le quartier:', np.array([len(r) for r in location_restaurants]).mean())
print('Liste de tous les restaurants')
print('-----------------------')
for r in list(restaurants.values())[:10]:
print(r)
print('...')
print('Total:', len(restaurants))
print('Liste des restaurants français')
print('---------------------------')
for r in list(french_restaurants.values())[:10]:
print(r)
print('...')
print('Total:', len(french_restaurants))
print('Restaurants autour de l´emplacement')
print('---------------------------')
for i in range(100, 110):
rs = location_restaurants[i][:8]
names = ', '.join([r[1] for r in rs])
print('Restaurants around location {}: {}'.format(i+1, names))
Tous les restaurants de la ville de Lyon sont indiqués en gris et ceux associés à la cuisine française sont surlignés en rouge.
tileset = r'https://api.mapbox.com/styles/v1/roqueleal08/cjyaey84d07zq1crze5r08yg1/tiles/256/{z}/{x}/{y}@2x?access_token=pk.eyJ1Ijoicm9xdWVsZWFsMDgiLCJhIjoiY2ppZzl5NWo2MTVmMTNrcGU0enR0ZTU2MyJ9.4ZWYdzUlqvIQwwSIR50xZA'
attribution = (r'Map data © <a href="http://openstreetmap.org">OpenStreetMap</a>'
' contributors, Imagery © <a href="http://mapbox.com">MapBox</a>')
map_lyon = folium.Map(location=lyon_center, zoom_start=13, tiles=tileset, attr=attribution)
folium.Marker(lyon_center, popup='City of Lyon').add_to(map_lyon)
for res in restaurants.values():
lat = res[2]; lon = res[3]
is_french = res[6]
color = 'red' if is_french else 'grey'
folium.CircleMarker([lat, lon], radius=3, color=color, fill=True, fill_color=color, fill_opacity=1).add_to(map_lyon)
map_lyon
Analyse de données explicatives et quelques informations supplémentaires à partir de données brutes. Comptez le nombre de restaurants dans chaque zone candidate :
location_restaurants_count = [len(res) for res in location_restaurants]
df_locations['Restaurants dans la zone'] = location_restaurants_count
print('Nombre moyen de restaurants dans chaque zone avec rayon = 300m:', np.array(location_restaurants_count).mean())
df_locations.head(10)
Calculons maintenant la distance du restaurant français le plus proche de chaque centre candidat de la région (non seulement ceux situés à moins de 300 m - nous voulons une distance jusqu'au centre le plus proche, quelle que soit sa distance).
distances_to_french_restaurant = []
for area_x, area_y in zip(xs, ys):
min_distance = 100
for res in french_restaurants.values():
res_x = res[7]
res_y = res[8]
d = calc_xy_distance(area_x, area_y, res_x, res_y)
if d<min_distance:
min_distance = d
distances_to_french_restaurant.append(min_distance)
df_locations['Distance to restaurant french'] = distances_to_french_restaurant
df_locations.head(10)
print('Distance moyenne en mètres du restaurant français le plus proche de chaque centre:', df_locations['Distance to restaurant french'].mean())
Nous avons utilisé des cartes heatmaps de Folium avec Mapbox pour visualiser la densité des restaurants dans le rayon sélectionné à partir du centre de Lyon.
lyon_boroughs_url = 'https://transcode.geo.data.gouv.fr/services/5bfd5598cb932f8781e55ede/feature-types/ms:adr_voie_lieu.adrarrond?format=GeoJSON&projection=WGS84'
lyon_boroughs = requests.get(lyon_boroughs_url).json()
def boroughs_style(feature):
return { 'color': 'blue', 'fill': False }
restaurant_latlons = [[res[2], res[3]] for res in restaurants.values()]
french_latlons = [[res[2], res[3]] for res in french_restaurants.values()]
from folium import plugins
from folium.plugins import HeatMap
tileset = r'https://api.mapbox.com/styles/v1/roqueleal08/cjyaey84d07zq1crze5r08yg1/tiles/256/{z}/{x}/{y}@2x?access_token=pk.eyJ1Ijoicm9xdWVsZWFsMDgiLCJhIjoiY2ppZzl5NWo2MTVmMTNrcGU0enR0ZTU2MyJ9.4ZWYdzUlqvIQwwSIR50xZA'
attribution = (r'Map data © <a href="http://openstreetmap.org">OpenStreetMap</a>'
' contributors, Imagery © <a href="http://mapbox.com">MapBox</a>')
map_lyon = folium.Map(location=lyon_center, zoom_start=13, tiles=tileset, attr=attribution)
HeatMap(restaurant_latlons).add_to(map_lyon)
folium.Marker(lyon_center).add_to(map_lyon)
folium.Circle(lyon_center, radius=1000, fill=False, color='white').add_to(map_lyon)
folium.Circle(lyon_center, radius=2000, fill=False, color='blue').add_to(map_lyon)
folium.Circle(lyon_center, radius=3000, fill=False, color='red').add_to(map_lyon)
map_lyon
à présent, nous créons une autre carte heatmap montrant uniquement heatmap / densité des restaurants français .
map_lyon = folium.Map(location=lyon_center, zoom_start=13, tiles=tileset, attr=attribution)
HeatMap(french_latlons).add_to(map_lyon)
folium.Marker(lyon_center).add_to(map_lyon)
folium.Circle(lyon_center, radius=1000, fill=False, color='white').add_to(map_lyon)
folium.Circle(lyon_center, radius=2000, fill=False, color='blue').add_to(map_lyon)
folium.Circle(lyon_center, radius=3000, fill=False, color='red').add_to(map_lyon)
map_lyon
D'après les cartes ci-dessus, il a été constaté que la plupart des restaurants sont dispersés du côté nord du centre-ville et que leur densité est plus présente après avoir consulté la carte thermique. La ville de Lyon, qui a été désignée comme le centre et ses régions du nord, a été occupée principalement par des bureaux administratifs comptant moins de population résidentielle. La partie occidentale a un attrait touristique important mais est déjà surchargée de restaurants. Notre objectif va être la partie ouest et sud du centre de Lyon qui a une bonne population résidentielle ainsi que des attractions touristiques.
roi_x_min = lyon_center_x - 2000
roi_y_max = lyon_center_y + 1000
roi_width = 5000
roi_height = 5000
roi_center_x = roi_x_min + 1900
roi_center_y = roi_y_max - 700
roi_center_lon, roi_center_lat = xy_to_lonlat(roi_center_x, roi_center_y)
roi_center = [roi_center_lat, roi_center_lon]
map_lyon = folium.Map(location=lyon_center, zoom_start=13, tiles=tileset, attr=attribution)
HeatMap(restaurant_latlons).add_to(map_lyon)
folium.Marker(lyon_center).add_to(map_lyon)
folium.Circle(roi_center, radius=2500, color='white', fill=True, fill_opacity=0.4).add_to(map_lyon)
map_lyon
Créons également une nouvelle grille plus dense de candidats localisés limitée à notre nouvelle région d'intérêt
k = math.sqrt(3) / 2
x_step = 100
y_step = 100 * k
roi_y_min = roi_center_y - 2500
roi_latitudes = []
roi_longitudes = []
roi_xs = []
roi_ys = []
for i in range(0, int(51/k)):
y = roi_y_min + i * y_step
x_offset = 50 if i%2==0 else 0
for j in range(0, 51):
x = roi_x_min + j * x_step + x_offset
d = calc_xy_distance(roi_center_x, roi_center_y, x, y)
if (d <= 2501):
lon, lat = xy_to_lonlat(x, y)
roi_latitudes.append(lat)
roi_longitudes.append(lon)
roi_xs.append(x)
roi_ys.append(y)
print(len(roi_latitudes), 'centres de quartier candidats générés.')
Calculez deux choses les plus importantes pour chaque emplacement candidat: le nombre de restaurants à proximité (nous utiliserons un rayon de 250 mètres) et la distance jusqu'au restaurant français le plus proche.
def count_restaurants_nearby(x, y, restaurants, radius=250):
count = 0
for res in restaurants.values():
res_x = res[7]; res_y = res[8]
d = calc_xy_distance(x, y, res_x, res_y)
if d<=radius:
count += 1
return count
def find_nearest_restaurant(x, y, restaurants):
d_min = 100000
for res in restaurants.values():
res_x = res[7]; res_y = res[8]
d = calc_xy_distance(x, y, res_x, res_y)
if d<=d_min:
d_min = d
return d_min
roi_restaurant_counts = []
roi_french_distances = []
print('Generating data on location candidates... ', end='')
for x, y in zip(roi_xs, roi_ys):
count = count_restaurants_nearby(x, y, restaurants, radius=250)
roi_restaurant_counts.append(count)
distance = find_nearest_restaurant(x, y, french_restaurants)
roi_french_distances.append(distance)
print('done.')
df_roi_locations = pd.DataFrame({'Latitude':roi_latitudes,
'Longitude':roi_longitudes,
'X':roi_xs,
'Y':roi_ys,
'Restaurants nearby':roi_restaurant_counts,
'Distance to french restaurant':roi_french_distances})
df_roi_locations.sort_values(by=['Restaurants nearby'], ascending=False, inplace=True)
df_roi_locations.head(5)
df_roi_locations.shape
Laissez-nous maintenant filtrer ces lieux: nous nous intéressons uniquement aux emplacements ne comptant pas plus de deux restaurants dans un rayon de 250 mètres et aucun restaurant français à un rayon de 400 mètres .
good_res_count = np.array((df_roi_locations['Restaurants nearby']<=2))
print('Emplacements avec pas plus de deux restaurants à proximité:', good_res_count.sum())
good_ind_distance = np.array(df_roi_locations['Distance to french restaurant']>=400)
print('Emplacements sans restaurants français à moins de 400m:', good_ind_distance.sum())
good_locations = np.logical_and(good_res_count, good_ind_distance)
print('Lieux avec les deux conditions remplies:', good_locations.sum())
df_good_locations = df_roi_locations[good_locations]
good_latitudes = df_good_locations['Latitude'].values
good_longitudes = df_good_locations['Longitude'].values
good_locations = [[lat, lon] for lat, lon in zip(good_latitudes, good_longitudes)]
map_lyon = folium.Map(location=lyon_center, zoom_start=14, tiles=tileset, attr=attribution)
HeatMap(restaurant_latlons).add_to(map_lyon)
folium.Circle(roi_center, radius=2500, color='white', fill=True, fill_opacity=0.6).add_to(map_lyon)
folium.Marker(lyon_center).add_to(map_lyon)
for lat, lon in zip(good_latitudes, good_longitudes):
folium.CircleMarker([lat, lon], radius=2, color='purple', fill=True, fill_color='blue', fill_opacity=1).add_to(map_lyon)
map_lyon
map_lyon = folium.Map(location=lyon_center, zoom_start=14, tiles=tileset, attr=attribution)
HeatMap(good_locations, radius=25).add_to(map_lyon)
folium.Marker(lyon_center).add_to(map_lyon)
for lat, lon in zip(good_latitudes, good_longitudes):
folium.CircleMarker([lat, lon], radius=2, color='purple', fill=True, fill_color='blue', fill_opacity=1).add_to(map_lyon)
map_lyon
Laissez-nous maintenant Cluster ces emplacements pour créer des centres de zones contenant de bons emplacements . Ces zones, leurs centres et adresses seront le résultat final de notre analyse.
from sklearn.cluster import KMeans
number_of_clusters = 16
good_xys = df_good_locations[['X', 'Y']].values
kmeans = KMeans(n_clusters=number_of_clusters, random_state=0).fit(good_xys)
cluster_centers = [xy_to_lonlat(cc[0], cc[1]) for cc in kmeans.cluster_centers_]
map_lyon = folium.Map(location=lyon_center, zoom_start=14, tiles=tileset, attr=attribution)
HeatMap(restaurant_latlons).add_to(map_lyon)
folium.Circle(roi_center, radius=2500, color='white', fill=True, fill_opacity=0.4).add_to(map_lyon)
folium.Marker(lyon_center).add_to(map_lyon)
for lon, lat in cluster_centers:
folium.Circle([lat, lon], radius=500, color='gray', fill=True, fill_opacity=0.25).add_to(map_lyon)
for lat, lon in zip(good_latitudes, good_longitudes):
folium.CircleMarker([lat, lon], radius=2, color='purple', fill=True, fill_color='blue', fill_opacity=1).add_to(map_lyon)
map_lyon
Voyons ces zones ouest et sud sur ville avec une carte thermique, en utilisant des zones ombrées pour indiquer nos clusters:
map_lyon = folium.Map(location=lyon_center, zoom_start=14, tiles=tileset, attr=attribution)
folium.Marker(lyon_center).add_to(map_lyon)
for lat, lon in zip(good_latitudes, good_longitudes):
folium.Circle([lat, lon], radius=250, color='#00000000', fill=True, fill_color='#0066ff', fill_opacity=0.07).add_to(map_lyon)
for lat, lon in zip(good_latitudes, good_longitudes):
folium.CircleMarker([lat, lon], radius=2, color='purple', fill=True, fill_color='blue', fill_opacity=1).add_to(map_lyon)
for lon, lat in cluster_centers:
folium.Circle([lat, lon], radius=500, color='white', fill=False).add_to(map_lyon)
map_lyon
Zoomons sur les zones candidates
candidate_area_addresses = []
print('==============================================================')
print('Adresses des centres de zones recommandées')
print('==============================================================\n')
for lon, lat in cluster_centers:
addr = get_address(lat, lon)
addr = addr.replace(', France', '')
addr = addr.replace(', Lyon', '')
addr = addr.replace(', FR', '')
addr = addr.replace(', Lyon', '')
addr = addr.replace("'", '')
candidate_area_addresses.append(addr)
x, y = lonlat_to_xy(lon, lat)
d = calc_xy_distance(x, y, lyon_center_x, lyon_center_y)
print('{}{} => {:.1f}km from City of Lyon'.format(addr, ' '*(50-len(addr)), d/1000))
map_lyon = folium.Map(location=lyon_center, zoom_start=13.4, tiles=tileset, attr=attribution)
folium.Circle(lyon_center, radius=50, color='red', fill=True, fill_color='red', fill_opacity=1).add_to(map_lyon)
for lonlat, addr in zip(cluster_centers, candidate_area_addresses):
folium.Marker([lonlat[1], lonlat[0]], popup=addr).add_to(map_lyon)
for lat, lon in zip(good_latitudes, good_longitudes):
folium.Circle([lat, lon], radius=250, color='#0000ff00', fill=True, fill_color='#0066ff', fill_opacity=0.05).add_to(map_lyon)
map_lyon
Les emplacements ci-dessus sont assez proches du centre de Lyon et chacun de ces emplacements ne compte pas plus de deux restaurants dans un rayon de 250 m et aucun Restaurant français ne se trouve à moins de 400 m. N'importe lequel de ces établissements est un candidat potentiel pour un nouveau Restaurant Français, du moins en raison de la concurrence à proximité. L'algorithme K-means d'apprentissage non supervisé permet de regrouper les 15 emplacements avec un choix adéquat pour les parties prenantes à choisir parmi les résultats présentés ci-dessous.
Le but de ce projet était d'identifier les secteurs lyonnais proches du centre avec un faible nombre de restaurants (en particulier des restaurants français) afin d'aider les parties prenantes à réduire la recherche d'un emplacement optimal pour un nouveau restaurant français.
En calculant la distribution de densité de restaurants à partir de données Foursquare, il est possible de générer une vaste collection de lieux qui répondent à certaines exigences de base.
Un regroupement de ces donnés en utilisant des algorithmes d'apprentissage machineemplace,ments a ensuite été effectué afin de créer des zones d'intérêt majeures (contenant le plus grand nombre d'emplacements potentiels) et les adresses de ces centres de zone ont été créées pour servir de points de départ à l'exploration finale par les parties prenantes.
Les parties prenantes prendront la décision finale quant à l'emplacement optimal des restaurants en fonction des caractéristiques spécifiques des quartiers et des emplacements dans chaque zone recommandée, en tenant compte de facteurs supplémentaires tels que l'attractivité de chaque emplacement (proximité d'un parc ou de l'eau), des niveaux de bruit / des routes principales. , disponibilité immobilière, prix, dynamique sociale et économique de chaque quartier, etc.
Enfin, une analyse plus complète et les travaux futurs devraient intégrer des données provenant d'autres bases de données externes.