В этом посте я делюсь с вами самым первым полноценным (для начинающих) проектом по науке о данных, над которым я работал в рамках моей специализации Coursera IBM по науке о данных. Обратите внимание, это был мой первый проект, так что вполне вероятно, что есть много вещей, которые можно было бы сделать лучше, улучшить или исправить, но я чувствую, что это может быть хорошей отправной точкой для тех, кто стремится окунуть свои пальцы в науку о данных. Этот проект был выполнен в блокноте Python. Чтобы избежать проблем с импортом в вашей локальной системе, вы даже можете попробовать использовать блокнот Google Colab. Без лишних слов... приступим!!!

Редактировать: я разместил этот блог более года назад, но по нескольким просьбам я недавно сделал учебник на YouTube для этого проекта, чтобы вы могли следить за ним!

https://youtu.be/D5AmAfRzgkw

1. Введение:

1.1 Предыстория

Новый Орлеан — популярное место в США с множеством известных мест, таких как переправы на пароме, кафе, рестораны морепродуктов и многое другое. Это привлекает в этот район много людей для туризма и поселения. В Новом Орлеане довольно много районов, каждый из которых имеет свою специальность и место проведения.

1.2 Бизнес-проблема

Для людей, переезжающих в совершенно новый штат/страну, часто бывает сложно найти хорошее место для поселения. Неверное решение может привести к множеству проблем. В это время было бы полезно, если бы человек имел некоторые знания о близлежащих районах, местах, модных местах и ​​т. д., которые соответствуют его вкусу, что помогло бы им в их решении найти правильный район для поселения. проект, мы заглянем в окрестности одного из самых модных мест в США, Нового Орлеана. Для человека, который совсем новичок в этом месте, будет сложно найти информацию о близлежащих объектах и ​​площадках, которые подошли бы ему лучше всего, и этот проект направлен на предоставление не только для Нового Орлеана, но и для любого места в мире. мир, описательный взгляд на каждое соседство, который поможет человеку остепениться.

1.3 Интерес

Людям, желающим переехать в местный район в новом штате/стране, было бы очень полезно иметь эти данные под рукой для выбора правильного района для поселения.

2. Сбор и очистка данных

2.1 Источник данных:

Для этого проекта я использовал данные о районах Нового Орлеана со следующей страницы Википедии: https://en.wikipedia.org/wiki/Neighborhoods_in_New_Orleans. Эта страница состоит из таблицы с районами. Нового Орлеана и их соответствующие координаты, чьи теги мы извлекаем с помощью beautisoup (библиотека Python для разбора документов HTML/XML) и сохраняем в виде таблицы, а далее в блокноте мы получаем доступ к ближайшим местам заданной координаты с помощью API Форсквера. Загрузите указанную выше страницу Википедии в виде HTML-файла и сохраните его в локальной системе/папке. Скриншот части страницы приведен ниже.

Приведенный ниже фрагмент кода импортирует все необходимые библиотеки и показывает, как использовать beautisoup для сохранения необходимых данных из html-файла в виде списка.

#importing required libraries 
import requests 
import pandas as pd 
import numpy as np 
from bs4 import BeautifulSoup 
import collections
#Accessing the HTML page and getting the table values(<td> tags)
#Data Source : https://en.wikipedia.org/wiki/Neighborhoods_in_New_Orleans


tabs = []
# File handling
with open('new_orleans_data.html', 'r',encoding="utf-8") as fp:
    html_content = fp.read()

    table_doc = BeautifulSoup(html_content, 'html.parser')
    # parsing html content
    for tr in table_doc.table.find_all('tr'):
        tabs.append(tr.find_all('td'))
#removing the Heading columns
tabs.pop(0)
print(tabs)

Ниже приведен скриншот нескольких начальных записей из списка «вкладки».

2.2 Очистка данных

Поскольку данные представляли собой HTML-теги ‹td›, мне пришлось использовать цикл для доступа к каждому из них и добавления в список, там я преобразовал их в строки, избавился от тегов ‹td› и ‹/td› и обрезал все лишнее пространство, которое могло закрасться. Затем я преобразовал окончательные данные в словарь с 3 ключами, такими как окрестности, широта и долгота, и преобразовал их в фрейм данных. Для использования координат на карте я преобразовал их из строки в число с плавающей запятой.

