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

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

Раньше я использовал веб-поиск, чтобы собирать заголовки новостей Google, что было довольно легко, потому что структура веб-сайта, на который я заходил, была очень простой. На этот раз структура была немного более сложной, поэтому позвольте мне сначала объяснить веб-сайт:
Веб-сайт finn.no - это норвежский сайт, на который можно найти практически все. Это не только рынок подержанных товаров, но и рынок недвижимости или вакансий. В разделе недвижимости на сайте представлено около 50 эскизов квартир, и вы можете пролистать несколько десятков страниц.

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

Первая часть собирала «href» или ссылку из каждого эскиза на каждой странице и записывала их в простой список. Во второй части цикл проходит по собранным ссылкам, обращаясь к конкретным веб-адресам один за другим и собирая все детали с каждого веб-сайта, где хранится наиболее интересная информация.

Вот и все о структуре высокого уровня. Я использовал пакет BeautifulSoup для просмотра веб-страниц и собрал атрибуты с помощью множества операторов try- except: Exception pass. Они работают примерно так же, как звучит: они пытаются обработать определенную команду, но если она не работает, код продолжается. Это идеально подходит при работе с содержимым, которое немного отличается, например, веб-страницы, которые имеют очень похожую структуру, но не все элементы одинаковы. Год постройки не указан? Нет проблем, код продолжается без ошибок.

Я собрал подробную информацию во вложенном словаре. На самом высоком уровне href работает как ключ, а значение - это набор атрибутов. Эта коллекция снова состоит из нескольких ключей (имен атрибутов) и значений (значений атрибутов).

Словари имеют гибкий формат и очень быстро преобразуются в аккуратный фрейм данных, используя ключ высокого уровня в качестве индекса и ключи нижнего уровня в качестве столбцов. Они также удобны при сборе нечисловых атрибутов и преобразовании их в фиктивные переменные. Такие атрибуты можно легко собрать в пару "ключ-значение", например {balcony:1}. Позже при преобразовании словаря в фрейм данных ключ превращается в отдельный столбец, а точки данных, не содержащие пары ключ-значение для балкона, получают Noneentry. С помощью одного лайнера и команды Python df.fillna(0) мы можем поменять местами записи None на 0 и сразу получить столбец двоичной фиктивной переменной, указывающий на наличие или отсутствие балкона.

Я прогнал веб-скребок по всем квартирам в Осло один раз, а затем добавил фильтр для «новых сегодня» и настроил планировщик задач, чтобы запускать его ежедневно, ежедневно расширяя набор данных, добавляя около 50–100 новых точек данных. Осло - небольшой город, и зима - медленный период для рынка недвижимости, но через несколько месяцев набор данных составил около 5500 наблюдений. Достаточно, чтобы получить представление и лучшую информацию о рынке.

Найдя интересное объявление на Finn, вы сможете увидеть средние цены за м² жилья, проданного в том же районе, с течением времени и сравнить выбранный вами объект со средним значением.

Возникает соблазн принять это за индикатор, относительно дешевый или дорогой дом, и мне приходилось слышать от риелторов, которые говорили мне, что у определенной квартиры цена м² «ниже, чем в среднем в этом районе». Но если вы видите, что средняя цена составляет 75 000 норвежских крон за м², а рассматриваемая квартира продается по
65 000 евро за м², это не обязательно означает, что это «воровство». Обычно цена за м² выше для небольших квартир и ниже для больших. Таким образом, может быть, что в среднем отображается много небольших квартир с очень высокой ценой за м². Если дом, на который вы смотрите, с ценой «ниже средней» за м² значительно больше по размеру, чем средняя, он все равно может быть дороже за м², чем сравнительно большие апартаменты. в том же районе. Давайте посмотрим, действительно ли цены на м² уменьшаются с размером в моем наборе данных:

Эта тенденция особенно заметна для небольших квартир до 45 м², и кажется почти невозможным найти даже студию 25 м² менее чем за 100 000 норвежских крон за м² (на момент написания почти 10 000 евро). Но небольшие квартиры также часто сгруппированы в наиболее привлекательных местах, поэтому, естественно, цены на м² уже из-за этого уже выше. Исследование одной и той же картины в одном районе уменьшает влияние различий в расположении и показывает, что эффект сохраняется: квартиры меньшего размера имеют более высокие цены за м². Всем известно, что сравнивать средние значения сложно, но стоит подумать о действующих переменных, чтобы избежать грубых ошибочных суждений.

