[Перевод] Прогнозируем результаты Чемпионата мира 2022 FIFA простой моделью на Python

nnz27bl30njdju-ni7dsrvszq7y.png


Многие люди (включая меня) называют футбол «непредсказуемой игрой», потому что в футбольном матче есть множество факторов, влияющих на окончательный счёт. И это верно… в определённой степени.

Сложно спрогнозировать окончательный счёт или победителя матча, однако при прогнозировании победителя в соревнованиях всё не так. За последние пять лет «Бавария» выиграла все Бундеслиги, а «Манчестер Сити» выиграл 4 Премьер-лиги.

Совпадение? Не думаю.

На самом деле, в середине сезона 20–21 годов я создал модель для прогнозирования победителя Премьер-лиги, Чемпионата Испании, Чемпионата Италии и Бундеслиги, и она успешно спрогнозировала всех победителей.

Прогноз сделать было не так сложно, потому что на тот момент было сыграно уже 19 матчей. Теперь я запущу ту же модель для прогнозирования результатов Чемпионата мира 2022.
В статье я расскажу, как спрогнозировал победителя Чемпионата мира при помощи Python (подробнее о коде можно узнать из моего часового видеотуториала).

Прим. переводчика: результаты ЧМ показали, что приведённая в этой статье модель допустила довольно много промахов. Однако мы считаем, что она имеет ценность, поэтому публикуем её здесь.


▍ Как мы будем прогнозировать матчи?


Существуют разные способы создания прогнозов. Я могу изготовить сложную модель машинного обучения и передать ей множество переменных, однако прочитав несколько научных статей, я решил дать шанс распределению Пуассона.

Почему? Давайте рассмотрим определение этого распределения.

Распределение Пуассона — это дискретное распределение вероятностей, описывающее количество событий, происходящих в фиксированном временном интервале или области возможностей.


Если мы будем воспринимать гол как событие, которое может произойти за 90 минут футбольного матча, то сможем вычислить вероятность количества голов, которые будут забиты в матче команды А и команды Б.

Но этого недостаточно. Нам ещё нужно соответствовать допущениям распределения Пуассона.

  1. Количество событий можно подсчитать (в матче может быть 1, 2, 3 или больше голов).
  2. События происходят независимо друг от друга (событие одного гола не должно влиять на вероятность другого).
  3. Частота событий постоянна (вероятность того, что гол будет забит в определённый интервал времени, должна быть совершенно одинаковой для любого другого интервала времени той же длины).
  4. Два события не могут произойти точно в один момент времени (два гола не могут произойти одновременно).

Допущения 1 и 4 соблюдаются, однако 2 и 3 справедливы лишь частично. Тем не менее, предположим, что допущения 2 и 3 всегда истинны.

Когда я прогнозировал победителей европейских чемпионатов, я создал гистограмму количества голов в каждом матче на протяжении последних пяти лет для четырёх чемпионатов.

dgffskjyxec0h5m5ch_rvq0salq.png

Гистограммы количества голов в четырёх чемпионатах

Аппроксимирующая кривая всех чемпионатов похожа на распределение Пуассона.

Мы можем прийти к выводу, что распределение Пуассона подходит для вычисления количества голов, которые могут быть забиты в матче.

Вот формула распределения Пуассона:

hjle2ce8j3hwgehl9hvtkx1lg1c.png


Для создания прогнозов я учёл следующие аспекты:

  • lambda: медианное значение голов за 90 минут (команда А и команда Б),
  • x: количество голов в матче, которые могли забить команда А и команда Б.

Для вычисления lambda нам нужно среднее количество голов, забитых/пропущенных каждой командой. Это приводит нас к следующему пункту.

▍ Голы забитые/пропущенные каждой командой


Собрав данные со всех матчей Чемпионата мира, сыгранных с 1930 по 2018 год, я смог вычислить среднее количество голов, забитых и пропущенных командой каждой страны.

8aut0ckckbdo2fudhu6f8drkyqi.png


