#!/usr/bin/env python # coding: utf-8 # ![Airbnb_banner.png](attachment:76ff8d77-df97-4a42-a9c9-8aa6d7addab2.png) # # # Содержание # # - [1. Цели](#objectives) # - [2. Подготовка и разведочный анализ данных](#dataprep) # - [3. Обзор собственников и отзывов](#hosts) # - [4. Обзор расположений](#location) # - [5. Анализ эмоциональной тональности отзывов клиентов](#sentiment) # - [6. Заключение](#conclusion) # # # # 1. Цели #
# #
# # # 2. Подготовка и разведочный анализ данных # # ## Импорт и настройка пакетов # In[11]: import pyodbc import pandas as pd import numpy as np import matplotlib.pylab as plt import seaborn as sns import folium from folium.plugins import MarkerCluster from folium import plugins from folium.plugins import FastMarkerCluster from folium.plugins import HeatMap from sqlalchemy import create_engine from collections import Counter from wordcloud import WordCloud import re pd.set_option("display.max_rows",100) pd.set_option("display.max_columns",100) plt.style.use('ggplot') #
# Данные с подробными сведениями об объявлениях и отзывах за четыре квартала, до июня 2023 года. Все файлы взяты с веб-ресурса Inside Airbnb. # CSV файлы сначала были загружены в базу данных SQL Server, а затем записи из отдельных файлов были объединены в одну таблицу. #
# In[13]: db_engine = create_engine('mssql+pyodbc://sa:8biq9V6NJxpUP4kSQVqi@system/AirBnb?driver=ODBC+Driver+17+for+SQL+Server') # In[14]: ''' 4 quarter periods and last_scraped values for each one Sep 9 2022: 2022-09-09 2022-09-10 Dec 6 2022: 2022-12-06 2022-12-07 2022-12-25 Mar 7 2023: 2023-03-07 2023-03-08 2023-03-09 Jun 6 2023: 2023-06-06 2023-06-07 2023-06-08 ''' listing = pd.read_sql_table('listing_la', db_engine) review = pd.read_sql_table('review_la', db_engine) listing.head() #
# Здесь мы рассматриваем таблицу объявлений, отмечая размеры, типы столбцов и пропущенные значения. # # Всего, более 173 тысяч объявлений, описанных 75 атрибутами. #
# In[16]: # displays the dimenstions of the listings dataset listing.shape # In[17]: # displays types for listings data listing.dtypes # In[18]: # checks missing values in all columns listing.isna().sum().sort_values(ascending=False) # In[19]: # removes select columns listing_mod = listing.drop(columns=["scrape_id", "source", "neighborhood_overview", "picture_url", "host_location", "host_about", \ "host_thumbnail_url", "host_picture_url", "host_neighbourhood", "host_verifications", "host_has_profile_pic", \ "host_identity_verified", "bathrooms", "minimum_minimum_nights", "maximum_minimum_nights", "minimum_maximum_nights", \ "maximum_maximum_nights", "minimum_nights_avg_ntm", "maximum_nights_avg_ntm", "calendar_updated", "calendar_last_scraped", \ "number_of_reviews_ltm", "number_of_reviews_l30d", "review_scores_accuracy", "review_scores_cleanliness", \ "review_scores_checkin", "review_scores_communication", "review_scores_value", "instant_bookable", "calculated_host_listings_count", \ "calculated_host_listings_count_entire_homes", "calculated_host_listings_count_private_rooms", \ "calculated_host_listings_count_shared_rooms", "reviews_per_month"]) # In[20]: listing_mod.dtypes #
# Мы выбрали некоторые атрибуты, которые можно удалить из набора данных, поскольку они являются неактуальными или все значения отсутствуют. # # Четыре столбца, указанные ниже, имели тип данных, состоящий из чисел с плавающей запятой, и были преобразованы в целые числа. #
# In[22]: # displays and fills NA values for some columns before type conversion temp = listing_mod[['id','host_listings_count','host_total_listings_count','bedrooms','beds']] temp[temp.isna().any(axis=1)] listing_mod.fillna({'host_listings_count':0,'host_total_listings_count':0,'bedrooms':0,'beds':0}, inplace = True) # converts columns type from float to integer listing_mod = listing_mod.astype({'host_listings_count':'int64','host_total_listings_count':'int64','bedrooms':'int64','beds':'int64'}) listing_mod.info() #
# Следующие три столбца имеют только некоторые повторяющиеся значения и более пригодны для категориальных типов данных. #
# In[24]: # explores the unique values in these columns print('Уникальные значения в "Neighbourhood": ') print(listing_mod.neighbourhood_group_cleansed.unique()) print('Уникальные значения в "Room type": ') print(listing_mod.room_type.unique()) print('Уникальные значения в "Host response time": ') print(listing_mod.host_response_time.unique()) # converts types to a category listing_mod = listing_mod.astype({'neighbourhood_group_cleansed':'category','room_type':'category', \ 'host_response_time':'category'}) listing_mod.dtypes # ### Проверка на дубликаты #
# Дубликаты в подмножестве выбранных столбцов в таблице объявлений не обнаружены. #
# In[26]: # checks duplicate records for all columns print('Количество дубликатов: ' + str(listing_mod.duplicated().sum())) # checks duplicates on a specific subset of columns listing_mod.duplicated(subset = ['id', 'last_scraped', 'host_id']).sum() # ## Исследование атрибутов # ### Корреляция # In[29]: listing_corr = listing_mod[['bedrooms','beds','price','number_of_reviews', 'minimum_nights']].dropna().corr() sns.heatmap(listing_corr, annot=True) plt.show() #
# Диаграмма выше показывает, как определенные численные атрибуты соотносятся друг с другом. #
# ### Удаление выбросов в столбце Price # In[32]: # identifies and removes outliers in the price column plt.figure(figsize=(12,6)) plt.title('Распределение цены') sns.boxplot(data=listing_mod, x='price') plt.show() listing_mod.price.describe() #
# Подход IQR (интерквартильный диапазон) был применён для фильтрации объявлений и удаления выбросов больше значения 2182 USD за сутки для более чистого и реалистичного анализа. #
# In[34]: Q1 = listing_mod.price.quantile(0.25) Q3 = listing_mod.price.quantile(0.75) IQR = Q3 - Q1 lower_limit = Q1 - IQR * 1.5 upper_limit = Q3 + IQR * 12 print(f'Нижний предел: {lower_limit}\nВерхний предел: {upper_limit}') # since the lower limit is negative, let's check if there is any prices below zero print(f'Записи с ценами ниже нуля: {len(listing_mod[listing_mod.price < 0].index)}') # we pick big factor of 12 to give room for listings with reasonably high prices considering the location and the property size listing_mod = listing_mod[~(listing_mod.price > upper_limit)] print(f'Обновлённое количество строк: {len(listing_mod.index)}') #
# Поскольку мы загрузили четыре файла по одному на каждый квартальный период, даты используются в качестве ссылок для определения каждого периода. # # У нас есть несколько дат, когда была собрана информация, и одна дата, когда были сохранены большинство записей. # # Например, период, датированный 6 июня 2023 года, также содержит некоторые записи, датированные двумя следующими днями. # #

