Выборка исследования: архив объявлений о продаже квартир в Санкт-Петербурге за несколько лет - данные сервиса Яндекс.Недвижимость.
Цель: определить рыночную стоимость объектов недвижимости. Необходимо установить параметры для построения автоматизированной системы, отслеживающей аномалии и мошеннические действия.
По каждой продаваемой квартире есть два вида данных. Первые — вносили сами пользователи при публикации объявлений, вторые — получены на основе картографических данных: расстояния до центра, аэропорта, ближайшего парка и водоёма.
# Необходимые библиотеки для исследования
import pandas as pd
import matplotlib.pyplot as plt
import numpy as np
%matplotlib inline
# Укажем удобный формат вывода для чисел с плавающей точкой
pd.options.display.float_format = '{:.3f}'.format
# Загрузка данных
data = pd.read_csv('real_estate_data.csv', sep='\t')
# Всего набор данных состоит из 23699 строк и 24 столбцов
data.shape
# Изучем типы данных
data.info()
# Проверка распределения ключевого показателя квартиры - цены - перед удалением строк с пустыми значениями
# в столбцах с названием населенного пункта и общим кол-вом этажей в доме
ax = data[data['locality_name'].isnull()].plot(kind='hist', y='last_price', histtype='step', range=(0, 45000000), bins=50, label='delete_locality_name')
data[data['floors_total'].isnull()].plot(kind='hist', y='last_price', histtype='step', range=(0, 45000000), bins=50, ax=ax, label='delete_floors_total')
data.plot(kind='hist', y='last_price', histtype='step', range=(0, 45000000), bins=50, label='Стоимость квартир')
plt.title('Распределение цены квартир'+ "\n")
plt.xlabel('Цена')
plt.ylabel('Частотность')
plt.show()
# Просматриваем сводные статистические данные распределния стоимости кварт в удаляемых строках с пропуском названий населенного пункта
data[data['locality_name'].isnull()]['last_price'].describe()
# Просматриваем сводные статистические данные распределния стоимости кварт в удаляемых строках с пропускам в этажнсти здания
data[data['floors_total'].isnull()]['last_price'].describe()
Удаление строчек с пустыми значениями в столбцах floors_total и locality_name сильно не повлияют на общее распределение ключевого показателя исследования.
Наиболее типичные параметры квартиры: цена, общая площадь квартиры и количество комнат.
# Отбираем целевые характеристики квартиры
typical_flat = data[['last_price', 'rooms', 'total_area']]
# Описательная статистика целевых характеристик
typical_flat.describe()
# Распределение характеристик
typical_flat.hist(figsize=(10,10), bins=30)
plt.title('Распределение типичных характеристик квартир'+ "\n")
plt.xlabel('Цена')
plt.ylabel('Частотность')
plt.show()
Целевые характеристики содержат экстремальные значения. Это видно по расчетам описательных статистик, так и по визуализации распределения.
Например, стоимость квартиры колеблется от 450 тысяч и до 420 миллионов. Подобная картина в количестве комнат: самая многоквартирная квартира включает 19 комнат.
data['last_price'].describe()
data.plot(kind='box', y='last_price')
plt.show()
data.plot(kind='box', y='last_price', ylim=(0, 12000000))
plt.show()
На диаграмме размаха видны выбросы. Одним из способов борьбы с ними - удаление всех экстремальных значений, которые превышают сумму третьего квартиля и полтора интерквартильного размаха.
# Вычисление границы до выбросов
data['last_price'].describe()['75%'] + 1.5*(data['last_price'].describe()['75%']-data['last_price'].describe()['25%'])
data['last_price'].hist(range=(0, 11875000))
plt.title('Распределение стоимости квартиры до 11,875 млн.'+ "\n")
plt.xlabel('Стоимость')
plt.ylabel('Частотность')
plt.show()
data['last_price'].hist(range=(11875000, 100000000))
plt.title('Распределение стоимости квартиры превыщающую 11,875 тыс.'+ "\n")
plt.xlabel('Цена')
plt.ylabel('Частотность')
plt.show()
print('Количество квартир, стоимость которых превышает 50 млн. - ', len(data[data['last_price'] > 50000000]), 'квартиры')
# Описательные статистики квартир до 11,875 тыс.
data.query('last_price <= 11875000')['last_price'].describe()
# Распределение квартир до 3,4 млн. - 1 квартиль
data['last_price'].hist(range=(0, 3400000))
plt.title('Распределение стоимости квартиры до 3,4 млн.'+ "\n")
plt.xlabel('Стоимость')
plt.ylabel('Частотность')
plt.show()
# Проверим, на сколько удаление дорогостоящих квартир скажется на распредленении цены
ax = df_good.plot(kind='hist', y='last_price', histtype='step', range=(0, 35000000), bins=50, label='Good data')
data.plot(kind='hist', y='last_price', histtype='step', range=(0, 35000000), bins=50, ax=ax, label='Data', linestyle='dashed')
plt.show()
# Визуализация количественных характеристик
df_good.select_dtypes(include=np.number).iloc[:, :24].hist(figsize=(15,15), bins=30)
plt.show()
# Описательные характеристики
df_good.select_dtypes(include=np.number).iloc[:, :24].describe()
# Описательные статистики цены за квадратный метр
df_good['price_per_meter'].describe()
df_good.plot(kind='box', y='price_per_meter')
plt.show()
По диаграмме размаха видны выбросы: есть как экстремально дешевые, так и экстремально дорогие квартиры. Не смотря на выбросы, значения медианы и среднего арифметического не сильно отличаются друг от друга
df_good['price_per_meter'].hist(bins=20)
plt.title('Распределение стоимости квартир за квадратный метр'+ "\n")
plt.xlabel('Цена за квадратный метр')
plt.ylabel('Частотность')
plt.show()
Распределение цены за квадратный метр похоже на нормальное распределение.
# Проверка на сколько удаление экстремальных значений повлияет на распределение цены за квадратный метр квартиры
ax = df_good.plot(kind='hist', y='price_per_meter', histtype='step', bins=50, label='Old data')
df_good.query('price_per_meter < 170000').plot(kind='hist', y='price_per_meter', histtype='step', bins=50, ax=ax, label='New Data', linestyle='dashed')
plt.show()
df_good.query('price_per_meter < 20000').describe()
# Создаем дасатет по областям, где стоимость за квадратный метр меньше 20 тысяч
data_price_per_meter = (
pd.DataFrame(df_good.query('price_per_meter < 20000').groupby('locality_name')['price_per_meter'].count())
.join(df_good.groupby('locality_name')['price_per_meter'].count().sort_values(), lsuffix='_<20000', rsuffix='_all')
)
# Доля дешевых квартир по городам
data_price_per_meter['rate'] = (data_price_per_meter['price_per_meter_<20000']/
data_price_per_meter['price_per_meter_all'])*100
df_good.query('price_per_meter > 170000')['cityCenters_nearest'].describe()
# Удаляем выбросы
df_good = df_good.drop(df_good.query('price_per_meter > 170000').index).reset_index(drop=True)
print('Количество квартир в наборе данных:', len(df_good))
Отдельно изучите, зависит ли цена квадратного метра от числа комнат, этажа (первого или последнего), удалённости от центра и даты размещения: дня недели, месяца и года.
# Рассчитывает корреляцию для стоимости квартир
df_good.corr()['last_price'].sort_values()
Наибольшее влияние на стоимость квартиры оказывает общая площадь, стоимость квадратного метра, размер кухни и жилой площади. В меньшей степени на цену жилой недвижимости влияет количество комнат и высота потолков.
corr_price = df_good[['last_price', 'rooms', 'ceiling_height', 'kitchen_area', 'living_area', 'total_area', 'price_per_meter']]
pd.plotting.scatter_matrix(corr_price, figsize=(15, 15))
plt.show()
Промежуточные выводы
1) Зависимость между стоимостью квартиры и ценой за кв.м., а также между ценой и общей площади квартиры положительная и достаточно сильная: с увеличением стоимости квартиры, увеличивается стоимость за квадратный метр и общая площадь помещения.
2) Зависимость между ценой и жилой площадью можно визуально можно разделить на 4 кластера:
Устойчивая связь наблюдается у квартир стоимость 4,5 - 8 млн. и жилой площадью до 50 квадратных метров. Интересно, что квартиры с жилой площади более 50 кв.м. неравномерно связаны со стоимостью квартиры: цена квартиры может быть как до 5 млн., так и более 10 млн. с жилой площадью более 50 кв.м.
3) Устойчивая связь наблюдается между ценой и квартир с кухней до 15 кв.м. Затем с увеличением кухни стоимость квартиры сильно вариьруется.
4) Положительные и линейные связи видны между ценой и высотой потолка. Устойчивую связь можно наблюдать с высотой потолков от 2.5 и до 3.0 метров.
5) Присутствует четкая зависимость между ценой площади и одно-, двух- и трехкомнатных квартир. Стоимость многокомнатных квартир малопредсказуема - прямолинейной зависимости нет.
Выделите сегменты типичных квартир в центре (по удалённости, числу комнат и площади) и вне центра. Сравните корреляцию основных факторов с ценой по всем предложениям и объявлениям в вашей выборке.
df_good.plot(kind='density', y='cityCenters_nearest',label='Удаленность от центра', figsize=(14,5))
plt.xlim((0,df_good['cityCenters_nearest'].max() + 5000))
plt.title('Плотность распределения удаленности от центра')
plt.grid(True)
plt.xlabel('Удаленность от центра, м.')
plt.show()
По распределению удаленности от центра видно, что самые "центровые" квартиры находятся примерно в 8 км. удаленности от центра.
Наибольшее количество квартир сосредоточенно в пределах 10-17 км. удаленности от центра.
# Распределение квартир по населенным пунктам
ax = plt.gca()
df_good.query('locality_name == "Санкт-Петербург"').plot(kind='density', y='cityCenters_nearest', ax=ax, label='Санкт-Петербург', figsize=(14,5))
df_good.query('locality_name != "Санкт-Петербург"').plot(kind='density', y='cityCenters_nearest', ax=ax, label='Остальные населенные пункты', figsize=(14,5))
plt.title('Плотность распределения Санкт-Петербурга и остальных населенных пунктов по удаленности от центра')
plt.xlim((0,df_good['cityCenters_nearest'].max() + 5000))
plt.grid(True)
plt.xlabel('Удаленность от центра, м.')
plt.show()
По распределению видно, что большая часть квартир, которые находятся близко к центру находятся в Санкт-Петербурге. Видимо, алгоритм, который рассчитывает удаленность от центра, считает центром - Санкт-Петербург, а не центр каждого города.
# Проверка количества квартир в каждой группе
print('до 8,5 км.:', len(df_good.query('cityCenters_nearest < 8500')))
print('между 8,5-20 км.:', len(df_good.query('8500 <= cityCenters_nearest < 20000')))
print('более 20 км.:', len(df_good.query('20000 <= cityCenters_nearest ')))
# Функция для категоризации
def category_distance(dis):
if dis < 8500:
return 'до 8,5 км'
elif dis < 20000 and dis >= 8500:
return 'между 8,5-20 км'
elif dis >= 20000:
return 'более 20 км'
# Категоризация
df_good['category'] = df_good['cityCenters_nearest'].apply(category_distance)
# Проверка категоризации
df_good['category'].value_counts()
# Корреляция для квартир по категориям удаленности от центра
data_corr = (
pd.DataFrame(df_good.query('category == "до 8,5 км"').corr()['last_price'])
.join(pd.DataFrame(df_good.query('category == "между 8,5-20 км"').corr()['last_price']),
lsuffix='_до 8,5 км', rsuffix='_между 8,5-20 км')
)
data_corr = data_corr.join(pd.DataFrame(df_good.query('category == "более 20 км"').corr()['last_price']))
data_corr.columns = ['до 8,5 км', 'между 8,5-20 км', 'более 20 км']
data_corr.sort_values(by='более 20 км')
Вывод
Во всех категориях сильная положительная корреляция между ценой и общей площадью квартиры - выше 7,5. Такая высокая корреляция объясняется третьим фактором - жилой площадью.
Площадь кухни в самых "центральных" квартирах влияет на цену меньше, чем в других группах. Больше всего цена зависит от площади кухни в квартирах, которые расположены в среднем сегменте по удаленности от центра.
Количество комнат сильнее всего влияет на стоимость жилья в самых отдаленных районах от центра. Также для этой категории квартир наблюдается положительная связь между ценой и стоимостью квадратного метра. Коэффициент корреляции самый высокий среди остальных категорий удалености - 0,43. К тому же, высота потолков влияет сильнее всего на цену среди остальных категорий центральности.