#Cleaning and transforming the tags into strings.
new_tab=[]
for row in tabs:
  for elem in row:
    elem=str(elem)
    elem=elem.strip()
    elem=elem.replace('\n',' ')
    elem=elem.replace('<td>','')
    elem=elem.replace('</td>','')
    elem=elem.strip()
    new_tab.append(elem)
new_tab

Список new_tab будет выглядеть примерно так.

Затем я конвертирую список в словарь следующим образом, чтобы, наконец, преобразовать его в кадр данных pandas.

#converting list data into dictionary 

my_dict=collections.defaultdict(list)
i=0
while(i<len(new_tab)):
  my_dict["Neighborhood"].append(new_tab[i])
  my_dict["Longitude"].append(new_tab[i+1])
  my_dict["Latitude"].append(new_tab[i+2])
  i=i+3
#Converting dictionary into a dataframe

no_table=pd.DataFrame(my_dict)

#converting latitudes and longitiudes into floats
convert_dict = {'Latitude': float,
               'Longitude':float} 
no_table= no_table.astype(convert_dict)
print(no_table.dtypes)
no_table.head()

Вывод будет выглядеть примерно так.

2.3 Выбор функций

Поскольку было задействовано всего 3 столбца, и все они были цельными, отбор признаков не производился. Но если есть лишние столбцы, которые вам не нужны, вы можете удалить их с помощью функций pandas.

2.4 Визуализация данных

Это забавная/неотъемлемая часть всего процесса, когда вы видите, как данные, которые вы собрали, оживают. Вы можете запустить приведенный ниже код, чтобы импортировать необходимые библиотеки.

pd.set_option('display.max_columns', None)
pd.set_option('display.max_rows', None)
import json # library to handle JSON files
!conda install -c conda-forge geopy --yes
from pandas.io.json import json_normalize # tranform JSON file into a pandas dataframe
# Matplotlib and associated plotting modules
import matplotlib.cm as cm
import matplotlib.colors as colors
from sklearn.cluster import KMeans
from geopy.geocoders import Nominatim
!conda install -c conda-forge folium=0.5.0 --yes
import folium # map rendering library
print('Libraries imported.')

Затем мы используем функцию библиотеки геокодера, чтобы получить широту и долготу Нового Орлеана для моей базовой карты. Это довольно полезная функция, которая использует агент Foursquare для получения широты и долготы действительного адреса.

address = 'new orleans'
geolocator = Nominatim(user_agent="foursquare_agent")
location = geolocator.geocode(address)
latitude = location.latitude
longitude = location.longitude
print(latitude, longitude)
#output should be 29.9499323 -90.0701156

Теперь мы визуализируем наши данные на базовой карте, используя приведенный ниже код.

# create map of New Orleans using latitude and longitude values
map_no = folium.Map(location=[latitude, longitude], zoom_start=10)
# add markers to map
for lat, lng, neighborhood in zip(no_table['Latitude'], no_table['Longitude'], no_table['Neighborhood']):
 label = '{}'.format(neighborhood)
 label = folium.Popup(label, parse_html=True)
 folium.CircleMarker(
 [lat, lng],
 radius=5,
 popup=label,
 color='blue',
 fill=True,
 fill_color='#3186cc',
 fill_opacity=0.7,
 parse_html=False).add_to(map_no)
map_no

Теперь этот фрагмент кода может показаться громоздким, и вам не нужно получать его за один раз, как и мне ;) Но я попытаюсь объяснить фрагменты кода для простоты. мы используем библиотеку folium для построения базовой карты заданной широты и долготы с помощью функции Map. zoom_start просто указывает размер масштабирования карты по умолчанию. Затем мы зацикливаем наш Data-Frame, чтобы индивидуально добавить наши данные на карту. название района хранится в «метке», а функция «Попоп» гарантирует, что метка отображается, когда вы нажимаете на точку данных на карте. «CircleMarker» просто используется для отметки нашей точки данных на карте с желаемым радиусом, цветом, непрозрачностью и так далее. Вы можете возиться с этим кодом, чтобы иметь разные формы, цвета и непрозрачность для ваших точек данных и персонализировать их. Наконец, вы добавляете эту точку данных в переменную карты, используя «add_to». После цикла for мы отображаем карту, просто вызывая переменную, и если все пойдет хорошо, карта должна выглядеть примерно так.

