в этом блоге
постов
0
комментариев
0
фото
0
видео
0
хосок
0

Статистика по стоимости недвижимости — визуализация на карте

Начну с конца. Это скриншот с некой web-карты, визуализирующей среднюю стоимость недвижимости на вторичном рынке Саратова и Энгельса:

e28b314201954a63a3ebb18c5e29ce2f.png

Цвета на карте можно соотнести с цветами на «легенде», цвет на «легенде» соответствует средней стоимости квадратного метра общей площади в тысячах рублей.

Точка на карте соответствует одному предложению по продаже (на вторичном рынке) квартиры с Авито. Всего таких точек, как видно на «легенде», для построения графика использовалось 4943.
Карта в интерактивном виде доступна на GitHub.

А теперь немного предыстории..
Давным-давно…

… когда будучи студентом мехмата я писал работу по статистической обработке данных по ценам на недвижимость, я случайно нашел полезный документ, — градация районов по ценам на недвижимость, что-то вроде “Ценовые зоны Саратова”. Не знаю, кто ее составлял, но составлена она была правдоподобно, — анализ показал, что (если учесть другие факторы), разница между средней стоимостью квадратного метра квартир из соседних ценовых зон составляла примерно 10%. С тех пор выступать в роли оценщика недвижимости (на бытовом уровне), как и большинству взрослых людей, приходилось не раз.

Идея снова подойти к этому вопросу более “научно” пришла в голову когда я увидел публикацию Jeff Kaufman Boston Apartment Price Maps.

Товарищ сделал следующее:

  • Python скрипт, который парсит с сайта PadMapper данные по стоимости аренды недвижимости.
  • еще скрипт, который используя методы Spatial interpolation, визуализирует эти данные в виде растрового изображения.
  • Наложил этот рисунок на страничку с Google Maps

(Исходники и данные — здесь)

В результате у него получилась такая интерактивная карта распределения стоимости аренды на карте Бостон-а:

e0e8d5f31cfa423ab47c1477ef57b562.png

После первого “Вау, прикольно”, следующая мысль — “А что если попробовать сделать такое же, но для России, только не по стоимости аренды, а по ценам на квадрат недвижимости”.

Сразу оговорюсь, я, по текущему роду деятельности, скорее database developer, и не являюсь специалистом ни по GIS данным, ни по front-end разработке, ни по статистике, ни по Python, поэтому отнеситесь снисходительно к… еще не знаю к чему, но надеюсь этого будет не очень много и вы мне об этом напишете для исправления.

И здесь возникает первая крупная задача

Web Scraping с Авито.
Так как с Python-ом на тот момент я был совсем на “вы”, после изучения HTML кода страниц Авито, свой собственный велосипед я решил написать в виде библиотеки на C#, используя для парсинга AngleSharp.

Не буду сейчас углубляться в особенности реализации, замечу только что:

  • все метаданные парсинга Авито я вынес в отдельный конфигурационный класс, чтобы в будущем, при изменении структуры разметки на Авито, можно было бы просто изменить строку в конфигурационном файле.
  • дабы избежать бана от Авито, пришлось добавлять искусственные задержки в 10 секунд после обработки каждого объявления. (Согласен, — не самый умный способ, но найти подходящий пул прокси -айпишников навскидку не удалось).


В конечном итоге, запустив парсилку на ночь, я получил нужные данные, записанные в табличку в базе

5e3e0480ad4140e0a9b5c40d1763eefb.png

Очистив данные от “Старого фонда”, “Элитных”, и всяких явно неадекватных предложений, я получил выборку из пяти тысяч точек — продажи квартир на вторичном рынке Саратова и Энгельса. И это дало возможность перейти к следующему шагу:

Обработка GIS данных.
Общая постановка задачи:

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

Пример, — сняли в один момент показания с датчиков температуры в разных местах, обработали, получили такую картинку:

bd129cb3a95bd6c4073be2bc123cb520.png

На самом деле, задачи такого рода требуют погружения в тему “Spatial interpolation”. Здесь есть свой математический аппарат, например:

— Inverse Distance Weighting (IDW) Interpolation 
— Kriging
И есть свой инструментарий в составе популярных GIS пакетов, например
— Интерполяция точечных данных qGIS
— GeoStatistical Analyst ArcGIS 
— Use of SAGA GIS for spatial interpolation(kriging) 
— Kriging in R.

IDW в составе qGIS (см первую из ссылок) я попробовал, — результат меня не удовлетворил.

5af8dd0c12f342e7849ba30b747497a1.png

Поэтому, чтобы не завязнуть надолго в освоении нового инструментария, я предпочел воспользоваться готовым Рython скриптом от Jeff Kraufman . Для расчета расстояний и для трансляции GPS координат в X,Y координаты на растре здесь применяется упрощенная линейная формула, без применения используемой в Google MapsWeb Mercator проекции.

Насколько я понял, в скрипте используется вариация на тему Inverse Distance Weighting (IDW), главная (и самая тяжеловесная по расчетной части) в этом скрипте часть здесь в этом коде.

Python код spatial interpolation
Подогнал свой массив данных под нужный формат, убрал из скрипта ту часть, которая делает расчет корреляций относительно bedroom_category (количество комнат), поигрался с входными параметрами, запустил расчет. На моем компе (Intel® Core(TM) i7-6700 CPU, 16GB RAM), расчет с 5 тыс точек на входе и разрешением изображения на выходе 1000*1000 занял примерно полчаса.