# Июнь 6 2023:
# 2023-06-06
# 2023-06-07
# 2023-06-08
#

# Эти даты заменены последним значением в каждом квартале для более чистой группировки. #
# # In[36]: # shows unique dates of scraped data for all 4 quarters listing_mod.groupby('last_scraped').size() # substitutes dates with the latest value in each quarter for cleaner grouping listing_mod.loc[listing_mod.last_scraped == '2022-09-09', 'last_scraped'] = '2022-09-10' listing_mod.loc[listing_mod.last_scraped == '2022-12-06', 'last_scraped'] = '2022-12-25' listing_mod.loc[listing_mod.last_scraped == '2022-12-07', 'last_scraped'] = '2022-12-25' listing_mod.loc[listing_mod.last_scraped == '2023-03-07', 'last_scraped'] = '2023-03-09' listing_mod.loc[listing_mod.last_scraped == '2023-03-08', 'last_scraped'] = '2023-03-09' listing_mod.loc[listing_mod.last_scraped == '2023-06-06', 'last_scraped'] = '2023-06-08' listing_mod.loc[listing_mod.last_scraped == '2023-06-07', 'last_scraped'] = '2023-06-08' # displays 4 unique dates: one per quarter listing_mod.groupby('last_scraped').size() # # # 3. Обзор собственников и отзывов # В этом разделе исследуются 25 собственников с самой высокой прогнозируемой ежемесячной выручкой. # # In[38]: # identifies top 25 hosts by their projected revenue (average for all quarters) listing_mod['projected_revenue_30d'] = listing_mod['price'] * (30 - listing_mod['availability_30']) top_hosts_rev = listing_mod.groupby('host_id')['projected_revenue_30d'].mean().sort_values(ascending=False).head(25) top_hosts_rev = listing_mod.loc[listing_mod['host_id'].isin(top_hosts_rev.index), \ ['host_id', 'host_name', 'host_since', 'neighbourhood_cleansed', 'property_type', 'projected_revenue_30d', 'license']] # shows how many hosts are licensed host_lic_dist = top_hosts_rev[['host_id','license']] host_lic_dist = host_lic_dist.fillna({'license': 'None'}) host_lic_dist = host_lic_dist.drop_duplicates() plt.figure(figsize=(4,2)) host_lic_dist.license.value_counts().plot(kind='bar') plt.title("Распределение Лицензий Среди Лучших Собственников") plt.xlabel('Лицензия') plt.ylabel('Количество') plt.xticks(rotation=75) plt.show() #
# График показывает, что большинство из топ-собственников не имеют официальной лицензии. #
# In[40]: # lists information about top 25 hosts by projected revenue(30 days) top_hosts_rev = top_hosts_rev.groupby(['host_id', 'host_name', 'host_since', 'neighbourhood_cleansed', 'property_type']) \ .agg(average_proj_revenue=('projected_revenue_30d','mean')).sort_values('average_proj_revenue', ascending=False).reset_index() top_hosts_rev # In[41]: # displays the distribution of neighbourhoods for the top hosts loc_dist = top_hosts_rev.neighbourhood_cleansed.value_counts() plt.figure(figsize=(5, 4)) sns.countplot(y='neighbourhood_cleansed', data=top_hosts_rev, order=loc_dist.index, orient='h',edgecolor=None) plt.title("Распределение по Районам (Топ Собственники)") plt.xlabel("Количество") plt.ylabel("Районы") plt.show() #
# Собственники, которые приносят наибольший доход с помощью своих объявлений, имеют средний доход в диапазоне от 50 850 до 63 000 USD. Наиболее популярными районами, в которых они расположены, являются: Malibu, Beverly Crest, Westwood, Venice and Beverly Grove. #
# # In[43]: # displays the distribution of property types for the top hosts prop_dist = top_hosts_rev['property_type'].value_counts() #plt.style.use('ggplot') plt.pie(prop_dist, labels=prop_dist.index, autopct="%.1f%%") plt.title("Распределение по Виду Собственности(Топ Собственники)") plt.show() #
# Такой тип собственности как целый дом и арендуемая единица доминируют в объявлениях самых популярных собсвтенников с долей более 80%. #
# # ### Средняя цена среди всех собственников # Датасет содержит четыре отрезка времени, покрывающих информацию об объявлениях за период примерно с **июня 2022** до **6 июня 2023** # In[46]: price_mean = listing_mod.groupby('last_scraped').agg({'price':'mean'}) plt.figure(figsize=(5, 3)) sns.lineplot(x='last_scraped', y='price', data=price_mean, label='Цена') plt.title("Средняя Цена за Период Времени") plt.xlabel("Дата") plt.ylabel("Цена(USD)") # rotates x-axis labels for readability plt.xticks(rotation=45) plt.tight_layout() plt.show() #
# Поскольку у нас недостаточно значений дат цены для анализа, мы отмечаем, что цены меняются довольно резко. # # Это показывает нам тренд, при котором собственники снижают цены ближе к концу года, чтобы увеличить спрос в этом последнем квартале 2022 года. #
# ## Топ-25 объявлений по количеству отзывов # In[49]: # identifies top 25 listings with the largest number of reviews # shows all listings across all dates when the data was collected review_group = review.groupby('listing_id')['listing_id'].count().sort_values(ascending=False).head(25) listing_25 = listing_mod.loc[listing_mod.id.isin(review_group.index),['id', 'listing_url', 'name', 'host_id', \ 'host_name', 'last_scraped', 'neighbourhood_cleansed', 'property_type', \ 'number_of_reviews']].sort_values('number_of_reviews', ascending=False) print(f'\nРаспределение по виду собственности:\n\n{listing_25.property_type.value_counts()}') listing_25.head(15) #
# Таблица выше демонстрирует информацию об объявлениях с немного другого ракурса -- наибольшее количество обзоров. Виды собственности объектов распределены здесь более равномерно. #

