Введение

В качестве исходных данных был выбран открытый датасет Inside Airbnb для города Амстердам (North Holland, The Netherlands). Датасет содержит информацию об объявлениях Airbnb, включая цену за ночь, тип жилья, район, количество отзывов и другие характеристики.
Данные Airbnb представляют интерес с точки зрения анализа городской среды и экономики краткосрочной аренды. Конкретно для меня данный анализ тоже актуален, так как недавно моя сестра вернулась из этого города, и ее поездка замотивировала когда-нибудь в будущем туристически ознакомиться с этой локацией. Амстердам — это город с высокой плотностью объявлений, что делает датасет достаточно объёмным и содержательным для анализа и визуализации.
Амстердам (взгляд ИИ)
Для анализа и визуализации данных использовались библиотеки Pandas, Matplotlib, Seaborn, TextBlob, Numpy, Scikit-learn, WordCloud.
Структура данных (колонки): listing_id: идентификатор объявления id: уникальный идентификатор записи отзыва date: дата отзыва month: месяц отзыва year: год отзыва reviewer_id: идентификатор рецензента reviewer_name: имя рецензента comments: текст отзыва sentiment: значение сентимента отзыва (положительный, нейтральный, отрицательный) sentiment_type: классификация сентимента отзыва на положительный, нейтральный или отрицательный (основано на анализе текста)
Оформление графиков
#FF5A5F — основной цвет (данные) Фирменный красный цвет Airbnb. Используется для отображения основных данных на графиках.
#3A0F0F — цвет текста и обводки Тёплый тёмно-коричневый оттенок, заимствованный из айдентики Airbnb. Применяется для подписей, осей и рамок графиков. Обеспечивает хороший контраст на светлом фоне и выглядит мягче и аккуратнее, чем стандартный чёрный цвет.
#FFF6F6— цвет фона Светлый тёплый оттенок розово-кремового цвета. Используется в качестве фона графиков, чтобы избежать резкого контраста и улучшить читаемость данных.
#E6E6E6 — цвет сетки Нейтральный светло-серый цвет. Применяется для вспомогательной сетки, которая помогает считывать значения, не отвлекая внимание от основных данных.
цвета
Используемый шрифт: DejaVu Sans
import matplotlib as mpl
mpl.rcParams["font.family"] = "DejaVu Sans" mpl.rcParams["axes.titlesize"] = 14 mpl.rcParams["axes.labelsize"] = 11 mpl.rcParams["xtick.labelsize"] = 10 mpl.rcParams["ytick.labelsize"] = 10
шрифт
Используемые виды графиков:
- Столбчатая диаграмма (Гистограмма)
- Точечная диаграмма (Стрип-плот)
- Горизонтальная точечная диаграмма (Lollipop chart)
- Линейный график
- Круговая диаграмма
- Облако слов
Анализ данных
Анализ проводился в среде Google Colab с использованием библиотеки Pandas. Данные были загружены в формате CSV и считаны с помощью функции pd.read_csv().
Первым анализ заключается в желании узнать распределение цен на Airbnb в Амстердаме.
После загрузки данных была выполнена первичная проверка структуры датасета, включая размер таблицы, типы данных и наличие пропусков.
import os os.listdir()
import pandas as pd df = pd.read_csv("listings.csv") df.head()
На первом этапе был проведён изучающий анализ данных с целью понять структуру распределений и выявить возможные проблемы в данных.
Используемые методы:
df.shape— определение объёма данных df.info() — анализ типов данных и пропусков df.describe()— базовые описательные статистики
Особое внимание было уделено столбцу price, так как цена является ключевой характеристикой для анализа. В столбце price были обнаружены пропущенные значения (Nun). Для корректного анализа цен строки без указанной цены были исключены из выборки.
df_work = df.dropna(subset=["price"])
Такой подход позволил избежать искажения статистических показателей и визуализаций.