3. Исследовательский анализ данных

3.1 Расчет целевой переменной

Поскольку нашей основной целью является кластеризация окрестностей, мы использовали метод обучения без учителя, в котором метки кластеров являются целевыми переменными.

3.2 Связь между столбцами

Поскольку мы используем простые координаты, между столбцами нет никакой связи, кроме того факта, что столбцы широты и долготы являются координатами конкретного района. Но в случае таких данных, как цены на жилье, будет линейная зависимость с некоторыми аспектами, такими как количество комнат, квадратные футы и цена. В каких случаях потребуется нормализация данных (приведение всех признаков к одному диапазону значений) и другие подобные процессы. Для нашего набора данных это не требуется, но это полезный аспект, который следует учитывать для будущих проектов по науке о данных.

3.3 Получение данных о близлежащих площадках

Теперь мы изучаем близлежащие места на предмет предпочтений поселенцев, используя API Foursquare. Foursquare – это независимая платформа данных о местоположении, позволяющая понять, как люди перемещаются в реальном мире. Я получил доступ к этому API в ходе моего курса IBM, но его легко получить бесплатно, используя шаги, указанные в приведенной ниже ссылке. После всей процедуры вы должны получить «Идентификатор клиента» и «Секрет клиента». Просто создайте учетную запись разработчика и выполните следующие действия (URL-адрес приложения может быть веб-сайтом со случайным именем, насколько я помню)



Теперь, когда это сделано, сохраните идентификатор клиента и секрет в некоторых переменных, как показано ниже.

CLIENT_ID = 'XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX'
CLIENT_SECRET = 'YYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYY'
VERSION = '20180605'

Переменная версии указывает версию используемого вами API. Я не уверен, есть ли выпущенные последние версии, но это тоже должно работать.

Потрясающий! Теперь давайте обнаружим близлежащие места вокруг наших точек данных. Приведенный ниже код представляет собой функцию, которая делает то же самое.

#function to explore nearby venues for given location using Foursquare API
def getNearbyVenues(names, latitudes, longitudes, radius=500):
venues_list=[]
for name, lat, lng in zip(names, latitudes, longitudes):
print(name)
# create the API request URL
url = 'https://api.foursquare.com/v2/venues/explore?&client_id=     {}&client_secret={}&v={}&ll={},{}&radius={}&limit={}'.format(CLIENT_ID,CLIENT_SECRET,VERSION,lat,lng,radius,100)
# make the GET request
results = requests.get(url).json()["response"]['groups'][0]['items']
# return only relevant information for each nearby venue
venues_list.append([(name,lat,lng,v['venue']['name'],v['venue']['location']['lat'],v['venue']['location']['lng'],v['venue']['categories'][0]['name']) for v in results])
nearby_venues = pd.DataFrame([item for venue_list in venues_list for item in venue_list])
nearby_venues.columns = ['Neighborhood','Neighborhood Latitude','Neighborhood Longitude','Venue','Venue Latitude','Venue Longitude','Venue Category']
return(nearby_venues)

Функция принимает название, широту и долготу местоположения, а также радиус, в котором вы хотите искать близлежащие места. Теперь API Foursquare предоставляет различные конечные точки, такие как поиск, выбор, изучение, тренды, или группы конечных точек, такие как места проведения, пользователи (пользователи Foursquare), фотографии, регистрации и т. д., которые можно использовать для множества приложений. Вы можете найти больше информации об API здесь: https://developer.foursquare.com/docs/places-api/endpoints/

А пока мы «исследуем» «места проведения». URL-адрес принимает идентификатор клиента и секрет вместе с радиусом поиска и отправляет запрос на получение. Ответ содержит много информации, поэтому мы извлекаем только необходимую информацию и добавляем ее в список. Затем мы преобразуем список в заголовки столбцов Data-Frame и Append. Теперь мы просто вызываем функцию для нашего фрейма данных.

no_venues = getNearbyVenues(names=no_table['Neighborhood'],latitudes=no_table['Latitude'],longitudes=no_table['Longitude'])

Поскольку мы использовали функцию print(name) в функции, вы должны увидеть список всех проверяемых окрестностей. Как только это будет сделано, мы теперь проверяем собранные данные.

no_venues.head()

