Кластеризация ночной жизни города с помощью машинного обучения
Кластеризация KMeans с использованием API Foursquare
Введение
Всем известно, как пандемия Covid-19 опустошила индустрию ночной жизни социальным дистанцированием, блокировками, ношением масок и ранним комендантским часом. Эти места для ночных развлечений были закрыты, потому что они считались второстепенными услугами и местами легкой передачи коронавируса. Теперь, когда центральное правительство и правительство штата в Индии ослабили ограничения, люди, наконец, могут насладиться передышкой, отмечая особое событие или просто проводя время с друзьями за едой и напитками.
В таком городе, как Пуна, который может похвастаться бурлящей ночной жизнью, где-то всегда проходит вечеринка. Широко известный как «ИТ-центр Индии», «Автомобильный и производственный центр Индии» и «Оксфорд Востока», Пуна известна своим стилем жизни, приятной погодой и просто… всем хорошим. С увеличением количества пабов, баров, лаунж-баров и подобных тусовок ночная жизнь в Пуне стала еще более захватывающей. В Пуне много баров, клубов, пабов для пар, компаний друзей, а также полуночников.
Любому новому посетителю в Пуне может быть трудно понять, где ему следует провести вечеринку и выпить. В этой статье мы постараемся сгруппировать город в районы или пригороды с высокой концентрацией баров и пабов.
Исследование окрестностей
Для кластеризации нам потребуется список окрестностей Пуны, их широта и долгота.
Получить соседей
Википедия — отличный источник информации. Список районов Пуны был взят с помощью библиотеки Python BeautifulSoup с указанной страницы Википедии.
data = requests.get("https://en.wikipedia.org/wiki/Category:Neighbourhoods_in_Pune").text soup = BeautifulSoup(data, 'html.parser') neighbors = [] for row in soup.find_all("div", {"class": "mw-category"})[0].findAll("li"): neighbors.append(row.text) #Create a new DataFrame from the list df_neighbors = pd.DataFrame({"neighbor": neighbors}) df_neighbors = df_neighbors.drop(labels=[26,32,39, 40], axis=0) df_neighbors.reset_index(drop=True)
Получить координаты соседей
Далее, используя библиотеку geocoderpython, мы получим широту и долготу каждого района.
## Function to fetch the geo coordinates for the locations def getNeighborCoords(neighbor): lat_lng_coords = None while(lat_lng_coords is None): geo = geocoder.arcgis('{}, Pune, India'.format(neighbor)) lat_lng_coords = geo.latlng return lat_lng_coords
Используя приведенную выше функцию, мы получим координаты для каждой из соседних областей. Результирующий вывод функции отображается как новые столбцы, то есть «широта» и «долгота».
coords = [ getNeighborCoords(neighbor) for neighbor in df_neighbors["neighbor"].tolist()] ## Map the latitude and longitude retuned from the function df_neighbors['latitude'], df_neighbors['longitude'] = zip(*coords)
Наш окончательный кадр данных теперь имеет соседа и его соответствующие координаты широты и долготы.
Давайте посмотрим, как наши соседи выглядят на карте. Я использовал библиотеки geopy и folium, чтобы создать карту Пуны с координатами нашего соседа, наложенными сверху.
## center of pune pune_center = [18.5204303, 73.8567437] ## plot the neighbors on the map using Folium library map_pune = folium.Map(location=pune_center, zoom_start=11) for neighbor, lat, lon in zip(df_neighbors['neighbor'],df_neighbors['latitude'],df_neighbors['longitude']): label = '{}'.format(neighbor) label = folium.Popup(label, parse_html=True) folium.CircleMarker([lat, lon], radius=5, color='black', fill_color='blue', fill_opacity=0.25, popup=label, fill=True).add_to(map_pune) map_pune
Исследование площадок
Теперь, когда у нас есть районы Пуны с соответствующей информацией о широте и долготе, давайте получим окружающие места в пределах заданного радиуса (км).
Получить места
Foursquare API, социальная служба определения местоположения, позволяет пользователям узнавать о компаниях и достопримечательностях. Поскольку мы пытаемся найти бары, я добавил идентификатор категории 4bf58dd8d48988d116941735 как часть URL-адреса запроса API. Я определил лимит мест, возвращаемых на 100 в радиусе 1 км.
url = 'https://api.foursquare.com/v2/venues/explore?&client_id={}&client_secret={}&categoryId=4bf58dd8d48988d116941735&v={}&ll={},{}&radius={}&limit={}&offset={}'.format( CLIENT_ID, CLIENT_SECRET, VERSION, latitude, longitude, RADIUS, LIMIT, offset)
С помощью вызова API Foursquare мы получили около 168 площадок.
Инжиниринг данных
С нашей извлеченной информацией о близлежащих барах мы можем объединить ее в новый фрейм данных, содержащий информацию о наших соседях. Кроме того, из этого нового фрейма данных мы удаляем соседей, у которых нет возвращенных мест, т. е. удаляем записи NA.
df_Venues = getNearbyVenues(df_neighbors['neighbor'],df_neighbors['latitude'],df_neighbors['longitude']) df_Venues.dropna(how='any', inplace=True) df_Venues.reset_index(drop=True, inplace=True) df_Venues.head()
Кроме того, в пределах заданного радиуса 1 км существует высокая вероятность того, что API Foursquare вернет те же места для соседей, которые находятся в непосредственной близости. Давайте найдем и удалим повторяющиеся места. В этом процессе мы сохраним «первое» вхождение повторяющихся записей и удалим остальные.
df_Venues.drop_duplicates(subset=['name','categories','lat','lng'], keep='first', inplace=True)
Это привело к удалению повторяющихся площадок, и теперь у нас осталось 113 площадок. Пока отлично! Теперь давайте рассмотрим уникальные места, возвращаемые API Foursquare.
df_Venues['categories'].unique()
Вы видите, что уникальный список включает в себя все виды заведений, включая Juice Bars, Falafel Restaurants, Jazz Club, Cafe. и т. д. Хотя не уверен, что во всех этих заведениях подают алкоголь, но для простоты и лучшей группировки давайте просто сосредоточимся на нескольких известных категориях заведений.
## filter out only Bars from the fetched venues df_hotels = df_Venues[df_Venues['categories'].isin(['Bar', 'Hotel Bar','Pub', 'Cocktail Bar', Lounge','Sports Bar', 'Gastropub','Brewery','Distillery']) ] df_hotels.reset_index(drop=True, inplace=True) ## rearrange and rename the columns df_hotels.columns=['neighbor','latitude','longitude','venue','category','venue_lat','venue_lon'] df_hotels.head()
Кластеризация соседей и площадок
Имея данные о соседях и местах проведения, все задачи по обработке данных выполнены, давайте теперь приступим к кластеризации.
Сначала мы применим метод горячего кодирования к столбцу «категория»; возьмите среднюю частоту для каждой категории мест и, наконец, выполните кластеризацию K-средних, алгоритм обучения без учителя, используемый для создания кластеров K точек данных на основе сходства признаков.
## one-hot encode the category df_hotels_OHE = pd.get_dummies(df_hotels['category'], prefix='', prefix_sep='') df_hotels_OHE['neighbor'] = df_hotels['neighbor'] ## rearrange the columns with 'neighbor' as first column. fixed_columns = [df_hotels_OHE.columns[-1]] + list(df_hotels_OHE.columns[:-1]) df_hotels_OHE = df_hotels_OHE[fixed_columns] df_hotels_grouped = df_hotels_OHE.groupby('neighbor').mean().reset_index() df_hotels_grouped.head()
Используя этот сгруппированный фрейм данных, мы удалим соседний столбец и применим метод кластеризации K-средних, чтобы получить оптимальное значение «k». Глядя на метод локтя и оценку силуэта, кажется, что оптимальное количество кластеров для использования составляет K = 8.
Теперь давайте выполним фактическую кластеризацию K-средних, используя k = 8 в наборе данных, и сопоставим метки кластера с каждым соседом.
kclusters = 8 x = df_hotels_grouped.drop(['neighbor'], 1) # run k-means clustering kmeans = KMeans(n_clusters=kclusters, random_state=0).fit(x) df_hotels_merged["cluster"] = kmeans.labels_ df_clustered = pd.merge(df_hotels, df_hotels_merged[['neighbor','cluster']] ,on=["neighbor"])
Чтобы визуализировать, как формируются кластеры, давайте снова построим карту соседей и их меток кластеров с уникальным цветовым кодированием.
occurences = folium.map.FeatureGroup() map_clusters = folium.Map(location=pune_center, zoom_start=11) # Setting color scheme for the clusters x = np.arange(kclusters) ys = [i+x+(i*x)**2 for i in range(kclusters)] colors_array = cm.rainbow(np.linspace(0, 1, len(ys))) rainbow = [colors.rgb2hex(i) for i in colors_array] for lat, lon, poi, cluster in zip(df_clustered['latitude'], df_clustered['longitude'], df_clustered['neighbor'], df_clustered['cluster']): label = folium.Popup(str(poi) + ' - Cluster ' + str(cluster), parse_html=True) folium.CircleMarker([lat,lon], radius=5, popup=label, color=rainbow[cluster-1], fill=True, fill_color=rainbow[cluster-1], fill_opacity=1, icon = folium.Icon(icon='beer', prefix='fa')).add_to(map_clusters) map_clusters
Ниже представлена карта, показывающая кластеры для разных районов. Точки данных, выделенные разными цветами, представляют собой сгруппированные области.
Чтобы лучше понять, как соседство было сгруппировано, давайте визуализируем его в виде гистограммы. Как видите, имеется 8 кластеров, и на графике показаны соседние кластеры в соответствии с их количеством мест проведения.
Для дальнейшего изучения и дополнительного понимания кластеризации, вот тепловая карта кластеризованных соседей. Если вы посмотрите на район, Виманнагар показывает высокую плотность, тогда как центральные районы Пуны имеют среднюю плотность.
df_neighbor_HM = df_clustered.groupby(['neighbor','latitude','longitude'])['category'].count().reset_index() pune_latlons = df_neighbor_HM[['latitude','longitude','category']] pune_map = folium.Map(location=pune_center, zoom_start=10) folium.TileLayer('cartodbpositron').add_to(pune_map) #cartodbpositron cartodbdark_matter HeatMap(pune_latlons, blur=10).add_to(pune_map) folium.Marker(pune_center).add_to(pune_map) pune_map
Давайте также построим тепловую карту для различных мест, чтобы увидеть, как они расположены на географической карте.
pune_latlons = df_clustered[['venue_lat','venue_lon']] pune_map = folium.Map(location=pune_center, zoom_start=13) folium.TileLayer('cartodbpositron').add_to(pune_map) #cartodbpositron cartodbdark_matter HeatMap(pune_latlons, blur=20).add_to(pune_map) folium.Circle(pune_center, radius=2000, fill=False, color='white').add_to(pune_map) folium.Circle(pune_center, radius=4000, fill=False, color='white').add_to(pune_map) pune_map
Вывод
В заключение мы использовали кластеризацию K-средних, которая создала 8 различных кластеров для соседей Пуны, вокруг которых расположены ночные клубы. Вокруг Виманнагара, Кальяни-Нагара густонаселено большое количество ночных клубов (в радиусе 1 км). Эти районы находятся в непосредственной близости от ИТ-центров, и, следовательно, эти места являются хорошими источниками дохода.
Кроме того, если вы видите центральную часть Пуны, в районах кемпинга также есть множество мест для ночных вечеринок. Удивительно, но в районах Пимпри-Чинчвад, Банер, Вакад, которые находятся недалеко от парка Хинджевади (крупный IT-хаб), похоже, не так много площадок. Вероятно, это может быть из-за данных/местоположений соседей ИЛИ небольшого количества мест/мест, зарегистрированных на Foursquare.
Итак, если вы посещаете Пуну впервые ИЛИ планируете вечеринку, вы можете использовать эту карту, чтобы получить хорошее представление о том, где можно выпить.
Приятных вечеринок! Пейте ответственно 🍻 🍸 🍷
Надеюсь, вы нашли статью познавательной. Я хотел бы услышать отзывы, чтобы импровизировать и вернуться с лучшим содержанием.