# Большинство предложений представляют собой весь гостевой дом, частную комнату в доме и номер в бутик-отеле. #

# # # 4. Обзор расположений #
#

Все объявления на карте

# Визуализация карты показывает географическое распределение и плотность более чем 67 000 уникальных месторасположений по всему Лос-Анджелесу Даунтауну, Sherman Oaks, Hollywood, Venice #
и другим прибрежным районам, которые и содержат большинство объявлений. #
# # In[53]: unique_coordinates = listing_mod.drop_duplicates(subset=['latitude', 'longitude']) locations = list(zip(unique_coordinates.latitude, unique_coordinates.longitude)) init_long = -118.36 init_lat = 34.06 map_la_listings = folium.Map(location=[init_lat, init_long], zoom_start=9) HeatMap(locations,min_opacity=0.25).add_to(map_la_listings) FastMarkerCluster(data=locations, radius=5).add_to(map_la_listings) map_la_listings # ## Лучшие районы по количеству объявлений # In[55]: neig_dist = listing_mod.neighbourhood_cleansed.value_counts().head(50) plt.figure(figsize=(12,10)) plt.title("50 Лучших Районов по Количеству Объявлений", fontsize=16) plt.xlabel('Количество Объявлений', fontsize=12) reversed_palette = sns.color_palette("inferno", n_colors=len(neig_dist))[::-1] sns.countplot(y='neighbourhood_cleansed', data=listing_mod, order=neig_dist.index, palette=reversed_palette) plt.ylabel('Районы') plt.xlabel('Количество Объявлений') # adds listings count beside each neighbourhood for index, value in enumerate(neig_dist): plt.text(value + 20, index, str(value), fontsize=8, va='center', weight='bold') plt.show() #
#