Я обнаружил еще одну очень удобную функцию моей базы данных; Изучая данные, я заметил квартиры, которые загружались более одного раза и по скорректированной цене. Я пришел к выводу, что это подскажет мне направление общего рыночного тренда. Как мы видим, почти все они были скорректированы в сторону понижения с большим скачком в районе -5%!

Что говорит обо всем рынке, что квартиры перезагружаются по более низкой цене? Был ли я прав, когда на короткое время сделал вывод, что цены снижаются? Нет, я не был. Представьте себе распределение квартир от заниженных до завышенных. Квартиры по заниженной цене продаются быстро и, возможно, даже выше начальной цены продажи. В середине спектра устройства со справедливой ценой предложения также продаются без больших проблем и уходят с рынка, так что как заниженная, так и справедливая сторона спектра исчезают из поля зрения. Мы наблюдаем только те единицы, которые были оценены слишком дорого, и нам нужно попробовать еще раз с более конкурентоспособной ценой. Мы никогда не видим, чтобы те, которые были недооценены, загружались по более высокой цене, по которой они были проданы. Здесь играет роль некая форма предвзятого отношения к выживанию, а вовсе не крах норвежского рынка недвижимости. Я по-прежнему считаю, что это полезная информация, если вас интересует конкретная квартира и вы заметили, что она не продавалась по определенной цене, а вернулась дешевле. Представьте, что вы пошли бы на просмотр за такой квартирой. Вы уже знаете, что рынок отклонил его по определенной цене, и вы, вероятно, не сделаете ставку в области первой, неудачной цены. Это также может дать небольшую долю переговорных возможностей, если знать, что сторона продавца потенциально становится нетерпеливой.

Через несколько недель я нашла объявление о квартире, которая мне очень понравилась. Это казалось относительно дешевым, но, учитывая только среднюю цену за м², показанную на Finn.no, как я мог быть уверен. Я подумал, что может помочь прогностическая модель, обученная на моем наборе данных и предсказывающая стоимость интересующей меня квартиры. После очистки данных всегда рекомендуется немного изучить данные и найти функции, которые можно преобразовать или отбросить для лучшего соответствия и производительности. Давайте сначала посмотрим на общую взаимосвязь между площадью м² и ценой на жилье в Осло с нанесенной на нее линейной линией.

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

Глядя на логарифмическую диаграмму рассеяния, мы видим, что разброс разброса уменьшился.

Затем мне нужно было выбрать значимые функции из широкого диапазона собранных атрибутов. Я видел, как люди использовали диаграммы рассеяния для выбора функций. Они исследуют предполагаемую причинно-следственную связь между объясняющей и целевой переменной, наблюдая за сюжетом. Иногда делается вывод, что функция не влияет на целевую переменную, потому что на графике отображается горизонтальная линия. Но диаграмма рассеяния - это просто двухмерное представление многомерных отношений, и как таковая она не раскрывает всей картины. Представьте себе этот пример: допустим, у нас есть дома разного размера и в разных местах. Самый маленький находится прямо в популярном центре города. Остальные расположены по мере удаления от центра города и также становятся все больше с удалением. Негативный эффект увеличения расстояния до центра нивелирует рост цен, который могла бы принести дополнительная площадь. Если вы построите диаграмму рассеяния этих наблюдений с ценой на оси y и размером на оси x, вы можете наблюдать горизонтальную линию и ошибочно заключить, что размер абсолютно не влияет на цену. Мы проповедуем, что корреляция не обязательно подразумевает причинную связь, но отсутствие корреляции также не означает, что не может быть лежащих в основе причинных эффектов. График показывает только два измерения по отдельности, но не дает информации о том, как некоторые функции взаимодействуют друг с другом.

Для исследования такого взаимодействия часто используется корреляционная матрица. В моем случае, однако, важно помнить, как данные появились. Не каждое наблюдение имело ценность для каждой функции на сайте Finn. Риэлторы просто перечисляли все, что, по их мнению, достойно перечисления. Видя, что многие переменные часто появляются с одной и той же формулировкой, может быть предварительный выбор атрибутов, из которых они могут выбирать, но многие «экзотические» функции появляются только один раз для конкретного объявления, предполагая, что они были введены вручную. Поэтому некоторые, казалось бы, надежные переменные не могут считаться надежными, а также показывают неправильную степень корреляции с другими характеристиками. Некоторые объявления, например. заявил, что дом или квартира «центральные». И основной движущей силой цен, конечно же, является район, в котором он расположен, или, как когда-либо проповедует каждый риэлтор: «местоположение, местоположение, местоположение!»