В прогнозе, сделанном для четырёх европейских чемпионатов, я учёл фактор игры дома/в гостях, но поскольку на Чемпионате мира почти все команды играют на нейтральном стадионе, в этом анализе данный фактор не учитывался.

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

▍ Прогнозирование группового этапа


Ниже показан код, использованный мной для прогнозирования количества очков, которые получит команда каждой страны в групповом этапе. Выглядит пугающе, однако в этом коде присутствует многое из того, что описано выше.

def predict_points(home, away):
    if home in df_team_strength.index and away in df_team_strength.index:
        lamb_home = df_team_strength.at[home,'GoalsScored'] * df_team_strength.at[away,'GoalsConceded']
        lamb_away = df_team_strength.at[away,'GoalsScored'] * df_team_strength.at[home,'GoalsConceded']
        prob_home, prob_away, prob_draw = 0, 0, 0
        for x in range(0,11): #количество голов команды хозяев
            for y in range(0, 11): #количество голов команды гостей
                p = poisson.pmf(x, lamb_home) * poisson.pmf(y, lamb_away)
                if x == y:
                    prob_draw += p
                elif x > y:
                    prob_home += p
                else:
                    prob_away += p
        
        points_home = 3 * prob_home + prob_draw
        points_away = 3 * prob_away + prob_draw
        return (points_home, points_away)
    else:
        return (0, 0)


Если объяснять простым языком, predict_points вычисляет количество очков, которые получат команды, играющие дома и в гостях. Чтобы получить их, я вычисляю lambda для каждой команды по формуле average_goals_scored * average_goals_conceded.

Затем я симулирую все возможные результаты матча от 0–0 до 10–10 (этот последний счёт является просто пределом моего интервала голов). Получив lambda и x, я использую формулу распределения Пуассона для вычисления p.

prob_home, prob_draw и prob_away накапливают значение p, если, допустим, матч заканчивается со счётом 1–0 (выиграла команда хозяев), 1–1 (ничья) или 0–1 (выиграла команда гостей). Затем по показанной ниже формуле вычисляется результат.

points_home = 3 * prob_home + prob_draw
points_away = 3 * prob_away + prob_draw


Если мы используем predict_points для прогнозирования матча Англии против США, то получим следующее.

>>> predict_points('England', 'United States')
(2.2356147635326007, 0.5922397535606193)


Это значит, что Англия получит 2,23 очка, а США — 0,59. Я получаю десятичные дроби, потому что использую вероятности.

Если мы применим функцию predict_points ко всем матчам в групповом этапе, то получим первое и второе место в каждой группе, а значит, следующие матчи плей-офф.

tk77andty3cycrlz2urr6d9y-d0.png


▍ Прогнозирование плей-оффов


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

def get_winner(df_fixture_updated):
    for index, row in df_fixture_updated.iterrows():
        home, away = row['home'], row['away']
        points_home, points_away = predict_points(home, away)
        if points_home > points_away:
            winner = home
        else:
            winner = away
        df_fixture_updated.loc[index, 'winner'] = winner
    return df_fixture_updated


Если points_home больше points_away, то победителем стала команда хозяев, в противном случае — команда гостей.

Благодаря функции get_winner я могу получить результаты предыдущих турнирных таблиц.

naopw6dq6ley_hciha3s_cmnsfo.png


▍ Прогнозирование четвертьфинала, полуфинала и финала


Если я снова использую get_winner, то смогу спрогнозировать победителя Чемпионата мира. И вот окончательный результат!

c3d2achv6j6t0fbpm5x1w7wewdw.png


Запустив функцию ещё раз, я выяснил, что победителем будет…

Бразилия!


Вот и всё! Вот так я спрогнозировал результаты Чемпионата мира 2022 года при помощи Python и распределения Пуассона. Полный код выложен на GitHub. Также вы можете изучить мой список на Medium со всеми статьями, относящимися к этому проекту на Python.

ymoc6_v0doy8yrm1y4xsrjlxotc.jpeg

© Habrahabr.ru