Лучшие районы по среднему прогнозируемому месячному доходу

# Ниже перечислены 25 лучших районов по среднему прогнозируемому ежемесячному доходу. Ключевые выводы: # #
# In[57]: neighbourhoods = listing_mod.groupby('neighbourhood_cleansed', as_index=False) top_loc = pd.DataFrame({'Neighbourhood': pd.Series(dtype='str'), 'Mean Projected Revenue(30d)':pd.Series(dtype='float'), 'Mean Price': pd.Series(dtype='float'), 'Property Type': pd.Series(dtype='str')}) # calculates most used property type for a neighbourhood and adds the row to the temporary table for neighb, data in neighbourhoods: projected_revenue = data['projected_revenue_30d'].mean() price = data['price'].mean() property_type = data['property_type'].mode()[0] top_loc.loc[len(top_loc)] = [neighb, projected_revenue, price, property_type] # Mean Projected Revenue(30d) value is relative to all listings in the neighbourhood and not the whole neighbourhood itself top_loc = top_loc.sort_values('Mean Projected Revenue(30d)', ascending=False).head(25) top_loc # In[58]: loc_rooms_price = listing_mod[listing_mod.neighbourhood_cleansed.isin(top_loc.Neighbourhood)] loc_rooms_price = loc_rooms_price.groupby(['neighbourhood_cleansed', 'bedrooms']).price.mean().unstack() loc_rooms_price.sort_values('neighbourhood_cleansed', ascending=False) plt.figure(figsize=(10,8)) sns.heatmap(loc_rooms_price, annot=True, fmt=".0f") plt.ylabel('Районы') plt.xlabel('Количество спален') plt.show() #
# Мы уже выявляли, что тип собственности в виде целого дома является основным типом для лучших мест. Этот график позволяет нам увидеть средние цены всех объявлений, разбитые по месторасположениям и количеству спальных комнат. #