Для изучения распределения цен была построена гистограмма. Первичная визуализация показала наличие экстремальных выбросов, из-за которых основная масса данных была сжата в узком диапазоне (фото слева).
Для решения этой проблемы был применён квантильный подход: из анализа были исключены значения выше 95-го процентиля.
Используемые статистические методы:
(1) медиана (2) квантили (3) фильтрация выбросов по процентилю
price_95 = df_work["price"].quantile(0.95) df_price_filtered = df_work[df_work["price"] <= price_95]
Это позволило сосредоточиться на типичных ценовых предложениях и сделать визуализацию более интерпретируемой.
график/код № 1
После анализа общего распределения цен была построена агрегированная визуализация по ценовым диапазонам. Она позволяет наглядно показать, в каких ценовых сегментах сосредоточено наибольшее и наименьшее количество объявлений.
Вначале я создал новые ценовые диапазоны:
bins = [0, 50, 100, 150, 200, 250, 300, 400, 500, 1000] df_price_filtered["price_range"] = pd.cut( df_price_filtered["price"], bins=bins )
и посчитал количество объявлений:
price_ranges = ( df_price_filtered .groupby("price_range") .size() )
график/код № 2
Вывод
Большинство объявлений Airbnb в Амстердаме сосредоточено в среднем ценовом сегменте. Наибольшее количество предложений приходится на диапазон примерно от 150 до 250 € за ночь, что указывает на преобладание жилья среднего ценового уровня.
По мере увеличения цены количество объявлений постепенно снижается, а предложения в высоком ценовом сегменте представлены значительно реже. Низкобюджетные варианты также составляют меньшую долю рынка по сравнению со средним сегментом.
Следующим были проанализированы типы жилья, так как это является одним из самых логичных и классических факторов цены.
Вначале мной были проверены типы жилья:
df_price_filtered["room_type"].value_counts()
После чего я задался вопросом "Какие реальные цены встречаются внутри каждого типа жилья?" и для изучающей визуализации был выбрал точечный тип диаграммы.
график/код № 3
После построения изучающей визуализации, показывающей распределение индивидуальных значений цен внутри каждого типа жилья, стало видно, что внутри категорий наблюдается значительный разброс, а ценовые диапазоны частично пересекаются (до 250).
Чтобы перейти от изучения «сырых» данных к формулировке более чёткого вывода, данные были агрегированы по типам жилья. Для каждой категории была рассчитана медианная цена за ночь как устойчивая к выбросам характеристика.
price_by_room = ( df_price_filtered .groupby("room_type")["price"] .median() .sort_values(ascending=False) )
На основе агрегированных значений была построена объясняющая визуализация, позволяющая наглядно сравнить типичные уровни цен между различными типами жилья.
график/код № 4
Вывод
Несмотря на пересечения цен внутри категорий, медианные значения существенно различаются. Самыми дорогими являются отдельные квартиры и дома, тогда как частные и общие комнаты формируют более доступный сегмент рынка.
Далее мне было интересно изучить данные с отзывами, поэтому я загрузил данные из файла reviews.csv для дальнейшего анализа.
reviews_df = pd.read_csv("reviews.csv")
Преобразовал столбец date в формат datetime, чтобы можно было извлекать месяц и год для анализа:
reviews_df['month'] = reviews_df['date'].dt.month
Сгруппировал данные по месяцам и посчитал количество отзывов в каждом месяце:
monthly_reviews = reviews_df.groupby('month').size() monthly_reviews = monthly_reviews.reset_index(name='review_count')
Все это позволило мне построить линейный график для отображения количества отзывов по месяцам за последние 3 года.
reviews_df['year'] = reviews_df['date'].dt.year recent_reviews = reviews_df[reviews_df['year'] >= 2020]
график/код № 5
После получения этих данных я захотел показать зависимость количества отзывов от месяца, в которым они были написаны.
Для объясняющей визуализации я агрегировал данные, считая среднее количество отзывов в каждом месяце:
monthly_avg_reviews = reviews_df.groupby('month').size() / len(reviews_df['year'].unique()) monthly_avg_reviews = monthly_avg_reviews.reset_index(name='average_review_count')
И присвоил каждому месяцу его название для удобства отображения:
month_names = ['Январь', 'Февраль', 'Март', 'Апрель', 'Май', 'Июнь', 'Июль', 'Август', 'Сентябрь', 'Октябрь', 'Ноябрь', 'Декабрь'] monthly_avg_reviews['month_name'] = monthly_avg_reviews['month'].apply(lambda x: month_names[x-1])
график/код № 6
Далее я решил наглядно показать наибольшее и наименьшее значение на графиках.
min_idx = monthly_avg_reviews['average_review_count'].idxmin() max_idx = monthly_avg_reviews['average_review_count'].idxmax()
График / код № 6 (2.0)




