Вывод должен быть примерно таким, как показано ниже, но может измениться, так как новые магазины открываются, некоторые закрываются, и все зависит от того, когда вы запустите этот код, в течение следующих нескольких дней или, может быть, в футуристическом мире через 100 лет;)

Теперь вы можете видеть различную информацию о данных. Например, количество мест в каждом районе можно проверить,

no_venues.groupby('Neighborhood').count()

И это дает что-то вроде ниже

Теперь мы делаем то, что называется векторным кодированием One-Hot. По сути, мы создаем столбцы для каждого возможного типа места из нашего набора данных для каждого места. Приготовьтесь увидеть много нулей, ведь мест, которые есть в одном районе, может не быть в другом.

#creating dummy columns for each venue among those retreived, and comparing numbers for each neighborhood
no_onehot = pd.get_dummies(no_venues[['Venue Category']], prefix="", prefix_sep="")
move neighborhood column to the first column
fixed_columns = [no_onehot.columns[-1]] + list(no_onehot.columns[:-1])
no_onehot = no_onehot[fixed_columns]
no_onehot.insert(0,'Neighborhood',no_venues['Neighborhood'])
no_onehot.head()

Мы также можем проверить частоту проведения мероприятия в районе.

#checking frequency of venues in each neighnorhood
no_grouped = no_onehot.groupby('Neighborhood').mean().reset_index()
no_grouped

Теперь давайте найдем 3 лучшие площадки в каждом районе. Для этого мы используем следующий код. Вы можете найти топ 1, топ 5 или любое значение, которое вам нравится.

#finding top 3 venues in each neighborhood
num_top_venues = 3
for hood in no_grouped['Neighborhood']:
print("----"+hood+"----")
temp = no_grouped[no_grouped['Neighborhood'] == hood].T.reset_index()
temp.columns = ['venue','freq']
temp = temp.iloc[1:]
temp['freq'] = temp['freq'].astype(float)
temp = temp.round({'freq': 2})
print(temp.sort_values('freq', ascending=False).reset_index(drop=True).head(num_top_venues))
print('\n')

В этом блоке кода мы перебираем один кадр данных с горячим кодированием. Сначала мы сбрасываем индекс Default для каждого района, сохраняем только место проведения и его частоту для каждого района, преобразуем частоту в число с плавающей запятой и округляем, чтобы ее можно было использовать для сортировки по убыванию. Мы сохраняем только первое количество записей num_top_values ​​для каждого района, в моем случае это первые 3 значения. Вывод выглядит примерно так.

Теперь мы можем определить функцию, которая возвращает наиболее распространенные места в каждом районе.

def return_most_common_venues(row, num_top_venues):
row_categories = row.iloc[1:]
row_categories_sorted = row_categories.sort_values(ascending=False)
return row_categories_sorted.index.values[0:num_top_venues]

Последним шагом на всем этапе предварительной обработки данных является создание кадра данных, в котором хранятся наиболее распространенные места для каждого района.

#creating a new dataframe which shows most common venues in each neighbourhood
# the top 3 places weren't giving me enough information,I considered the top 5.
num_top_venues = 5
indicators = ['st', 'nd', 'rd']
# create columns according to number of top venues
columns = ['Neighborhood']
for ind in np.arange(num_top_venues):
try:
columns.append('{}{} Most Common Venue'.format(ind+1, indicators[ind]))
except:
columns.append('{}th Most Common Venue'.format(ind+1))
# create a new dataframe
neighborhoods_venues_sorted = pd.DataFrame(columns=columns)
neighborhoods_venues_sorted['Neighborhood'] = no_grouped['Neighborhood']
for ind in np.arange(no_grouped.shape[0]):
 neighborhoods_venues_sorted.iloc[ind, 1:] =  
return_most_common_venues(no_grouped.iloc[ind, :], num_top_venues)
neighborhoods_venues_sorted.head()

Здесь список индикаторов используется просто для заголовков столбцов. Блок try и exclude делает именно это. Теперь мы создаем окончательный кадр данных, куда мы добавляем столбец окрестности, а затем наиболее распространенные места для них, вызывая функцию, которую мы определили выше. Окончательный кадр данных должен выглядеть примерно так.

Вы будете рады узнать, что большая часть тяжелой работы уже сделана! Все, что осталось сейчас, — это применить некоторый алгоритм кластеризации, чтобы сгруппировать районы и выяснить, где вы хотите поселиться, на основе близлежащих мест.