# Самыми дорогими микрорайонами в целом являются Bel-Air, Malibu, Beverly Crest, Pacific Palisades и Unincorporated Santa Monica Mountains. # Средние цены этих микрорайонов колеблются от 400 до более 1500 долларов за ночь.

#

Мы также замечаем тенденцию среди лучших микрорайонов. Например, Bel-Air показывает значительный скачок цен с 2-х до 3-х спальных комнат, в среднем 879 долларов за трёхспальные комнаты в сравнении с 303 долларами за две спальни.

#

Newport Beach имеет наибольшую доступность среди более крупных объектов 4-8 спальных комнат среди лучших районов.

#
# #
#

Удобства в объявлениях

# Давайте взглянем на удобства, указанные в объявлениях. Два графика рядом друг с другом дают нам понимание того, что популярно и важно не только в топовых локациях, но и в целом. # # Мы видим много пересечений. Это подтверждает, что новые хозяева должны обратить внимание на эти наиболее упомянутые удобства, чтобы соответствовать высочайшим ожиданиям клиентов. # #
# # In[61]: amenities = listing_mod.amenities.to_list() amenities_top_loc = listing_mod[listing_mod.neighbourhood_cleansed.isin(top_loc.Neighbourhood)].amenities.to_list() import matplotlib.gridspec as gridspec # extracts individual words from amenities column words = [] for item in amenities: words_lst = item.strip('][').split(', ') words.extend(words_lst) amenities_counter = Counter(words) # gets the top 25 most common words top_words = amenities_counter.most_common(25) top_words_df = pd.DataFrame(top_words) top_words_df.rename(columns={0:'Words', 1:'Count'}, inplace=True) # extracts individual words from amenities column words_loc = [] for item in amenities_top_loc: words_lst_loc = item.strip('][').split(', ') words_loc.extend(words_lst_loc) amenities_counter_loc = Counter(words_loc) # gets the top 25 most common words for neighbourhoods top_words_loc = amenities_counter_loc.most_common(25) top_words_loc_df = pd.DataFrame(top_words_loc) top_words_loc_df.rename(columns={0:'Words', 1:'Count'}, inplace=True) # plots gs = gridspec.GridSpec(1, 2, width_ratios=[1.5,2]) fig = plt.figure(figsize=(25,14),dpi=300) ax = plt.subplot(gs[0,0]) reversed_palette = sns.color_palette("inferno", n_colors=25)[::-1] ax = sns.barplot(x='Count', y='Words', data=top_words_df, palette=reversed_palette, alpha=0.9) ax.set_title('Топ 25 Наиболее Упоминаемых Удобств (Все Объявления)\n', fontsize=24) ax.set_xlabel('Количество слов', fontsize=16) ax.set_ylabel('Слова', fontsize=16) ax.tick_params(axis='x', labelsize=16) ax.tick_params(axis='y', labelsize=16) ax1=plt.subplot(gs[0,1]) ax1 = sns.barplot(x='Count', y='Words', data=top_words_loc_df, palette=reversed_palette, alpha=0.9) ax1.set_title('Топ 25 Наиболее Упоминаемых Удобств (Лучшие Районы)\n', fontsize=24) ax1.tick_params(axis='x', labelsize=16) ax1.tick_params(axis='y', labelsize=16) plt.xlabel('Количество слов', fontsize=16) plt.ylabel('Слова', fontsize=16) fig.tight_layout() plt.show() # # # 5. Анализ эмоциональной тональности отзывов клиентов #
# Была использована заранее натренированная модель трансформера для расчёта положительных, нейтральных и отрицательных оценок. #
Она была обучена на 124 миллионах постах на платформе X(Twitter), охватывающих почти четыре года. #
Некоторые ключевые результаты: # #
# In[64]: from transformers import AutoTokenizer, AutoConfig from transformers import AutoModelForSequenceClassification from scipy.special import softmax import warnings warnings.filterwarnings('ignore') #pd.set_option('display.max_colwidth', None) # In[ ]: MODEL = f"cardiffnlp/twitter-roberta-base-sentiment-latest" tokenizer = AutoTokenizer.from_pretrained(MODEL) config = AutoConfig.from_pretrained(MODEL) model = AutoModelForSequenceClassification.from_pretrained(MODEL) # In[66]: # max sequence length MAX_LEN = 512 # uses the pretrained model to get the scores for each review def get_sentiment(review): review = review[:MAX_LEN] encoded_input = tokenizer(review, return_tensors='pt') output = model(**encoded_input) scores = output[0][0].detach().numpy() scores = softmax(scores) # gets the highest score and fills a sentiment label max_score = max(scores) sentiment = 'Neutral' if scores[0] == max_score: sentiment = 'Negative' elif scores[2] == max_score: sentiment = 'Positive' return np.array([scores[0], scores[1], scores[2]]), sentiment # applies sentiment scores and labels to a copy of the dataframe def apply_sentiment(df): df_copy = df.copy() # creates sentiment columns first df_copy['score_neg'] = None df_copy['score_neu'] = None df_copy['score_pos'] = None df_copy['sentiment'] = None for idx, row in df.iterrows(): scores, sentiment = get_sentiment(row.comments) df_copy.at[idx, 'score_neg'] = scores[0] df_copy.at[idx, 'score_neu'] = scores[1] df_copy.at[idx, 'score_pos'] = scores[2] df_copy.at[idx, 'sentiment'] = sentiment return df_copy # obtains the top 25 hosts listings ids for mathcing with reviews records listings_ids = listing_mod.loc[listing_mod['host_id'].isin(top_hosts_rev.host_id), 'id'] review_top_hosts = review[review.listing_id.isin(listings_ids)] review_sentiment = apply_sentiment(review_top_hosts) review_sentiment.head() # In[67]: # displays the distribution of property types for the top hosts sent_dist = review_sentiment['sentiment'].value_counts() plt.figure(figsize=(5,3)) plt.pie(sent_dist, labels=sent_dist.index, autopct="%.1f%%") plt.title("Распределение Тональности Отзывов (Лучшие собственники)") plt.show() #
#

