Как обучить модель для детекции доминирования команды?

Что такое доминирование команды в спорте и как его измерить по данным матча

Доминирование команды в матче — это не только счет на табло. Команда может вести в один мяч, но почти не выходить из обороны, или наоборот проигрывать, постоянно создавать моменты и давить соперника. Для аналитики, беттинга и автоматизированных рекомендаций важно формализовать доминирование через количественные показатели, которые можно извлечь из данных матча: владение мячом, удары, опасные атаки, единоборства, штрафные и другие метрики.

На практике доминирование удобно описывать как целевой признак для модели машинного обучения. Его можно задать в виде бинарного класса (доминирует / не доминирует), мультикласса (доминирует хозяин, равная игра, доминирует гость) или непрерывного индекса от 0 до 1. Такой индекс рассчитывается по совокупности статистических показателей в выбранный промежуток времени: весь матч, тайм, пяти‑десятиминутное окно, отдельный игровой отрезок после гола или удаления.

Ключ к объективному измерению доминирования — богатый и детальный набор статистики. Через специализированный спортивный API вы получаете структуру данных по матчу: общую и по таймам статистику, live‑события, составы, турнирный контекст и коэффициенты букмекеров. На основе этого набора модель может оценивать, какая команда контролирует игру, и обновлять прогноз по мере поступления новых событий.

import requests
API_KEY = "YOUR_API_KEY"
BASE_URL = "https://api.api-sport.ru/v2/football"
MATCH_ID = 14570728  # пример ID матча
headers = {"Authorization": API_KEY}
url = f"{BASE_URL}/matches/{MATCH_ID}"
response = requests.get(url, headers=headers)
match = response.json()
# Извлечем сводную статистику по матчу (period == "ALL")
stats_all = next(
    (p for p in match.get("matchStatistics", []) if p.get("period") == "ALL"),
    None,
)
if stats_all:
    overview_group = next(
        (g for g in stats_all["groups"] if g["groupName"] == "Match overview"),
        None,
    )
    if overview_group:
        for item in overview_group["statisticsItems"]:
            if item["key"] in ("ballPossession", "totalShotsOnGoal", "shotsOnGoal"):
                print(item["name"], ":", item["home"], "vs", item["away"])

Какие данные спортивного API нужны для модели детекции доминирования команды

Для качественной модели детекции доминирования важно иметь доступ к максимально детализированным данным матча. В API спортивных событий вы получаете несколько ключевых слоев информации: базовые параметры встречи (турнир, команды, статус, текущая минута), расширенную статистику matchStatistics по периодам, live‑события liveEvents, а также коэффициенты букмекеров oddsBase. Такой набор позволяет оценивать как игровой рисунок, так и рыночные ожидания.

Статистика матча включает десятки метрик: владение мячом, удары, удары в створ, передачи, входы в финальную треть, угловые, фолы, отборы, дуэли, сейвы вратаря и многое другое. Для разных видов спорта используются свои наборы показателей: в футболе и хоккее важны броски/удары и опасные моменты, в баскетболе — эффективность атак и подборы, в теннисе — подача и реализованные брейк‑поинты. Live‑события дополняют статистику контекстом: голы, удаления, пенальти, длительные паузы, что позволяет учитывать изменения баланса сил в течение матча.

Данные по коэффициентам букмекеров oddsBase дают представление о том, как рынок оценивает силы команд и динамику доминирования в live. Используя историю изменения коэффициентов совместно с текущей статистикой, можно строить более устойчивые модели, снижающие шум и переобучение. В итоге в ваш датасет для обучения модели доминирования входят числовые признаки из статистики, категориальные признаки контекста матча и временные ряды изменений коэффициентов.

import requests
API_KEY = "YOUR_API_KEY"
BASE_URL = "https://api.api-sport.ru/v2/football"
headers = {"Authorization": API_KEY}
params = {
    "date": "2025-09-03",   # дата матчей
    "status": "finished",   # завершенные матчи
}
resp = requests.get(f"{BASE_URL}/matches", headers=headers, params=params)
data = resp.json()
for match in data.get("matches", []):
    print("Матч:", match["homeTeam"]["name"], "-", match["awayTeam"]["name"])
    print("Турнир:", match["tournament"]["name"])
    print("Текущая минута:", match.get("currentMatchMinute"))
    print("Есть статистика:", bool(match.get("matchStatistics")))
    print("Есть live события:", bool(match.get("liveEvents")))
    print("Есть коэффициенты:", bool(match.get("oddsBase")))
    print("---")

Выбор метрик и признаков для модели доминирования команды по статистике матча

Следующий шаг — трансформировать сырые показатели из спортивного API в информативные признаки для модели. Для оценки доминирования в футболе часто используют блоки метрик: контроль (владение, точные передачи, входы в финальную треть), создание моментов (удары, удары в створ, большие моменты), давление (угловые, штрафные рядом с воротами, входы в штрафную), оборонительные действия (отборы, перехваты, выносы, сейвы). В данных статистики эти параметры представлены в виде групп и элементов внутри массива matchStatistics.

