Регионы кроме 66 фильтровали обе стороны ТОЛЬКО по region_code и соединяли их
ТОЛЬКО по бакету комнат — sold-медиана и ask-медиана считались по разным
географическим популяциям одного региона. Объявления смещены к дальней дешёвой
периферии сильнее, чем сделки, поэтому область 50 давала 0.891 при 0.73 у Москвы
и 0.73 у ЕКБ; разложение по кольцам 10 км показывает 0.808/0.847/0.951/0.688
ВНУТРИ колец и 0.81-0.85 в ближних кольцах, где лежит 76% сделок.
Обе стороны теперь раскладываются по одной регулярной сетке 0.1°x0.2° (~11x12-16 км
на широтах 45-60°N — масштаб, на котором замер показал устойчивость отношения),
медианы берутся внутри ячейки, в итог идут только ячейки с обеими сторонами, и
агрегация взвешена числом СДЕЛОК: ratio = Σ(w·sold)/Σ(w·ask). Сетка, а не кольца,
потому что у региона 50 нет своего города-центра; совмещение по названию
муниципалитета невозможно — listings.city там пуста (3 строки из 70 996).
Путь ЕКБ (_REDERIVE_SQL) не тронут — там своя историческая калибровка городской квотой.
Гарды и наблюдаемость: регион не получает НИ ОДНОЙ строки (старые всё равно
удаляются), если сторон без geom больше половины, ячеек с обеими сторонами <3 или
в пересечение попало <50% геокодированных сделок — оценка честно остаётся без
коэффициента вместо неверного. Порегионные счётчики: ячеек на сторону, ячеек
отброшено по порогу, доля сделок/объявлений вне пересечения и доля строк без geom
(>25% — WARNING, чтобы негеокодированные строки не выпадали молча).
district остаётся пустым: ячейка — промежуточная единица расчёта, а не публикации,
а потребитель читает строго district = '' (гео-разрез — отдельная задача #647).