Облаки слов

# Два облака слов иллюстрируют наиболее часто используемые слова, оставленные в отзывах на объявления лучших собственников и во всех объявлениях в целом. #

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

#
Общая диаграмма слов рассказывает о похожем настроении. #
Список содержит "отличное местоположение", "чисто", "дом", "отличное место", "настоятельно рекомендую", "отличный хозяин", "определённо остаюсь", "парковка на улице", "быстрый ответ", "Санта-Моника", "тихий район" и так далее. Термины более общие и сконцентрированы на местоположении, виде имущества, условиях проживания и общем удовлетворении. #
# In[69]: # fills missing values print(review.isna().sum()) review.comments = review.comments.fillna('') print('\n --- Отзывы после вызова fillna: \n') print(review.isna().sum()) print('\n') gs = gridspec.GridSpec(1, 2, width_ratios=[1,1]) fig = plt.figure(figsize=(14,10), dpi=250) ax_r_hosts = plt.subplot(gs[0,0]) ax_r_all = plt.subplot(gs[0,1]) review_words_hosts = " ".join(rev for rev in review_top_hosts.comments) wordcloud_hosts = WordCloud(width=700, height=400, max_words=350, min_word_length=3, ranks_only=True).generate(review_words_hosts) ax_r_hosts.set_axis_off() ax_r_hosts.set_title('Облако Слов (Отзывы лучших собственников)') ax_r_hosts.imshow(wordcloud_hosts, interpolation='bilinear') # generates a wordcloud plot review_words = " ".join(rev for rev in review.comments) wordcloud = WordCloud(width=700, height=400, max_words=350, min_word_length=3, ranks_only=True).generate(review_words) ax_r_all.set_axis_off() ax_r_all.set_title('Облако Слов (Все отзывы)') ax_r_all.imshow(wordcloud, interpolation='bilinear') fig.tight_layout() plt.show() # # # 6. Заключение #
# # Цели, поставленные для этого проекта, были достигнуты, и этот раздел содержит ключевые выводы, открытия и фактические числа. Объявления AirBnb и отзывы для региона Лос-Анджелеса, Калифорния, были получены из публичного веб-ресурса [Inside AirBnb](http://insideairbnb.com/), который регулярно публикует квартальные данные в виде снимков объявлений, доступных в определенной точке времени. # #