Важно не только взять абсолютные значения, но и нормализовать их по времени и контексту. Например, удары и удары в створ делят на количество минут сыгранного времени, чтобы сравнивать отрезки разной длины. Владение мячом удобно использовать как долю от 0 до 1, а не строку «54%». Часть показателей полезно сводить в разность между хозяевами и гостями или в относительное превосходство: (home − away) / (home + away). Такой подход делает признаки инвариантными к общему темпу игры и уровню команд.

Дополнительно в модель имеет смысл включать признаки на основе live‑событий и коэффициентов: наличие красных карточек, недавние голы, количество замен, уровень предматчевого фаворита по стартовым коэффициентам и текущий дисбаланс рынков в live. На основе этих данных формируют композитный индекс доминирования, который затем используется как целевая переменная либо как вспомогательный таргет для более сложных AI‑моделей, например для прогноза следующего гола.

from typing import Dict, Any

def build_features_from_match(match: Dict[str, Any]) -> Dict[str, float]:
    """Пример выделения простых признаков доминирования из структуры матча."""
    features = {}
    stats_all = next(
        (p for p in match.get("matchStatistics", []) if p.get("period") == "ALL"),
        None,
    )
    if not stats_all:
        return features
    def get_item(key: str):
        for g in stats_all["groups"]:
            for it in g["statisticsItems"]:
                if it["key"] == key:
                    return it
        return None
    # Примеры признаков: владение и удары
    poss = get_item("ballPossession")
    shots_total = get_item("totalShotsOnGoal")
    shots_on = get_item("shotsOnGoal")
    if poss:
        features["possession_home"] = poss["homeValue"] / 100.0
        features["possession_diff"] = (poss["homeValue"] - poss["awayValue"]) / 100.0
    if shots_total:
        features["shots_total_diff"] = (
            shots_total["homeValue"] - shots_total["awayValue"]
        )
    if shots_on:
        features["shots_on_diff"] = shots_on["homeValue"] - shots_on["awayValue"]
    # Пример бинарного признака: есть ли красная карточка у хозяев/гостей
    home_red = any(
        ev["type"] == "card" and ev["team"] == "home" and "red" in ev.get("class", "")
        for ev in match.get("liveEvents", [])
    )
    away_red = any(
        ev["type"] == "card" and ev["team"] == "away" and "red" in ev.get("class", "")
        for ev in match.get("liveEvents", [])
    )
    features["home_red_card"] = float(home_red)
    features["away_red_card"] = float(away_red)
    return features

Как подготовить данные из спортивного API для обучения модели машинного обучения

Подготовка датасета — критически важный этап перед обучением любой модели, в том числе для детекции доминирования. Вам потребуется собрать историю матчей за несколько сезонов по выбранным турнирам и видам спорта, выгрузив их через метод /v2/{sportSlug}/matches. Для каждого матча нужно сохранить статистику по всему матчу и по периодам, события, коэффициенты и итоговый результат. Затем на основе этих данных формируется целевая переменная: например, класс «хозяева доминировали», если они создали больше опасных моментов и по итогам матча превзошли соперника по xG‑прокси (удары, удары в створ, большие моменты).

Далее важно обработать пропуски и привести данные к единому формату. Статистику с периодами (1‑й тайм, 2‑й тайм, весь матч) можно агрегировать или использовать как временные срезы. Текстовые значения процентов переводятся в числа, сложные строки вида «70/135 (52%)» разбираются на фактическое количество и процент. Важно также отфильтровать матчи с неполной статистикой или ошибочными значениями. После этого выстраивается таблица «матч — признаки», где каждая строка соответствует либо целому матчу, либо фиксированному времени в матче (например, 60‑я минута).

Наконец, датасет нужно разбить на обучающую, валидационную и тестовую выборки по времени, чтобы избежать утечки будущей информации. Чаще всего используют разбиение по сезонам или по временной оси. На этом этапе полезно сохранить готовый набор в удобном формате (CSV/Parquet) и зафиксировать код построения признаков, чтобы затем применять его к live‑данным. Получить API‑ключ для автоматизированной выгрузки можно в личном кабинете и интегрировать процесс в регулярный ETL‑pipeline.

import requests
import datetime as dt
import pandas as pd
API_KEY = "YOUR_API_KEY"
SPORT = "football"
BASE_URL = f"https://api.api-sport.ru/v2/{SPORT}"
headers = {"Authorization": API_KEY}
rows = []
start_date = dt.date(2025, 8, 1)
end_date = dt.date(2025, 8, 31)
cur = start_date
while cur <= end_date:
    params = {"date": cur.isoformat(), "status": "finished"}
    resp = requests.get(f"{BASE_URL}/matches", headers=headers, params=params)
    data = resp.json()
    for match in data.get("matches", []):
        # Здесь можно вызвать build_features_from_match(match)
        # и добавить целевой признак dominance_label
        row = {"match_id": match["id"], "date": match["dateEvent"]}
        # ... заполняем признаки ...
        rows.append(row)
    cur += dt.timedelta(days=1)
_df = pd.DataFrame(rows)
_df.to_csv("matches_features.csv", index=False)

Как обучить и протестировать модель детекции доминирования команды шаг за шагом

