fix(forecast): segment + dedup demand/supply → индекс дефицита больше не −1.00 везде (#1959) #2018
No reviewers
Labels
No labels
admin
analytics
auth
automation
bug
business
chore
ci
compliance
data
data-moat
docs
duplicate
dx
enhancement
Fable 5 ревью
feedback/max
generative
GG-форсайт
needs-discussion
needs-human
observability
pause-bots
performance
priority/p0
priority/p1
priority/p2
priority/p3
scope/backend
scope/db
scope/devops
scope/frontend
scope/qa
scrapers
security
site-finder
stage/1
stage/2
status/blocked
status/done
status/needs-analysis
status/needs-fix
status/qa
status/ready
status/review
status/wip
tech-debt
tradein
ux
week ревью 1
wontfix
вторичка
ИРД
No milestone
No project
No assignees
1 participant
Notifications
Due date
No due date set.
Dependencies
No dependencies set.
Reference: lekss361/gendesign#2018
Loading…
Add table
Reference in a new issue
No description provided.
Delete branch "fix/deficit-segmentation-1959"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Батч 4 аудита (эпик #1953) — главный data-honesty фикс. deep-code-reviewer ✅ APPROVE (no blockers), прод-verified.
Проблема
compute_demand_supply_forecastкормил КАЖДУЮ ячейкуwhat_to_buildодинаковым district-wide спросом (unit_velocity=847.5/мес, все классы/комнаты) и одинаковым стоком района (n_available=62961) → одинаковый ratio → всеdeficit_indexклампятся в −1.0. Плюсobjective_lots= append-per-snapshot (~2.9× инфляция, симметрично бьёт спрос И предложение).Фикс (сегментация + дедуп)
market_metrics.py:compute_market_metrics(+obj_class,+room_bucket);_STOCK_SQL/_SALES_WINDOW_SQLпереписаны на DISTINCT ON физ-ключа → latest snapshot, затем агрегат; фильтр класса (LOWER=LOWER) + room-bucket (Source-Broom_area, зеркалоroom_area_bucket_of). Confidence по deduped-счётчикам.demand_supply_forecast.py: base_pace + open-сток теперь посегментные; L2/L3 (class/format-agnostic) исключены из посегментного баланса (иначе double-count → ре-вырождение). Валидирующий vocab-транслятор_market_room_bucket.what_to_build.py/recommendation.py:эконом→стандарт(в objective_lots нет «эконом»; стандарт=483k лотов — старая ячейка матчила 0 строк, тихо дропалась).report_assembler.py: honesty-guard — когда все измеренные ячейки|di|≥0.999, печатать «недостаточно гранулярных данных для посегментного вывода» вместо «строить»/«избегать».data/sql/173_objective_lots_physflat_idx.sql(renamed из коллизии 100→173): partial-индекс под DISTINCT ON; prod dry-run → Index Only Scan, no Sort на 1.75M.Прод-verify (66:41:0205010:287, h=24)
Было: все ячейки = velocity 847.5 + n_avail 62961 → deficit −1.0 везде.
Стало: 12 ячеек, 7 разных значений → PASS (дифференцируются). Напр.: комфорт/студия −0.106 (MOI 49.8), бизнес/1-к −0.312, комфорт/1-к −0.772, стандарт/студия −1.0 (MOI 316). MOI комфорт/студия 38.5 vs стандарт/студия 244.3 — точно по спеку.
Blast radius / scope (честно, по deep-review)
Дедуп в
compute_market_metricsбезусловный → влияет на все 9 потребителей (не только forecast). deep-review протрассировал каждый: безопасно — ratio-потребители (overstock/MOI/absorption) стабильны (дедуп схлопывает оба члена), velocity-потребители в зоне насыщения (847→292/мес → demand-скор 0.991→0.973, негладко лишь на низких velocity),demand_indexcity-relative (ранжирование сохранено). Считаем физ-квартиры, а не snapshot-строки = честнее. Платформенный дедуп raw-таблицы остаётся #1964.⚠️ Деплой-окно: индекс 173 на 1.75M строк → краткий SHARE-lock блокирует objective-скрейп-UPSERT на время сборки. Применять вне окна скрейпа (скрейп недельный; сейчас сб).
Качество
:name::type; 1424 passed (+ regression-тесты: «ячейки различаются не все −1.0», vocab-транслятор, honesty-guard, dedup-SQL, segmented/unsegmented split).Closes #1959. Coupled follow-up: #1964 (platform-wide raw-table дедуп).