Начальная загрузка данных, моделирование и подготовка

# Четыре файла с подробной информацией об объявлениях были загружены и объединены в одну таблицу в базе данных Microsoft Sql Server. 173 168 записей были загружены с 75 столбцами различных типов данных. После просмотра некоторые лишние столбцы были удалены, и остался 41 столбец. # График цены показал некоторые выбросы со значениями до 100 000 за ночь, которые не подходили для разумного анализа. После установки предела в 2182 долларов, у нас осталась 171 061 запись. # #

Собственники и объявления

# Ключевые собственники, генерировавшие доход, заработали от 50 850 до 63 000 в месячном доходе исходя из прогноза, в различных видных районах, таких как Malibu, Beverly Crest, и Westwood. # Мы рассмотрели динамику цен. Она показывает нам тренд, при котором собственники скидывают цены ближе к концу года, чтобы увеличить спрос в последний квартал 2022 года. # #

Районы

# Географическое распределение более 67 000 уникальных мест по всему Лос-Анджелесу было визуализировано, демонстрирующее районы с более высокой плотностью объявлений, такие как Даунтаун, Sherman Oaks, и Hollywood. #

Оценивая лучшие районы на основе средней прогнозируемой месячной выручки, можно отметить, что прибрежные районы, такие как, Emerald Bay, Newport Beach, и Palos Verdes, занимают высокие позиции по генерации выручки. #
Анализ показал, что эти прибыльные районы поддерживают баланс между спросом и доходами, предлагая цены, часто находящиеся в диапазоне от 400 до 800 долларов. Доминирование целых домов в этих районах подчеркивает фокус на групповом и семейном проживании. #

# Списки по количеству спален показали, что цены существенно увеличиваются от 2 до 3+ спален в самых дорогих рафонах. # Мы также проанализировали наиболее часто упоминаемые удобства в объявлениях. Сопоставив это с лучшими районами, было установлено, что определенные удобства пользуются постоянной популярностью. # Это подчёркивает важность включения этих удобств для соответствия ожиданиям гостей и повышения удовлетворённости. # #

Анализ эмоциональной тональности в отзывах

# Используя предтренированную модель трансформера, был проведён анализ тональности обзоров клиентов. Большинство отзывов демонстрировали высокоположительную тональность, с оценками, значения которых обычно находились в диапазоне 0.95-0.99. Отзывы с негативной тональностью были редкими и касались только автоматической отмены резервирования. Анализ показал положительную тональность, обусловленную такими аспектами, как отличное расположение, потрясающий вид, необходимые удобства и исключительные хозяева. #

#
#

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

#