Las cafeterías de Buenos Aires forman parte de la cultura de la ciudad, de las costumbres de sus habitantes y su circuito turístico. Han sido objeto de estudio de distintos escritores, inspiradores de innumerables creaciones artísticas y tradicional encuentro de unión de los porteños. En este proyecto, la idea es encontrar una ubicación óptima para una nueva cafetería, basados en algoritmos de aprendizaje automático tomados del curso "The Battle of Neighborhoods: Coursera Capstone Project" (1). Partiendo de que existe una asociación de las cafeterías con los restaurantes, primero intentaremos detectar ubicaciones en función de la definición de factores que influirán en nuestra decisión:
1- Lugares que aún no están llenos de restaurantes.
2- Áreas con poco o ninguna cafetería cercana.
3- Cerca del centro, si es posible, suponiendo que se cumplan las dos primeras condiciones.
Con estos sencillos parámetros vamos a programar un algoritmo para descubrir que soluciones se pueden obtener.
Se necesitarán las siguientes fuentes de datos para extraer y generar la información requerida:
1.- Los centros de las áreas candidatas serán generados automáticamente siguiendo el algoritmo y las direcciones aproximadas de los centros de estas áreas se obtendrán usando uno de los paquetes de Geopy Geocoders. (2) 2-El número de restaurantes, su tipo y ubicación en cada vecindario se obtendrá utilizando la API de Foursquare. (3)
Los datos se utilizarán en los siguientes escenarios:
1- Para descubrir la densidad de todos los restaurantes y las cafeterías a partir de los datos extraídos.
2- Para identificar áreas que no están muy densas y no son muy competitivas.
3- Para calcular las distancias entre restaurantes competidores.
El área objetivo será el centro de la ciudad, donde las atracciones turísticas son + numerosas en comparación con otros lugares. A partir de ello vamos a crear una cuadrícula de celdas que cubra el área de interés la cual sera unos 12x12 Kilómetros centrados alrededor del centro de la ciudad de Buenos Aires.
import requests
from geopy.geocoders import Nominatim
address = 'Sarmiento 999, Buenos Aires, Argentina'
geolocator = Nominatim(user_agent="lyon_explorer")
location = geolocator.geocode(address)
lat = location.latitude
lng = location.longitude
caba_center = [lat, lng]
print('Coordinate of {}: {}'.format(address, lyon_center), ' location : ', location)
Creamos una cuadrícula de las áreas candidatas equidistantes, centradas alrededor del centro de la ciudad y que sea 6 km alrededor de este punto, para ello calculamos las distancias que necesitamos para crear nuestra cuadrícula de ubicaciones en un sistema de coordenadas cartesianas 2D que nos permitirá luego calcular distancias en metros.
A continuación, proyectaremos estas coordenadas en grados de latitud / longitud para que se muestren en los mapas con Mapbox y Folium (3).
#!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('Verificación de las Coordenadas')
print('-------------------------------')
print('Centro de Buenos Aires longitud={}, latitud={}'.format(lyon_center[1], lyon_center[0]))
x, y = lonlat_to_xy(lyon_center[1], lyon_center[0])
print('Centro de Buenos Aires UTM X={}, Y={}'.format(x, y))
lo, la = xy_to_lonlat(x, y)
print('Centro de Buenos Aires longitud={}, latitud={}'.format(lo, la))
Creamos una cuadrícula hexagonal de celdas: desplazamos todas las líneas y ajustamos el espaciado de las líneas verticales para que cada centro de celda sea equidistante de todos sus vecinos.
caba_center_x, caba_center_y = lonlat_to_xy(caba_center[1], caba_center[0]) # City center in Cartesian coordinates
k = math.sqrt(3) / 2 # Vertical offset for hexagonal grid cells
x_min = caba_center_x - 6000
x_step = 600
y_min = caba_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(caba_center_x, caba_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), 'Grilla del centro de la Ciudad de Buenos Aires - CABA')
Veamos los datos que tenemos hasta ahora: ubicación en el centro y los centros vecinales candidatos:
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_caba = folium.Map(location=caba_center, zoom_start=14, tiles=tileset, attr=attribution)
folium.Marker(caba_center, popup='Ciudad de Buenos Aires').add_to(map_caba)
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=200, color='purple', fill=False).add_to(map_caba)
#folium.Marker([lat, lon]).add_to(map_caba)
map_caba
En este punto, ahora tenemos las coordenadas de los centros / áreas locales a evaluar, a la misma distancia (la distancia entre cada punto y sus vecinos es exactamente la misma) y aproximadamente a 4 km del centro de Buenos Aires.
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(caba_center[0], caba_center[1], addr))
print(type(location[0]))
print('Obteniendo Ubicaciones: ', end='')
addresses = []
for lat, lon in zip(latitudes, longitudes):
address = get_address(lat, lon)
if address is None:
address = 'NO ADDRESS'
address = address.replace(', Argentina', '')
addresses.append(address)
print(' .', end='')
print(' done.')
import pandas as pd
df_locations = pd.DataFrame({'Dirección': addresses,
'Latitud': latitudes,
'Longitud': longitudes,
'X': xs,
'Y': ys,
'Distancia desde el centroide': distances_from_center})
df_locations.head()
df_locations.shape
df_locations.to_pickle('./Dataset/caba_locations.pkl')
Ahora vamos a utilizar el API de Foursquare para explorar la cantidad de restaurantes disponibles dentro de estas cuadriculas y limitaremos la búsqueda a las categorías de alimentos para recuperar los datos de latitud y longitud de los restaurantes y de las cafeterías.
client_id = 'xxxxx'
client_secret = 'xxxxx'
VERSION = 'xxxxx'
Utilizamos la API de Foursquare para explorar la cantidad de restaurantes disponibles dentro de los 4 km del centro de Buenos Aires y limitar la búsqueda a todos los locales asociados con la categoría de restaurantes y especialmente aquellos que corresponden a las Cafeterías.
food_category = '4d4b7105d754a06374d81259' # 'Food' Catégorie de restaurant
caba_cafe_categories = ['4bf58dd8d48988d143941735', '4bf58dd8d48988d128941735', '4bf58dd8d48988d16d941735',
'4bf58dd8d48988d1e0931735', '4bf58dd8d48988d147941735'] # 'Food' Catégorie de restaurants cafe
def is_restaurant(categories, specific_filter=None):
restaurant_words = ['restaurant']
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 'Restaurante' 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(', Argentina', '')
address = address.replace(', Argentina', '')
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 = {}
caba_cafe = {}
location_restaurants = []
print('Obtención de los candidatos', 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_cafe = is_restaurant(venue_categories, specific_filter=caba_cafe_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_cafe, x, y)
if venue_distance<=300:
area_restaurants.append(restaurant)
restaurants[venue_id] = restaurant
if is_cafe:
caba_cafe[venue_id] = restaurant
location_restaurants.append(area_restaurants)
print(' .', end='')
print(' done.')
return restaurants, caba_cafe, location_restaurants
# Essayez de charger à partir du système de fichiers local au cas où nous l'avions déjà fait
restaurants = {}
caba_cafe = {}
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/caba_cafe_350.pkl', 'rb') as f:
caba_cafe = pickle.load(f)
print('Descargando Datos de las Cafeterías')
with open('/Dataset/location_restaurants_350.pkl', 'rb') as f:
location_restaurants = pickle.load(f)
print('Descargando datos de Restaurantes de la Ciudad de Buenos Aires')
loaded = True
except:
print('Datos de Restaurantes Descargandose')
pass
# Si le chargement échoue, utilisez l'API Foursquare pour obtenir les données.
if not loaded:
restaurants, caba_cafe, location_restaurants = get_restaurants(latitudes, longitudes)
print('**Resultados**',)
print('Número Total de Restaurantes:', len(restaurants))
print('Número Total de Cafeterías:', len(caba_cafe))
print('Porcentaje de Cafeterías: {:.2f}%'.format(len(caba_cafe) / len(restaurants) * 100))
print('Promedio de locales por cuadrícula:', np.array([len(r) for r in location_restaurants]).mean())
print('Lista de Todos los Restaurantes')
print('-----------------------')
for r in list(restaurants.values())[:10]:
print(r)
print('...')
print('Total:', len(restaurants))
print('Lista de todas las Cafeterías')
print('---------------------------')
for r in list(caba_cafe.values())[:10]:
print(r)
print('...')
print('Total:', len(caba_cafe))
print('Restaurantes de Autor')
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))
Todos los restaurantes en la ciudad de Buenos Aires están indicados en gris y aquellos asociados a cafeterías los vamos a resaltar en rojo.
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_caba = folium.Map(location=caba_center, zoom_start=13, tiles=tileset, attr=attribution)
folium.Marker(caba_center, popup='Buenos Aires').add_to(map_caba)
for res in restaurants.values():
lat = res[2]; lon = res[3]
is_cafe = res[6]
color = 'red' if is_cafe else 'grey'
folium.CircleMarker([lat, lon], radius=3, color=color, fill=True, fill_color=color, fill_opacity=1).add_to(map_caba)
map_caba
Vamos a explicar los datos y la información adicional de ellos en bruto. número de restaurantes en cada área candidata:
location_restaurants_count = [len(res) for res in location_restaurants]
df_locations['Restaurantes en las zonas'] = location_restaurants_count
print('Promedio de restaurantes desde el centro de Buenos Aires a un rango de 300m:', np.array(location_restaurants_count).mean())
df_locations.head(10)
Ahora calculamos la distancia de la cafetería + cercana a cada cuadricula (no sólo aquellos ubicados a menos de 300 m de distancia, ya que deseamos conocer tambien la distancia al centro + cercano.
distances_to_caba_cafe = []
for area_x, area_y in zip(xs, ys):
min_distance = 100
for res in caba_cafe.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_caba_cafe.append(min_distance)
df_locations['Distancias a la Cafetería'] = distances_to_caba_cafe
df_locations.head(10)
print('Distancia promedio en metros desde la Cafetería + cercana a cada centro:', df_locations['Distancias a la Cafetería'].mean())
Utilizamos HeatMap con Mapbox para visualizar la densidad de restaurantes en el radio seleccionado desde el centro de Buenos Aires.
restaurant_latlons = [[res[2], res[3]] for res in restaurants.values()]
cafe_latlons = [[res[2], res[3]] for res in caba_cafe.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_caba = folium.Map(location=caba_center, zoom_start=13, tiles=tileset, attr=attribution)
HeatMap(restaurant_latlons).add_to(map_caba)
folium.Marker(caba_center).add_to(map_caba)
folium.Circle(caba_center, radius=1000, fill=False, color='white').add_to(map_caba)
folium.Circle(caba_center, radius=2000, fill=False, color='blue').add_to(map_caba)
folium.Circle(caba_center, radius=3000, fill=False, color='red').add_to(map_caba)
map_lyon
Ahora presentamos otra visualización con un Heatmap de sólo las cafeterías
map_caba = folium.Map(location=caba_center, zoom_start=13, tiles=tileset, attr=attribution)
HeatMap(cafe_latlons).add_to(map_caba)
folium.Marker(caba_center).add_to(map_caba)
folium.Circle(caba_center, radius=1000, fill=False, color='white').add_to(map_caba)
folium.Circle(caba_center, radius=2000, fill=False, color='blue').add_to(map_caba)
folium.Circle(caba_center, radius=3000, fill=False, color='red').add_to(map_caba)
map_caba
De los mapas anteriores, se descubrimos que la mayoría de los restaurantes están dispersos en el lado norte del centro del área en estudio. Vamos a enfocarnos en las áreas con una menor densidad para localizar los candidatos.
roi_x_min = caba_center_x - 2000
roi_y_max = caba_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_caba = folium.Map(location=caba_center, zoom_start=13, tiles=tileset, attr=attribution)
HeatMap(restaurant_latlons).add_to(map_caba)
folium.Marker(caba_center).add_to(map_caba)
folium.Circle(roi_center, radius=2500, color='white', fill=True, fill_opacity=0.4).add_to(map_caba)
map_caba
Construimos nuevamente una grilla para ubicar los candidatos y las principales atracciones turísticas.
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), 'Localidades con posibles candidatos.')
Calculamos dos cosas + importantes para cada ubicación candidata: el número de restaurantes cercanos (utilizaremos un radio de 250 metros) y la distancia a la cafetería + cercana.
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_cafe_distances = []
print('Generando los datos de los posibles candidatos... ', 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, caba_cafe)
roi_cafe_distances.append(distance)
print('done.')
df_roi_locations = pd.DataFrame({'Latitud':roi_latitudes,
'Longitud':roi_longitudes,
'X':roi_xs,
'Y':roi_ys,
'Restaurantes Cercanos':roi_restaurant_counts,
'Distancia a las Cafeterías cercanas':roi_cafe_distances})
df_roi_locations.sort_values(by=['Restaurantes Cercanos'], ascending=False, inplace=True)
df_roi_locations.head(5)
df_roi_locations.shape
Ahora vamos a filtrar estos lugares: sólo estamos interesados en ubicaciones con no + de dos restaurantes dentro de un radio de 250 metros y sin nunguna cafetería dentro de un perímetro de 400 metros.
good_res_count = np.array((df_roi_locations['Restaurantes Cercanos']<=2))
print('Lugares con no + de dos restaurantes cercanos:', good_res_count.sum())
good_ind_distance = np.array(df_roi_locations['Distancia a las Cafeterías cercanas']>=400)
print('Cuadriculas sin Cafeterías a menos de 400 m.:', good_ind_distance.sum())
good_locations = np.logical_and(good_res_count, good_ind_distance)
print('Lugares con ambas condiciones cumplidas:', good_locations.sum())
df_good_locations = df_roi_locations[good_locations]
good_latitudes = df_good_locations['Latitud'].values
good_longitudes = df_good_locations['Longitud'].values
good_locations = [[lat, lon] for lat, lon in zip(good_latitudes, good_longitudes)]
map_caba = folium.Map(location=caba_center, zoom_start=14, tiles=tileset, attr=attribution)
HeatMap(restaurant_latlons).add_to(map_caba)
folium.Circle(roi_center, radius=2500, color='white', fill=True, fill_opacity=0.6).add_to(map_lyon)
folium.Marker(caba_center).add_to(map_caba)
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_caba)
map_caba
map_caba = folium.Map(location=caba_center, zoom_start=14, tiles=tileset, attr=attribution)
HeatMap(good_locations, radius=25).add_to(map_caba)
folium.Marker(caba_center).add_to(map_caba)
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_caba)
map_caba
Ahora vamos agrupar estas ubicaciones utilizando un algoritmo de machine learning en este caso K-medias para crear 6 grupos que contengan buenas ubicaciones. Estas áreas, sus centros y direcciones serán el resultado final de nuestro análisis.
from sklearn.cluster import KMeans
number_of_clusters = 6
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_caba = folium.Map(location=caba_center, zoom_start=14, tiles=tileset, attr=attribution)
HeatMap(restaurant_latlons).add_to(map_caba)
folium.Circle(roi_center, radius=2500, color='white', fill=True, fill_opacity=0.4).add_to(map_caba)
folium.Marker(caba_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_caba)
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_caba)
map_caba
Miremos estas áreas al oeste y al sur de la ciudad con un Heatmap, usando áreas sombreadas para indicar los 6 grupos creados:
map_caba = folium.Map(location=caba_center, zoom_start=14, tiles=tileset, attr=attribution)
folium.Marker(caba_center).add_to(map_caba)
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_caba)
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_caba)
for lon, lat in cluster_centers:
folium.Circle([lat, lon], radius=500, color='white', fill=False).add_to(map_caba)
map_caba
Ahora vamos a listar las ubicaciones candidatas
candidate_area_addresses = []
print('==============================================================')
print('Direcciones de las localizaciones recomendadas')
print('==============================================================\n')
for lon, lat in cluster_centers:
addr = get_address(lat, lon)
addr = addr.replace(', Argentina', '')
addr = addr.replace(', Buenos Aires', '')
addr = addr.replace(', AR', '')
addr = addr.replace(', BA', '')
addr = addr.replace("'", '')
candidate_area_addresses.append(addr)
x, y = lonlat_to_xy(lon, lat)
d = calc_xy_distance(x, y, caba_center_x, caba_center_y)
print('{}{} => {:.1f}km del centro la ciudad de Buenos Aires'.format(addr, ' '*(50-len(addr)), d/1000))
map_caba = folium.Map(location=caba_center, zoom_start=14, tiles=tileset, attr=attribution)
folium.Circle(caba_center, radius=50, color='red', fill=True, fill_color='red', fill_opacity=1).add_to(map_caba)
for lonlat, addr in zip(cluster_centers, candidate_area_addresses):
folium.Marker([lonlat[1], lonlat[0]], popup=addr).add_to(map_caba)
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_caba)
map_caba
Las ubicaciones anteriores están bastante cerca del centro de Buenos Aires y cada una de estas ubicaciones no tiene + de dos restaurantes en un radio de 250 m ninguna cafetería a 400 m. Cualquiera de estos establecimientos es un candidato potencial para la nueva cafetería, al menos considerando la competencia cercana. El algoritmo de aprendizaje no supervisado K-medias nos ha permitido agrupar las 15 ubicaciones con una elección adecuada para que las partes interesadas elijan entre los resultados que se presentan a continuación.
El objetivo de este proyecto era identificar las áreas de Buenos Aires cercanas al centro, con una pequeña cantidad de restaurantes (especialmente cafeterías) para ayudar a las partes interesadas a reducir la búsqueda de una ubicación óptima para una nueva cafetería.
Al calcular la distribución de la densidad de los restaurantes a partir de los datos del API Foursquare, es posible generar una gran colección de ubicaciones que cumplen ciertos requisitos básicos.
Luego se realizó una agrupación de estos datos utilizando algoritmos de machine learning (K-medias) para crear las principales áreas de interés (que contienen el mayor número de ubicaciones potenciales) y se crearon las direcciones de estos centros de área. Desde esta interpretación podemos tener un punto de partida para la exploración final por parte de los interesados.
Las partes interesadas tomarán la decisión final sobre la ubicación óptima de los restaurantes en función de las características y ubicaciones específicas del vecindario en cada área recomendada, teniendo en cuenta factores adicionales como el atractivo de cada ubicación (proximidad a un parque o agua), niveles de ruido / carreteras principales. disponibilidad inmobiliaria, precio, dinámica social y económica de cada barrio, etc.
Finalmente, un análisis + completo y un trabajo futuro deberían integrar datos de otras fuentes externas.