Пересчитать asking_to_sold_ratios — бакет ≥85 м² занижен на 5%, остаток занижения крупного жилья #3256

Open
opened 2026-08-29 20:14:10 +00:00 by lekss361 · 1 comment
Owner

Контекст

Follow-up к #3248 (PR #3255). Перефит хедоники снял половину занижения крупного жилья (4+ комнат −21.4% → −10.4%), но остаток коэффициентами не закрывается — он лежит в самой таблице asking_to_sold_ratios.

Что не сходится

Замер на свежей прод-фикстуре (1269 оценённых сделок ЕКБ с 2025-06):

бакет площадь ratio в таблице фактический sold/ask промах
0 <30 м² 0.7742 0.7859 −1.5%
1 30-44 0.8648 0.8286 +4.4%
2 44-62 0.9362 0.8988 +4.2%
3 62-85 0.9313 0.9535 −2.3%
4 ≥85 м² 0.8211 0.8640 −5.0%

Бакет 4 занижен на 5% ещё до всякой хедоники — это и есть остаточные −10.4% по 4+ комнатам (после умножения на факторы).

Что сделать

  1. Перезапустить расчёт app/tasks/asking_to_sold_ratio.py и сверить выход с таблицей выше. Если расхождение воспроизводится — разобраться, откуда оно: окно периода, фильтры sanity-band, состав ask_side, или тонкие бакеты.
  2. Проверить достаточность выборки по бакетам. Сейчас n_deals: 2249 / 6243 / 4928 / 2954 / 2156. Бакет 4 не самый тонкий, так что дело вряд ли в объёме — скорее в составе.
  3. Отдельно проверить, не мешает ли методике то, что сделки Росреестра геокодированы до улицы, а не до дома (см. каверзу (d) в докстринге scripts/backtest_estimator.py, добавлена в #3254). Если ask_side собирается по радиусу от центроида улицы, для редкой крупной застройки состав объявлений systematically смещён.

Критерий приёмки

На прогоне по БД (ЕКБ, sample ≥1200, --resolve-house-id) в новом разрезе per_area_bucket (добавлен в #3254):

  • ни один бакет по модулю не хуже 8%
  • в частности бакет 4 не хуже 8% (сейчас −10.4%)
  • общий MAPE не хуже 14.28%

Проверять чем

docker exec tradein-backend python -m scripts.backtest_estimator   --city 'Екатеринбург' --sample 1500 --since 2025-06-01 --resolve-house-id --json

Смотреть блок expected_sold.per_area_bucket — ось независима от исхода, в отличие от per_segment (тот врёт по построению, см. #3254 и предупреждение в конфиге из #3249).

## Контекст Follow-up к #3248 (PR #3255). Перефит хедоники снял половину занижения крупного жилья (4+ комнат −21.4% → −10.4%), но остаток коэффициентами не закрывается — он лежит в самой таблице `asking_to_sold_ratios`. ## Что не сходится Замер на свежей прод-фикстуре (1269 оценённых сделок ЕКБ с 2025-06): | бакет | площадь | ratio в таблице | фактический `sold/ask` | промах | |---|---|---|---|---| | 0 | <30 м² | 0.7742 | 0.7859 | −1.5% | | 1 | 30-44 | 0.8648 | 0.8286 | **+4.4%** | | 2 | 44-62 | 0.9362 | 0.8988 | **+4.2%** | | 3 | 62-85 | 0.9313 | 0.9535 | −2.3% | | 4 | ≥85 м² | **0.8211** | **0.8640** | **−5.0%** | Бакет 4 занижен на 5% ещё до всякой хедоники — это и есть остаточные −10.4% по 4+ комнатам (после умножения на факторы). ## Что сделать 1. Перезапустить расчёт `app/tasks/asking_to_sold_ratio.py` и сверить выход с таблицей выше. Если расхождение воспроизводится — разобраться, откуда оно: окно периода, фильтры sanity-band, состав `ask_side`, или тонкие бакеты. 2. Проверить достаточность выборки по бакетам. Сейчас `n_deals`: 2249 / 6243 / 4928 / 2954 / 2156. Бакет 4 не самый тонкий, так что дело вряд ли в объёме — скорее в составе. 3. Отдельно проверить, не мешает ли методике то, что сделки Росреестра геокодированы **до улицы, а не до дома** (см. каверзу (d) в докстринге `scripts/backtest_estimator.py`, добавлена в #3254). Если `ask_side` собирается по радиусу от центроида улицы, для редкой крупной застройки состав объявлений systematically смещён. ## Критерий приёмки На прогоне по БД (ЕКБ, sample ≥1200, `--resolve-house-id`) в новом разрезе `per_area_bucket` (добавлен в #3254): - ни один бакет по модулю не хуже 8% - в частности бакет 4 не хуже 8% (сейчас −10.4%) - общий MAPE не хуже 14.28% ## Проверять чем ``` docker exec tradein-backend python -m scripts.backtest_estimator --city 'Екатеринбург' --sample 1500 --since 2025-06-01 --resolve-house-id --json ``` Смотреть блок `expected_sold.per_area_bucket` — ось независима от исхода, в отличие от `per_segment` (тот врёт по построению, см. #3254 и предупреждение в конфиге из #3249).
Collaborator

Приёмка PR #3445 на проде (12.09, BUILD_SHA=c4315b3).

Маркеры (признак ОТСУТСТВУЕТ в старой версии):

предикат "AND d.rooms = " в app/services/estimator.py — 0 вхождений   (было 2)
докстринг «Фильтра по rooms тут НЕТ осознанно» — 1                     (было 0)

Живая проверка на адресе из замера — Серафимы Дерябиной, 41, 1к/49 м² (в разборе он терял коридор: 18 сделок при старом ключе → 8 при варианте area_bucket, и 26 без предиката):

POST /api/v1/trade-in/estimate → 200 за 0.92 с
коридор ДКП: count=26, low=98 192, median=119 596, high=141 850 ₽/м², окно 12 мес

26 сделок — ровно предсказанное замером значение, то есть выборка действительно перестала резаться вторым фильтром по площади.

Оговорка про доезд. Первый деплой после мержа не состоялся: в CI упала джоба test, и deploy из-за этого не выполнился — но в статусах коммита он показан как success (Forgejo рисует пропущенную джобу зелёной). Увидел это только по маркеру в контейнере: BUILD_SHA оставался прежним, а предикат — на месте. Тот же класс, что #3448.

Причина красного test не установлена. Полный сьют на том же коммите зелёный локально и на 3.14, и на 3.12 (версия CI), и с CI-окружением (DATABASE_URL задан) — 5753 passed каждый раз; раннер здоров (диск 50 %, 7.4 ГБ свободной памяти). Перезапуск деплоя через workflow_dispatch прошёл, код доехал. Логи джобы через API Forgejo недоступны (404 на всех эндпоинтах actions/runs/*), поэтому честно: воспроизвести не удалось, «флейк» — предположение, а не вывод. Если повторится — искать по-настоящему.

Критерии приёмки самой issue (бакет ≥85 м² не хуже 8 %, MAPE не хуже 14.28) по-прежнему не выполнены и, как показано в PR, недостижимы честно по этой оси — она слепа, пока deals.rooms синтетика. Issue оставляю открытой: в ней теперь зафиксировано, почему её критерий нельзя брать за цель, и что нужно вместо него (источник настоящей комнатности сделок).

**Приёмка PR #3445 на проде (12.09, `BUILD_SHA=c4315b3`).** Маркеры (признак ОТСУТСТВУЕТ в старой версии): ``` предикат "AND d.rooms = " в app/services/estimator.py — 0 вхождений (было 2) докстринг «Фильтра по rooms тут НЕТ осознанно» — 1 (было 0) ``` Живая проверка на адресе из замера — Серафимы Дерябиной, 41, 1к/49 м² (в разборе он терял коридор: 18 сделок при старом ключе → 8 при варианте `area_bucket`, и 26 без предиката): ``` POST /api/v1/trade-in/estimate → 200 за 0.92 с коридор ДКП: count=26, low=98 192, median=119 596, high=141 850 ₽/м², окно 12 мес ``` **26 сделок** — ровно предсказанное замером значение, то есть выборка действительно перестала резаться вторым фильтром по площади. **Оговорка про доезд.** Первый деплой после мержа не состоялся: в CI упала джоба `test`, и `deploy` из-за этого не выполнился — но в статусах коммита он показан как `success` (Forgejo рисует пропущенную джобу зелёной). Увидел это только по маркеру в контейнере: BUILD_SHA оставался прежним, а предикат — на месте. Тот же класс, что #3448. **Причина красного `test` не установлена.** Полный сьют на том же коммите зелёный локально и на 3.14, и на 3.12 (версия CI), и с CI-окружением (`DATABASE_URL` задан) — 5753 passed каждый раз; раннер здоров (диск 50 %, 7.4 ГБ свободной памяти). Перезапуск деплоя через `workflow_dispatch` прошёл, код доехал. Логи джобы через API Forgejo недоступны (404 на всех эндпоинтах `actions/runs/*`), поэтому честно: воспроизвести не удалось, «флейк» — предположение, а не вывод. Если повторится — искать по-настоящему. Критерии приёмки самой issue (бакет ≥85 м² не хуже 8 %, MAPE не хуже 14.28) по-прежнему **не выполнены** и, как показано в PR, недостижимы честно по этой оси — она слепа, пока `deals.rooms` синтетика. Issue оставляю открытой: в ней теперь зафиксировано, почему её критерий нельзя брать за цель, и что нужно вместо него (источник настоящей комнатности сделок).
Sign in to join this conversation.
No milestone
No project
No assignees
2 participants
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference: lekss361/gendesign#3256
No description provided.