Но не каждая единица жилья, расположенная в центре, также получила этот атрибут в наборе данных. Только те, в которых риелтор хотел особо выделить это качество.
Лучше, чем полагаться на такую ​​слабую функцию, - это создать фиктивные переменные почтового индекса из адреса жилой единицы. Мы не можем перечислить каждый отдельный фактор, который делает определенное место привлекательным, и мы, конечно, не можем получить его от Финна, но мы каким-то образом знаем или принимаем, что некоторые почтовые индексы являются просто хорошими местами. Манекены с почтовым индексом отражают всю необъяснимую привлекательность места, в том числе центральное расположение, отличные школы или низкий уровень преступности. Поскольку почтовые индексы обычно сгруппированы географически (0251 находится рядом с 0252 и т. Д.), Мы используем более высокий уровень, скажем, 3-значные, для группировки почтовых индексов и обеспечения достаточного количества наблюдений для фиктивных почтовых индексов. Чтобы получить макеты почтовых индексов, вы можете использовать очень удобную функцию get_dummies:

df = pd.concat([df, pd.get_dummies(df.post_code3)], axis=1)

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

С другой стороны, непрерывные переменные довольно надежны, поскольку они содержат самую основную информацию.

Из категориальных переменных я нашел наиболее полезными: манекены для типа жилья и собственности, почтовые индексы, наличие балкона, сада, камина (очень распространено и популярно в Норвегии → 25% всех наблюдений!), Лифт , гараж или парковочное место. Я также сохранил несколько более «экзотических» манекенов, если они упоминались достаточно часто (> 10% наблюдений), но не устарели, например, «базовый доступ к сточным водам». Эти переменные включают, например, «Дворник-сервис».

Я запустил две разные модели. Линейная регрессия в спецификации журнала и модель повышения градиента. Я выделил одно наблюдение, которое хотел предсказать перед обучением модели, так что наблюдение действительно «новое». Для линейной модели нет необходимости выполнять разделение на поезд-тест, поскольку переобучение не вызывает беспокойства, если вы ограничиваете функциональную форму своего оценщика.

from sklearn.metrics import mean_squared_errorfrom sklearn.linear_model import LinearRegression
Y = df.price_log
X = df.drop(columns = 'price_log')
regressor = LinearRegression()
regressor.fit(X, Y)
print('Liner Regression R squared: %.4f' % regressor.score(X, Y))
mse_linear = mean_squared_error(Y, regressor.predict(X))
print("MSE: %.4f" % mse_linear)
regressor.predict(ad_for_pred)

Для модели повышения градиента я попробовал несколько настроек параметров, но не тратил слишком много времени на их оптимизацию, так как прогноз существенно не менялся с каждым изменением. R² для тестового набора был немного выше, чем для всего набора в линейной модели (0,89 против 0,85), а среднеквадратичная ошибка уменьшилась (0,012 против 0,014). Модель проста в настройке:

from sklearn.model_selection import train_test_split
from sklearn import ensemble
X_train, X_test, y_train, y_test = train_test_split(X, Y, test_size=0.3, random_state=0)
params = {'n_estimators': 500, 'max_depth': 4, 'min_samples_split': 30, 'min_samples_leaf':10,'learning_rate': 0.1, 'loss': 'ls'}
regressor_gr_boost = ensemble.GradientBoostingRegressor(**params)regressor_gr_boost.fit(X_train, y_train)
mse_gb = mean_squared_error(y_test, regressor_gr_boost.predict(X_test))
print("MSE: %.4f" % mse_gb)
print('Gradient Boosting training set R squared: %.4f' %regressor_gr_boost.score(X_train,  y_train)
print('Gradient Boosting test set R squared: %.4f' %regressor_gr_boost.score(X_test,  y_test)
regressor_gr_boost.predict(ad_for_pred)

Цена интересующего меня объекта была примерно на 5% выше, чем цена предложения, указанная на Finn с линейным прогнозом. Повышение градиента показало разницу даже в 8%. Точный прогноз в этом случае имеет второстепенное значение и, конечно, неверен для евро, учитывая недостатки качества данных, которые обсуждались ранее. Но мы получаем четкое указание на то, что квартира находится в нижней части ценового диапазона, учитывая ее характеристики.

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