Echo
README.md
# Echo — объяснимые аномалии во временных рядах через Matrix Profile
Единственный проект в этой серии, который меняет не только масштаб, а
**домен и математический аппарат**. Вместо дискретных символьных систем
(git, файлы, политики) — непрерывные сигналы: показания датчиков,
метрики серверов, биометрика, финансовые ряды.
## Почему Matrix Profile, а не просто "порог + z-score"
Наивное обнаружение аномалий ("если значение выше среднего на 3σ — тревога")
не объясняет *почему* что-то аномально и плохо работает с формой сигнала —
пропускает "структурные" аномалии (например, правильные значения, но не в
то время суток). Matrix Profile для каждого фрагмента сигнала находит его
**ближайшего двойника** во всей остальной истории. Нет хорошего двойника —
аномалия, и вот **её конкретное объяснение**: "вот с чем я тебя сравнивал,
и даже это не похоже".
## Демонстрация на реалистичных данных
240 часов (10 дней) почасовых показаний датчика температуры с естественным
суточным циклом. В часы 120–131 датчик "застревает" на 23°C, не падая
ночью, как во все остальные дни.
```
ANOMALIES (discords):
Subsequence at [100:124] doesn't resemble anything else in the series
(distance=4.77). ...
Subsequence at [124:148] doesn't resemble anything else (distance=2.05).
RECURRING PATTERNS (motifs):
Subsequence at [0:24] closely repeats the pattern at index 24
(distance=0.00, near-zero means an almost exact shape match).
```
Обе найденные аномалии точно накрывают внедрённый диапазон 120–131 —
подтверждено golden-тестом. При `top_k=3` отчёт всё равно возвращает
только две: нулевые distance после дедупликации — это мотивы, не аномалии,
и `top_discords` их не подмешивает. Мотивы находят точные суточные циклы
(окна на границах дней) — корректный фон нормальности.
## Ключевая деталь алгоритма: exclusion zone
Без неё у каждого фрагмента "ближайшим соседом" тривиально оказался бы он
сам или соседнее перекрывающееся окно (расстояние ~0), и весь профиль был
бы бессмысленным. Exclusion zone (половина длины окна вокруг каждой точки)
— это не оптимизация, а требование корректности из оригинальной статьи
(Yeh et al., 2016). Проверено отдельным тестом, что при её отсутствии все
"ближайшие соседи" оказались бы тривиально рядом.
## Реальная находка в процессе разработки: дедупликация top-k
При первом запуске top-3 "самых аномальных" фрагментов оказались тремя
**соседними окнами, покрывающими одну и ту же аномалию** — технически
корректно, но бесполезно. Решение: `top_discords` пропускает кандидатов
ближе `subsequence_length` к уже выбранным и останавливается на distance≈0.
## Архитектура (Clean Architecture по Мартину)
```
src/echo/
├── entities/
│ ├── time_series.py # z-normalize, distance, finite checks
│ ├── matrix_profile.py # алгоритм + exclusion zone
│ └── findings.py # Discord/Motif, top-k, preview
├── use_cases/
│ └── analyze_time_series.py
├── interfaces/
│ └── ports.py # SeriesLoader
├── infrastructure/
│ └── csv_series_loader.py
└── presentation/
├── composition_root.py
├── formatting.py
└── mcp_server.py
```
Алгоритм — brute-force O(n²m), осознанно: корректность важнее скорости
для типичных объёмов (сотни–тысячи точек). MASS/FFT не реализован —
задокументированное ограничение.
## Установка
```bash
pip install -e ".[dev]"
```
## Подключение к Cursor / Claude Desktop
Как у остальных серверов серии (надёжнее, чем `echo-mcp` вне PATH):
```json
{
"mcpServers": {
"echo": {
"command": "C:\\Python314\\python.exe",
"args": ["-m", "echo.presentation.mcp_server"]
}
}
}
```
## MCP Tools
| Tool | Назначение |
|---|---|
| `analyze_series(values, subsequence_length, top_k)` | Анализ ряда напрямую |
| `analyze_csv(file_path, column, subsequence_length, top_k)` | Числовая колонка CSV |
Вход отвергает NaN/Inf, пустой CSV, `top_k < 1` и неверную длину окна.
## Тесты
```bash
.\make.ps1
```
Стена Мартина: black, ruff, mypy, pytest --cov≥90, лимиты функций.
Golden stuck-sensor, hypothesis-свойства distance/exclusion zone,
дедупликация top-k без фейковых аномалий, MCP-tools.
## Лицензия
MIT
This server cannot be deployed
Maintenance
ActivityMaintained
ResponsivenessNo issues