ЛОКАЦИЯ — ЭТО НЕ «ЧУЙКА», ЭТО ФИЧИ
Полтора года назад я выкладывала пост про локационные задачи — LSCP, MCLP, p-median — сухой теорией и черновой презентацией. Потом ещё доклад по той же теме. И каждый раз оставалось чувство, что такие вещи бесполезно объяснять текстом, их надо давать в руки.
Поэтому собрала интерактивный планшет: четыре демо по геоаналитике в недвижимости плюс вкладка с разобранными кейсами. Всё считается прямо в браузере, ставить ничего не надо, ползунки крутятся, карта пересчитывается на каждом движении.
📍Планшет: https://m-erts.github.io/geoanalytics-realestate/
НАЧНУ С ZILLOW, ПОТОМУ ЧТО ЭТО ЛУЧШАЯ ИЛЛЮСТРАЦИЯ КО ВСЕМУ ОСТАЛЬНОМУ
В 2021 Zillow закрыл выкуп домов. Убыток сегмента Zillow Homes за третий квартал — $421,6 млн до налогов, это прямо в отчётности. Медианная ошибка публичного Zestimate по домам, которых нет в продаже, — около 7%.
7% — нормальная метрика, не позор. А в Финиксе 93% купленных домов пришлось выставлять ниже цены покупки.
Сразу оговорюсь: выкупали не по публичному Zestimate, там работала своя ценовая модель. Но природа беды одна — модель ровно и уверенно переплачивала в целых районах. Систематически, а не случайно. Я про это писала весной: accuracy ≠ precision, и «high precision + low accuracy» — самое коварное состояние в ГИС. Уверенная ложь.
Бустинг из коробки не читает координаты. Для дерева широта и долгота — просто два числа с плавающей точкой, и оно честно нарежет по ним пороги.
Локацию надо превращать в признаки:
- ячейка гексагональной сетки —
H3от Uber илиS2от Google, и дальше всё агрегируется в неё; - плотность POI внутри ячейки, расстояние до k ближайших конкурентов;
- охват в 15-минутной изохроне по графу улиц, а не циркулем по радиусу.
Про изохроны отдельно. В Лавке я соседей искала роутером по улично-дорожной сети, т.к. в моих задачах важна была транспортная доступность, а не расстояние по прямой. Человек не ходит сквозь заборы и через реку. А циркуль ходит) Буфер честно нарисует вам спрос там, где его нет.
На карте 780 ячеек примерно по 400 м, шесть факторов на ползунках и три пресета. Жильё, стрит-ритейл, инвест-участок.
Оговорка, которую я вписала прямо в интерфейс: в бою эти веса никто не проставляет рукой (т.е. никто не сидит и не гадает, 0.3 там или 0.4). Их получают обучением на факте — выручке, темпах продаж. Тогда это становится настоящей feature importance, а не представлениями о прекрасном.
📍 А ещё скоринг без юридического фильтра — красивая картинка и ничего больше. Это я добавила уже потом, отдельным слоем: зона охраны наследия, приаэродромная территория, водоохранная полоса. Под ограничениями оказалось 165 ячеек из 780. Включаете слой — и планшет считает, сколько площадок из наивной десятки пришлось бы выбросить уже на юридической проверке. На дефолтных весах это 4 из 10. Модель, которая про ограничения не знает, приведёт вас в квартал, где строить нельзя, и узнаете вы об этом не от неё.
Скоринг отвечает на вопрос «где лучше». Есть другой вопрос, и он часто дороже: сколько вообще в городе типов территорий и какой из них рынок пока не заметил.
Тут работает агломеративная кластеризация Уорда. В отличие от k-means дерево строится один раз, без k на входе, а число кластеров выбирают потом — разрезая дендрограмму там, где ветки расходятся далеко. Но выбрать k всё равно придётся, и это решение аналитика, а не свойство данных.
Логика поиска резерва простая: ищем кластер, где транспортная доступность выше средней по городу, а цена земли ниже. Разрыв между этими двумя и есть место, куда рынок ещё не пришёл.
И вот тут самое интересное, из-за чего я вкладку переделала. При семи кластерах резерв находится: доступность на 0,77σ выше средней, цена земли на 0,83σ ниже, разрыв 1,60σ. При восьми — тот же кластер. А при двух, трёх, четырёх, пяти и шести резерв не находится вообще. То есть вывод держится ровно в двух значениях k из семи.
Я вписала эту проверку прямо в интерфейс: планшет перебирает все k и пишет, при каких из них резерв есть. Потому что «мы нашли недооценённый кластер» без такой проверки — это не находка, а свойство ползунка. И на реальных данных первое, что надо сделать с любым кластером, — подвигать k и посмотреть, выживет ли он.
Вторая оговорка, тоже в интерфейсе: цена земли в демо модельная, она собрана из доступности и среды. Значит часть разрыва предопределена весами, а не найдена в данных. В рабочей задаче на её месте медиана цен реальных сделок по району — и только тогда вывод перестаёт быть тавтологией.
Земля и лендбанк. Инстанс-сегментация (Mask R-CNN, YOLO-seg) по ортофото и снимкам с БПЛА, векторизация масок в полигоны, пересечение с кадастром. Зрение тут — самая простая часть. Всё встанет на spatial join по миллионам полигонов, привет PostGIS, на pandas это просто умрёт. У Росреестра под «Умный кадастр» заявлено кол-во около 17 млн объектов к внесению в ЕГРН — вот на таком масштабе это перестаёт быть ноутбучной задачей.
Цена и продукт. Модель Хаффа для зон влияния плюс пространственный лаг: цена объекта зависит от цен соседних сделок, и это надо моделировать явно (PySAL). Иначе бустинг ловит остаточную автокорреляцию и разваливается на первом же новом районе. VK Predict так считает квартирографию и цену каждого лота под заданные темпы продаж.
Коммерция и аренда. Изохроны вместо буферов переворачивают оценку зоны влияния, особенно там, где река или железная дорога. Webster Bank на данных Placer.ai собрал риск-индекс и мониторит им портфель ритейла на $1,3 млрд — т.е. геоданные тут уже не про выбор площадки, это надзор за тем, что куплено.
Риск и эксплуатация. То же зрение, но по свежим аэроснимкам и с детекцией изменений между съёмками. Cape Analytics распознала по снимкам больше 7,5 млн бассейнов в континентальных США, почти 3 млн — только Флорида и Калифорния (данные компании, 2019). В январе 2025 Moody's объявила о покупке Cape. Бассейн меняет и риск, и премию, а в анкете он оказывается далеко не всегда.
ЦЕНА МЕТРА: ЧТО СЧИТАЕТСЯ ИЗ ГЕОДАННЫХ, А ЧТО СПРАШИВАЮТ У ПРОДАВЦА
Коэффициенты брала из публичных исследований, и самое поучительное в них — контринтуитивность.
- КБ Стрелка (я там раньше работала) на 71,2 тыс. московских объявлений: близость метро удорожает квартиру всего на +1,5%, а дальше 500 м цена начинает падать. Полтора процента! При том что в объявлении метро — первая строка.
- И это про квартиры на продажу. У офисов метрика другая — ставка аренды, и пешие минуты бьют там совсем иначе: по замерам Times Estate офис в пяти минутах от метро сдаётся в среднем за 1,43 млн ₽ в месяц, в десяти — уже на 20% дешевле, а в получасе ходьбы за 885 тыс., то есть почти на 40% ниже пятиминутной отметки (последнее — мой пересчёт, в источнике этой цифры нет).
Вклады в демо считаются последовательно, от накопленной цены, поэтому сумма зависит от порядка строк. Настоящий SHAP аддитивен и от порядка не зависит. Так что это водопад в стиле SHAP, а не SHAP :)
Кстати про то, зачем вообще переводить SHAP в рубли. В июне вышла работа Saiu и Mocci в Urban Science — фреймворк RE-VAL на 1153 объявлениях в Кальяри. Идея ровно та же, что на этой вкладке: денормализовать вклады обратно в деньги и свести в таблицу надбавок и скидок, чтобы вывод модели лёг в логику сравнительного подхода IVS и RICS, где оценщик обязан документировать каждую корректировку. Оговорюсь честно: заполненной денежной таблицы в самой публикации нет, это заявленный механизм, и считалось на ценах предложения, а не сделок.
Там же есть результат, который стоит держать в голове всем, кто выбирает модель по третьему знаку R². Пять моделей, лучшие агрегированные метрики у нейросети — но отрыв от обычной линейной регрессии и случайного леса маленький, авторы прямо пишут, что нелинейные модели большого преимущества не дали. Градиентный бустинг LightGBM в тройку лидеров вообще не попал. А вывод авторов — Random Forest, как лучший баланс точности и интерпретируемости.
📍 И два места, где я сама наступила на грабли, пока это собирала.
- Я подписала базу в водопаде как «средняя цена по городу» — и когда сделала режим, который красит всю карту вкладом локации, вылезло, что положительный вклад только у 3% ячеек. Потому что база была не средней, а идеальной: центр, метро в пятиминутке, без промзоны. Мораль простая: если у вас «вклад» почти везде отрицательный, проверьте не модель, а от чего вы его считаете. Теперь база честно подписана точкой отсчёта — это значение до применения поправок, а не цена какой-то реальной ячейки, — а содержательным нулём на карте служит медиана города, посчитанная из самих ячеек.
- Коэффициент метро измерен на близости к станции — у Стрелки это первые сотни метров. Я продолжила его линейно на весь город, и в 316 ячейках из 780 работает уже не измеренная зависимость, а искусственный пол в −18%. Это классика гедонических моделей: коэффициент, вынесенный за диапазон, на котором его оценивали, перестаёт что-либо измерять. Прятать не стала — такие строки в разборе ячейки теперь помечены словом «экстраполяция», и видно, что это почти половина карты.
Кейс бытовой. Пока объектов пять, «кто едет куда» решает прораб в общем чате. На пятидесяти по всему городу это уже локационная задача, которой полвека:
- p-median (Hakimi, 1965) — минимум суммарного подъезда. Дешевле всего в среднем, окраины страдают.
- p-center (Hakimi, 1964) — минимум самого долгого подъезда. Постановка под SLA «мастер на объекте за N минут».
- MCLP (Church, ReVelle, 1974) — максимум доли объектов внутри норматива при фиксированном числе бригад. Модель честно признаёт, что бюджета покрыть всё не хватит.
Одна карта, 58 объектов, четыре бригады, норматив подъезда 20 минут. p-median даёт средний подъезд 10,6 мин при худшем 29,9. p-center — средний 12,2 при худшем 20,4.
А теперь смотрите строку «загрузка бригад». Доля работы внутри норматива у обеих постановок одинаковая, 97% по объёму, — но p-center раскидал работу как 12 и 50 единиц. На моей карте вышло так: оптимизируешь худший случай, ломаешь загрузку.
📍 БАЛАНСИРОВКА. Если просто гнать каждый объект к ближайшей базе, у одной бригады окажется 43 единицы работы, у другой 27. Ставим потолок — общий объём ÷ число бригад с округлением вверх, у меня это 145 ÷ 4 = 37 — и раздаём объекты по убыванию «сожаления», т.е. разницы во времени между ближайшей базой и следующей. Кто больше теряет от переброски, выбирает первым.
Загрузка сходится к 35–37. Средний подъезд растёт с 10,6 до 11,4 минуты, худший уезжает до 38, а покрытие падает с 97 до 92%. За справедливость платят минутами, и в договоре это лучше обсудить заранее, а не в мае.
Кст похожая логика в реновации, только оптимизируют там не минуты. Ст. 7.3 закона о статусе столицы требует дать жильё в том же районе Москвы (исключение — Зеленоградский, Троицкий и Новомосковский округа), а переселение идёт волнами через стартовые площадки. Класс задачи там другой — назначение с расписанием, — но цена решения та же: срок программы и число людей, которых всё-таки увезли из своего района.
Тут две картинки. Слева выборку разбили случайно: квартиры одного дома попали и в обучение, и в тест, у каждого тестового объекта соседи по трейну стоят в том же подъезде, и модель просто копирует их цену. MAPE 1,0%. Справа дом уезжает в тест целиком. MAPE 7,6%. Модель одна и та же, данные те же, разбиение разное.
В демо это можно потрогать руками, и вот это, по-моему, лучший способ понять утечку раз и навсегда. Щёлкаете по любой тестовой точке — и от неё идут линии к тем k объявлениям из обучающей выборки, по которым модель считает ответ. В случайном сплите все пять соседей оказываются в том же доме, и ошибка на точке 0,7%. Переключаете на сплит по домам — ни один из пяти соседей уже не в её доме, ошибка 4,8%. Ползунок «разброс между домами» показывает вторую половину механики: чем сильнее дома отличаются друг от друга, тем крупнее становятся тестовые точки в честном сплите, потому что радиус точки — это её ошибка. А в случайном они остаются мелкими при любом разбросе. Утечка держит метрику красивой независимо ни от чего.
Кст в демо я специально взяла kNN по координатам — тот самый, про который делала отдельный пост, почему он плох для геоданных. Именно его, потому что на нём утечка видна голыми глазами. Бустинг с гео-фичами устроен иначе, он режет пороги, но в эту же ловушку попадает — просто незаметнее.
Лечится Spatial K-Fold: в фолд целиком уезжает дом или кластер DBSCAN.
А дальше я собиралась написать: «а если модель поедет в новый район, то режьте фолды блоками карты, там ошибка будет ещё выше». Написала, посмотрела на фразу и полезла считать. Считала на том же демо, только честно: не один отложенный кусок, а полная кросс-валидация — каждая из 468 квартир ровно один раз оказывается в тесте, чтобы нельзя было подобрать удачный кусок. Из моей фразы выжила примерно половина.
Случайный сплит — 1,13%. Блоки карты, нарезанные по точкам, — 5,94%. Те же блоки, но собранные из целых домов, — 6,79%. По домам, leave-one-building-out, — 7,30%. Модель везде одна и та же, у обоих блочных вариантов и сетка одна и та же: 4 × 2 на всю карту.
Смотрите на вторую и третью строчку. Между ними не разный размер блока и не разное число фолдов — между ними одно правило: куда девать дом, которому граница блока прошла по двору. Если нарезать по точкам, часть его квартир уезжает в тест, часть остаётся в обучении. Таких домов у меня 6 из 26. Из-за них у 23% квартир среди пяти ближайших соседей снова оказывается кто-то из своего же дома — то есть та самая утечка, от которой мы только что лечились, просто в меньшем масштабе.
Полезла смотреть на эти 108 квартир отдельно. На них наивная нарезка показывает 1,22% вместо 4,93% — занижение вчетверо. А в метрике по всей карте это размывается до 5,94% против 6,79%: наивная цифра ниже честной на 12%, такую разницу легко списать на шум. Поэтому такая проверка спокойно может жить в проде годами. Сломанной она не выглядит, выглядит как удачный эксперимент, и всё.
Теперь про вторую половину моей фразы, которая не выжила. «Блоками — ещё выше» оказалось неправдой: 6,79% у блоков против 7,30% у leave-one-building-out. Я разложила промах на две части — «какая у дома своя надбавка» и «где этот дом стоит». У блоков вторая часть больше: соседей приходится искать в 98 пикселях вместо 45, и ошибка от географии вырастает с 2,64% до 4,87%. Зато первая часть чуть меньше — 5,98% против 6,20%. В сумме они частично гасят друг друга, и итог получается ниже. Разрыв маленький и неустойчивый: если поставить разброс между домами в ноль, блоки и дома меняются местами — 4,66% против 2,85%.
Так что «что строже» вопрос неправильный. Боитесь незнакомого дома в знакомом районе - leave-one-building-out. Боитесь нового района - блоки, но собранные из целых домов. А фольклор «блоки строже» на своих данных лучше проверить, чем пересказать.
Практическая часть на три строки кода. Кроме MAPE логируйте на каждом фолде среднее число соседей тестового объекта, попавших с ним в одну группу — в один дом или в одну сделку (у нас бывает ещё и один ЖК). Если это число не ноль, кросс-валидация мерит не то, что вы думаете. Лучше это увижу я, чем заказчик :) У меня в демо это отдельная строчка под каждым способом нарезки: 4,88 из 5 у случайного сплита, 1,09 у блоков по точкам, ровно 0 у двух честных.
В планшете все четыре нарезки живые, и ползунок «разброс между домами» действует на них тоже, можно самому найти точку, где блоки и дома меняются местами.