4. Выбор модели

4.1 ДБСКАН

DBSCAN был моим первым выбором для кластеризации данных, поскольку он учитывает выбросы и основан на плотности точек, и он действительно работал хорошо, но модель дала только 2 кластера в качестве выходных данных и, следовательно, не была хорошим выбором модели. за предоставление предложений человеку, который хотел бы выбрать из множества групп.

4.2 Агломеративная иерархическая кластеризация

Это также был выбор для кластеризации, и с выбором выбора большего количества кластеров это был лучший выбор, чем DBSCAN, но результаты были плохими, поскольку распределение кластеров было неравномерным: 95% данных в одном кластере и очень мало. в других даже рассмотреть.

4.3 Кластеризация K-средних

Это был последний и лучший выбор для модели кластеризации. Хотя параметр инициализации по умолчанию «kmeans++» работал не так хорошо, поэтому мне пришлось использовать «случайный» параметр, и он работал хорошо для 7 кластеров с довольно равномерным распределением между ними.

Ниже приведен код алгоритма кластеризации, который я применил.

#I have tried multiple clustering algorithms, And Kmeans ran the best, If you want to try others, comment the Kmeans and
#Uncomment the algorithm you want to try
from sklearn.preprocessing import StandardScaler
from sklearn.cluster import DBSCAN
from sklearn.cluster import AgglomerativeClustering
#applying kmeans clustering method on the neighborhoods...
kclusters = 8 #proved to be best from trial and error
no_grouped_clustering = no_grouped.drop('Neighborhood', 1)
#'kmeans++'' performs poorly,so i have used 'random'
# run k-means clustering
kmeans = KMeans(n_clusters=kclusters,init='random', random_state=1).fit(no_grouped_clustering)
#dbscan runs better, but gives only 2 clusters, difficult to examine
#epsilon = 0.4
#minimumSamples = 7
#db = DBSCAN(eps=epsilon, min_samples=minimumSamples).fit(no_grouped_clustering)
#agglomerative also runs decently, but not as good as kmeans
#ag = AgglomerativeClustering(n_clusters = 5, linkage = 'complete')
#ag.fit(no_grouped_clustering)
#if you are rerunning this and the next cell to check for different k values/algorithms, uncomment next line
#neighborhoods_venues_sorted.drop('Cluster Labels',axis=1,inplace=True)
# check cluster labels generated for each row in the dataframe
kmeans.labels_[0:10]
#output may look something like this
# array([0, 0, 1, 0, 1, 0, 0, 0, 5, 0])

Выше приведены различные типы алгоритмов кластеризации, которые вы можете попробовать. Я обнаружил, что K-средние были лучшими для нашего набора данных, но вы также можете попробовать другие алгоритмы, раскомментировав соответствующие строки. Поскольку у нас есть метки кластера, давайте объединимся с нашим исходным фреймом данных. Мы также удаляем выбросы и преобразовываем метки кластеров в целые числа для правильного сопоставления.

#join the cluster labels with our original Dataframe
neighborhoods_venues_sorted.insert(0, 'Cluster Labels', kmeans.labels_)
no_merged = no_table
#merging original table with new data obtained
no_merged = no_merged.join(neighborhoods_venues_sorted.set_index('Neighborhood'), on='Neighborhood')
#dropping outliers i.e Nan cluster values
no_merged = no_merged[no_merged['Cluster Labels'].notnull()]
#converting cluster labels from float to int for mapping
convert_dict = {'Cluster Labels': int}
no_merged= no_merged.astype(convert_dict)
no_merged

Давайте теперь визуализируем эти кластеры на карте. Код визуализации карты аналогичен предыдущему, но теперь мы определяем разные цвета для каждого кластера. а затем добавьте точки данных на карту вместе с меткой кластера. Окончательное изображение карты было визуально приятным, по крайней мере, для меня xD.

map_clusters = folium.Map(location=[latitude, longitude], zoom_start=11)
# set 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]
# add markers to the map
markers_colors = []
for lat, lon, poi, cluster in zip(no_merged['Latitude'], no_merged['Longitude'], no_merged['Neighborhood'],no_merged['Cluster Labels']):
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=0.7).add_to(map_clusters)
map_clusters