Когда датасет подготовлен, можно переходить к обучению модели. На первом этапе достаточно использовать интерпретируемые алгоритмы: логистическую регрессию, дерево решений или градиентный бустинг. Целевая переменная задает факт доминирования (например, 1 — хозяева доминировали, 0 — нет), а входные признаки формируются из статистики и событий. Важно разделить данные по времени: ранние сезоны для обучения, более поздние — для валидации и финального теста. Так вы получите реалистичную оценку качества в условиях, близких к работе модели в продакшене.

При обучении стоит контролировать баланс классов и выбирать метрики качества, отражающие задачу: ROC‑AUC, precision/recall, F1‑score, Brier score для вероятностных прогнозов. Полезно проводить кросс‑валидацию по сезонам или временным блокам, чтобы убедиться, что модель не переобучается под конкретный турнир или год. Интерпретация важнейших признаков (feature importance) подскажет, какие параметры статистики сильнее всего связаны с доминированием: возможно, это не только удары и владение, но и структура передач, количество входов в финальную треть или давление по угловым.

После выбора финальной модели ее стоит протестировать на отложенной выборке и на нескольких реальных матчах, проиграв их по минутам. Так вы проверите, насколько стабильно модель реагирует на голы, удаления и резкие изменения в статистике. Затем можно реализовать экспорт обученных весов или сериализацию модели для дальнейшей интеграции с боевым сервисом, который будет вызывать спортивный API и обновлять оценку доминирования в реальном времени.

import pandas as pd
from sklearn.model_selection import TimeSeriesSplit
from sklearn.metrics import roc_auc_score
from sklearn.ensemble import GradientBoostingClassifier
# Загрузка подготовленных данных
_df = pd.read_csv("matches_features_labeled.csv")
features = [c for c in _df.columns if c not in ("dominance_label", "match_id", "date")]
X = _df[features].values
y = _df["dominance_label"].values
# Временная кросс-валидация
cv = TimeSeriesSplit(n_splits=5)
aucs = []
for train_idx, test_idx in cv.split(X):
    X_train, X_test = X[train_idx], X[test_idx]
    y_train, y_test = y[train_idx], y[test_idx]
    model = GradientBoostingClassifier(random_state=42)
    model.fit(X_train, y_train)
    y_pred = model.predict_proba(X_test)[:, 1]
    auc = roc_auc_score(y_test, y_pred)
    aucs.append(auc)
print("Средний ROC-AUC:", sum(aucs) / len(aucs))
# Финальное дообучение на всей истории
final_model = GradientBoostingClassifier(random_state=42)
final_model.fit(X, y)

Интеграция модели детекции доминирования команды с API спортивных событий в реальном времени

После обучения и тестирования модели наступает ключевой этап — интеграция с боевой инфраструктурой и спортивным API. В реальном времени ваш сервис регулярно запрашивает данные матча через метод /v2/{sportSlug}/matches/{matchId}, извлекает актуальную статистику и события, прогоняет их через тот же пайплайн построения признаков и передает в модель. На выходе вы получаете текущую вероятность доминирования одной из команд или индекс от −1 до 1, который можно визуализировать в интерфейсе, использовать для алертов или для внутренних trading‑алгоритмов.

Сейчас интеграция строится через периодический опрос REST‑эндпоинтов, но уже сейчас можно проектировать архитектуру под стриминг. По мере развития платформы api-sport.ru планируется добавление WebSocket‑подключений и AI‑сервисов, что позволит получать обновления о событиях матча с минимальной задержкой и напрямую вызывать подготовленные модели. Это особенно важно для задач live‑аналитики, где оценка доминирования должна обновляться после каждого опасного момента, удаления или изменения коэффициентов букмекеров.

Финальная интеграция включает мониторинг качества и логирование. Для каждого матча имеет смысл сохранять последовательность оценок доминирования и сравнивать их с финальным результатом и ключевыми отрезками игры. На основании этих логов можно переобучать модель, донастраивать признаки и адаптировать алгоритм под новые турниры и виды спорта. Используя гибкость спортивного API и расширяемость собственной модели, вы строите устойчивую систему аналитики, которая масштабируется от одного чемпионата до глобального покрытия спорта и киберспорта.

import time
import requests
API_KEY = "YOUR_API_KEY"
SPORT = "football"
MATCH_ID = 14570728
BASE_URL = f"https://api.api-sport.ru/v2/{SPORT}"
headers = {"Authorization": API_KEY}
POLL_INTERVAL = 60  # секунд
while True:
    resp = requests.get(f"{BASE_URL}/matches/{MATCH_ID}", headers=headers)
    match = resp.json()
    if match["status"] == "finished":
        print("Матч завершен")
        break
    features = build_features_from_match(match)
    # dominance_score = final_model.predict_proba([list(features.values())])[0, 1]
    # В реальном коде используем сохраненную модель
    dominance_score = 0.5  # заглушка
    print(
        f"Минута {match.get('currentMatchMinute')}: индекс доминирования хозяев = {dominance_score:.3f}"
    )
    time.sleep(POLL_INTERVAL)