Как мы проверили, есть ли у зон MACD разные «виды» — и получили отрицательный ответ
Вопрос
Первая же кластеризация зон дала всего два грубых типа — «трендовая» и «пила». Ожидание было другим: зоопарк разнообразных видов, у каждого своя внутренняя механика.
Отсюда вопрос, который легко задать и трудно ответить честно:
Два типа — это потолок наших входов (мы описали зону слишком бедно) или потолок самих данных (разных видов там просто нет)?
Разница не академическая. Если видов много, продукт — каталог: определи вид, возьми его статистику. Если это континуум, каталог будет самообманом, а продукт — условные распределения.
Почему мы не стали просто перебирать кластеризаторы
Соблазн был очевидный: прогнать десяток алгоритмов и посмотреть, где картинка красивее. Так делать нельзя — любой партиционный метод обязан что-нибудь разделить. Дай ему облако точек без всякой структуры, попроси четыре кластера — он вернёт четыре кластера. Красота картинки в этом случае измеряет упорство алгоритма, а не устройство данных.
Поэтому сначала — гейт: проверить формально, есть ли многомодальность вообще и вдоль каких осей. И только если она есть, выбирать k и алгоритм.
Метод
Пять способов описать одну и ту же зону — от бедного к богатому:
что это
R0
8 ручных скаляров — исходный базовый набор
R1
15 обогащённых скаляров: позиционный профиль импульса и отката, квантили глубин, длительности ног, форма горба
R2
функциональный анализ формы кривой (масштаб убран)
R3
то же для амплитуды — контраст: ось размера
R4
сигнатуры пути 3-го порядка
И батарея проверок, каждая из которых считается до всякой кластеризации: Hopkins, dip-тест Хартигана по осям и по распределению попарных расстояний, кривая BIC, и главное — prediction strength: разделение, найденное на одной половине данных, проверяется на другой. Воспроизводится ли оно вообще.
Что получилось
показать код, которым посчитано
import plotly.graph_objects as gobattery = study("Z1a/battery.json")NAMES = {"R0_v0scalars": "R0 · 8 скаляров", "R1_enriched": "R1 · обогащённые","R2_FPCA_shape": "R2 · форма", "R3_FPCA_amp": "R3 · амплитуда","R4_signatures": "R4 · сигнатуры"}t = _theme("light")COLORS = [t["muted"], t["fan"], t["median"], t["actual"], t["green"]]fig = go.Figure()for (row, color) inzip(battery, COLORS): ps = row["pred_strength"] ks =sorted(int(k) for k in ps) fig.add_trace(go.Scatter(x=ks, y=[ps[str(k)] for k in ks], mode="lines+markers", name=NAMES[row["repr"]], line=dict(color=color, width=2.2)))fig.add_hline(y=0.8, line=dict(color=t["muted"], width=1, dash="dash"))_layout(fig, t, xtitle="число кластеров k", ytitle="prediction strength", height=380, title="Воспроизводится ли разбиение на независимых данных")fig.update_xaxes(dtick=1)show(fig)
Figure 1: Prediction strength: воспроизводится ли разбиение на независимых данных. Пунктир — общепринятый порог 0.8.
Читается это так: на k=2 держатся все пять представлений, а дальше кривые обрушиваются. Исключение — представления формы и амплитуды, у которых воспроизводимость дотягивает до k=3 и k=4. Но обогащение входов (R0 → R1, восемь скаляров против пятнадцати) картину не меняет: если бы дело было в бедности описания, именно здесь и появились бы новые воспроизводимые разделения.
Многомодальности нет ни по одной оси
Figure 2: Dip-тест Хартигана. Ниже пунктира — распределение значимо не унимодально. Ось значений логарифмическая.
Один-единственный столбик уходит под порог — расстояния в пространстве амплитуды. То есть многомодальность в данных всё-таки есть, и она ровно одна: по размеру зоны. Большие зоны отличаются от маленьких. Это не «виды», это шкала.
Проверка чужим методом
Партиционные методы обязаны делить всё — значит они могли бы размазать плотные острова по вынужденным разделам. Поэтому вердикт проверялся методом с другой посылкой: плотностной кластеризацией, которая делить не обязана и имеет право сказать «здесь шум».
Figure 3: Доля точек, помеченных шумом. 1.00 означает: ни одной области, достаточно плотной, чтобы засеять кластер.
Во всех пространствах и при всех параметрах — всё шум. Это учебная сигнатура континуума.
Зелёный столбик справа важнее остальных: это позитивный контроль. Тот же алгоритм, с той же настройкой, запущен на трёх заведомо разделённых облаках той же размерности — и нашёл их. Без этого контроля результат ничего не стоил бы: «ничего не найдено» и «искать не умеет» выглядят одинаково.
Дополнительно метод прогнали на самых мягких настройках, какие имеют смысл. Результат: один гигантский блоб и пылинка из пяти точек. Не острова.
Что оказалось ложной многомодальностью
Отдельная часть работы — проверить признаки поштучно и понять, что именно даёт «многомодальный» сигнал там, где он появляется.
Table 1
признак
p dip-теста
различных значений
что это на самом деле
n_peaks
0.0004
8
integer-count
cnt_b2
0.0073
2170
continuous
Из всех признаков значимо не унимодальны единицы, и по крайней мере один — артефакт счётчика: у признака восемь различных значений на две с лишним тысячи зон, потому что это целое число пиков. Dip-тест видит дискретность и честно сообщает о многомодальности, но это многомодальность шкалы измерения, а не структуры данных.
Ещё один признак оказался вырожденной константой: он всегда равен единице, потому что свинги чередуются по построению алгоритма разметки. Такой признак не несёт информации вообще, и его удалили.
NoteПочему BIC здесь не судья
Информационный критерий на всех пяти представлениях уверенно тянет к шести-восьми компонентам. Соблазнительно прочитать это как «видов много».
Так читать нельзя. На континууме BIC монотонно любит больше компонент: каждая новая гауссиана чуть лучше облегает непрерывное облако, и выигрыш никогда не обнуляется, он лишь становится всё меньше. Смотреть надо на выигрыш на точку данных — он здесь исчезающе мал.
Именно так родилось раннее «оптимальное k=5», от которого пришлось отказаться.
Вердикт
Разделённого зоопарка нет. Зоны образуют континуум: все оси унимодальны, единственная многомодальность — по размеру, воспроизводимое разбиение существует только на двух кластерах, а плотностной метод не находит островов даже на самых мягких настройках.
Обогащение описания зоны — с восьми признаков до пятнадцати, плюс функциональная форма, плюс сигнатуры пути — этого не изменило. Значит потолок был не в наших входах, а в данных.
Что это изменило в работе
Гейт был поставлен именно затем, чтобы отменять планы, и он их отменил:
каталог видов не строится — строить его значило бы продавать бины континуума как биологию;
порядок работ развернулся: продукт стал приоритетнее таксономии;
меню кластеризаторов перецелено: их роль теперь не «найти зоопарк», а очертить принципиальные бины непрерывных осей — для языка и коммуникации, а не для механики;
изделием стало распределение, а не класс: ансамбль сценариев продолжения с честной мерой доверия — то, что показано на главной.
Отрицательный результат здесь — самый полезный из возможных: он стоил одного прогона батареи и сэкономил ветку разработки, которая выглядела очевидной.