Тогда все в порядке ! Давайте перейдем к заключительной части всего процесса, сделав вывод о результатах любого сбора/кластеризации данных, которые мы выполнили. Мы просто обращаемся к строкам с соответствующими метками кластеров, которые хотим изучить, и выводим их 3, 5 или X лучших мест.

#examining cluster 0
temp=no_merged.loc[no_merged['Cluster Labels'] == 0]
temp[['Neighborhood','Cluster Labels','1st Most Common Venue','2nd Most Common Venue','3rd Most Common Venue']]

Проходя через кластер 0, в нем есть множество кофеен, ресторанов и кафе, этот кластер кварталов действительно любит свою еду и напитки. Так что, если вам нравится хорошая еда и кафе, эти районы кажутся хорошим кластером. Кроме того, этот кластер является единственным с лодками и паромами, поэтому, если вы любите прогулки на лодке по морю, это может быть правильным выбором для вас :). Также хорошо подходит для тренажерного зала и спорта.

#examining cluster 1
temp=no_merged.loc[no_merged['Cluster Labels'] == 1]
temp[['Neighborhood','Cluster Labels','1st Most Common Venue','2nd Most Common Venue','3rd Most Common Venue']]

Этот кластер кажется лучшим местом для рынков и живописных смотровых площадок, поэтому, если вы любите исследовать открытые места, это может быть правильным выбором для вас.

Выполняя аналогичную экспертизу на остальных кластерах, я нашел следующую информацию.

Кластер 3: этот кластер имеет довольно меньший, более случайный набор мест, но его можно отметить своими парками. Несмотря на то, что он не имеет большого веса, если вы любите гулять, возможно, стоит рассмотреть эти районы.

Кластер 4: в этих двух районах есть несколько магазинов одежды, а также магазины женской одежды. Несмотря на то, что этот кластер имеет небольшой вес, если вы увлекаетесь одеждой и покупками, это хороший набор районов.

Кластер 5: Эта группа районов, безусловно, любит свои морепродукты!;) , и если вы тоже, я не вижу никакого вреда в том, чтобы поселиться в этих районах.:)

Кластер 6: довольно большая группа, эти районы, кажется, имеют активную ночную жизнь, с барами, некоторыми ночными видами и ресторанами. Так что, если вы холостяк и планируете расслабиться, возможно, поселитесь здесь..;)

Кластер 7: Имея один район, этот кластер не имеет большого веса.

5. Результат

В результате получилось в общей сложности 7 кластеров, которые разделили районы на основе их наиболее часто встречающихся мест, при этом в одном кластере было больше кафе и ресторанов, а в другом больше баров, ночных клубов и т. д. Наконец, такое разделение кластеров позволило очень легко дифференцировать группа районов на основе их мест проведения и, следовательно, позволяет человеку выбирать район на основе его / ее вкусов и предпочтений.

6. Заключение

В этом исследовании я собрал данные о районе со страницы Википедии и нашел информацию о близлежащих местах с помощью FQ API, очистил данные и сгруппировал их на основе общих мест, чтобы дать человеку рекомендации по выбору района на основе его/ее предпочтений.

7. Будущие направления

Новый Орлеан был тестовым местом для проверки моего проекта, и набор данных был недостаточно большим, чтобы получить хорошие результаты, но с большим набором данных, большим количеством районов, другим местом с большим количеством мест и, возможно, лучшим алгоритмом кластеризации, лучше и больше можно получить однородные результаты. В зависимости от того, когда вы попробуете этот проект, данные будут различаться. Я предполагаю, что трендовый API FQ дал бы мне лучшие результаты, но я полагал, что не найду много трендовых мест из-за текущей ситуации с пандемией :(

Последние мысли…

На этом мой проект по науке о данных завершен от А до Я. Надеюсь, вам понравилось, и вы смогли понять то же самое. Это определенно не должно быть однодневным проектом. На это у меня ушло несколько дней с перерывами. Так что не торопитесь, ищите в Интернете вещи, которые вы не понимаете или с которыми не знакомы, или вы можете спросить меня в разделе комментариев, и я постараюсь ответить вам или предоставить вам ресурсы для решения того же самого. Я также признаю, что это, возможно, не лучший набор данных для части кластеризации и логического вывода, но для предварительной обработки базовых данных это было бы хорошим началом для вас.

Спасибо и берегите себя ;)