Осталось немного поработать с Front-End -ом. Смотрим HTML source карты Бостона от Jeff Kraufman. Немного видоизменяем его под свои данные, и получаем вот такую «картину маслом».

389d5c5e95114cb4b9619bfe751c27d7.png

Для проверки на явные “косяки” в Georeferencing загружаем тот же набор точек в qGIS, подгружаем ГуглоКарты, сравниваем картинки.

0521b77200284b238dffc5fd03a899e3.png

Убедившись, что точки на нарисованной карте находятся на своих местах, а цвета на ней правдоподобно отражают цены на квартиры в нашей деревне, можем выдохнуть — “Yes! Работает!”.

Только вот немного огорчает, что картинка у нас залезла в Волгу. Что естественно, — алгоритм расчета ничего про Волгу не знает.

Стало быть возникает задача:

Обрезать изображение на карте по береговой линии
Попытка найти гео-данные по административным границам городов с учетом береговой линии не увенчалась успехом. Есть Саратов, есть Энгельс, и граница между ними проходит посреди Волги. Ладно, мы пойдем другим путем.

Скачиваем из открытых источников shape file c данными по береговым линиям, загружаем как векторный слой в qGIS, выбираем нужные полигоны, разбираемся в структуре файлов, вытаскиваем полигоны, которые относятся к Волге у Саратова, заливаем данные в БД.

Немного магии типа:

DECLARE @Volga geography; select @Volga= PlacePolygon.MakeValid() from PlacesPolygons where PlacesPolygonID =1003 DECLARE @Saratov geography; select @Saratov= PlacePolygon.MakeValid() from PlacesPolygons where PlacesPolygonID =2 select @Saratov.STDifference(@Volga)
И мы получаем нужный полигон — границы города с учетом береговой линии.

Остается загрузить это в qGIS, загрузить туда же сгенерированную скриптом картинку как растровый слой, провести Georeferencing для этой картинки, и обрезать ее по полигону с границами городов, все стандартными средствами qGIS. Все было бы здорово если бы… мне это удалось сделать. Но увы… с Georeferencing в qGIS у меня отношения не сложились, и после нескольких попыток я решил, что используя spatial функции из Microsoft.SqlServer.Types, проще быстренько сделать очередной велосипед в виде утилиты на шарпе.

C# код для обрезки рисунка по GEO полигону
Загрузили исходный файл с растром, получили на выходе тот же файл, но в немного “обкусанном виде”. Наложили на Google Maps в браузере, смотрим, — на Волгу ничего не залезает, отлично! Смотрим чуть внимательнее и видим… что кажется кое-где откусили лишнее. Косяк в данных или косяк в процедуре? Снова смотрим на загруженные слои в qGIS.

51962870076b47b2ad8693ee68707dd0.png

Да… действительно, кое-где у нас порой… полигоны из слоя водоема явно залезает в город. Прям не Энгельс, а Венеция какая-то… Ладно, нет стремления к совершенству, мы пойдем другим путем.

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

Картинку с картой нам могут дать, например Google Maps static API.

Получаем API_KEY, выясняем, что:

— максимальное разрешение для бесплатного использования =640*640
— параметризации по BoundingBox -, в отличие от BING static maps API гугл не поддерживает, и придется дополнительно соотносить два рисунка в координатной плоскости.

Ладно, пробуем. Получаем от Google Statics Map API некую картинку с картой, покрывающую интересующую нас область, пишем такой код:

C# cropping по картинке
Обрабатываем наш файлик с разрешением 1000*1000 с помощью файлика с разрешением 640*640, получаем… такой результат (здесь для наглядности те точки, которые должны быть Transparent, сделаны желтыми):

d3f83924c6b744118a6f404ec62e6020.jpg

Результат — печальный, в том числе еще и потому, что Гуглокарта масштабируется дискретно, и поэтому с большим запасом перекрывает наш сгенерированный растр. То есть или нужно брать для “обрезки” много карт с подходящим масштабом или… как то все таки найти одну, но с хорошим разрешением.

Я — пошел вторым путем, то есть просто за-скриншотил нужную проекцию обычной гуглокарты из браузера. GPS координаты углов (mapping bound), я узнал, добавив в JavaScript code такое:

google.maps.event.addListener(map, 'bounds_changed', function () { console.log(map.getBounds());
Обработал наш растр с помощью новой карты из сохраненного скриншота, загрузил в браузер и… результат меня — скорее порадовал. Достаточно корректно(в пределах точности GPS позиционирования) “зачистился” даже маленький пруд в местном городском парке.

8e4217f1485f49b8ae992bc66f07c704.png

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

И что дальше?
А дальше возникает много вопросов, на которые я пока не знаю четкого ответа:

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

Ну и… спасибо всем дочитавшим до этого места. Мне было интересно решить практическую задачу, которая оказалась, “rather challenging for me”, надеюсь вам было интересно прочитать о моем опыте.

Источник

F5new

Написал: F5new 23 марта, 06:02

1
mood-o-meter
  • мозг off[0]
  • развлекаюсь[1]
  • мозг on[0]
  • мозг off[0]
  • развлекаюсь[1]
  • мозг on[0]
комментарии | 1
dok   12 апреля, 23:14
FaceBook
ВКонтакте
Загрузка...