«Непередбачувана гра»? Як я за допомогою Python намагався вирахувати переможця Чемпіонату світу з футболу 2022

Оленка Пилипчак

Багато хто називає футбол «непередбачуваною грою». Чому? Бо є безліч факторів, що можуть вплинути на рахунок. 

Це так… Але лише певною мірою. Так вважає розробник та дата-саєнтист Френк Ендрейд. Він пропонує вирахувати, хто все ж таки переможе на Чемпіонаті світу з футболу 2022. Передаємо йому слово.


Важко передбачити остаточний рахунок або переможця матчу, але можна дізнатись, хто переможе у змаганні. Останні п’ять років Bayern Munich вигравав всі Бундесліги, а Manchester City виграв чотири Прем’єр-ліги. Не думаю, що це випадковість…

У середині сезону 20-21 я створив модель для передбачення переможця Прем’єр-ліги, Ла Ліги, Серії А та Бундесліги. З її допомогою я правильно назвав усіх переможців.

Цей прогноз було не так вже й важко зробити, оскільки на той момент уже відбулось 19 матчів. Зараз я запускаю ту саму модель, щоб передбачити Чемпіонат світу з футболу 2022 року.

Ось результат мого передбачення, виконаний з допомогою Python. Щоб дізнатися більше про код, перегляньте цей годинний відеоурок:

Як ми будемо прогнозувати матчі?

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

Зараз поясню, чому. Давайте почнемо з визначення розподілу Пуассона.

Розподіл Пуассона — це дискретний розподіл ймовірностей, який описує кількість подій, що відбуваються протягом фіксованого інтервалу часу або області можливостей.

Якщо ми розглядаємо гол як подію, яка може статися протягом 90 хвилин футбольного матчу, ми можемо обчислити, скільки голів ймовірно заб’є команда А і команда Б.

Нам потрібно, щоб виконувались такі умови:

  1. Кількість подій можна підрахувати (під час матчу можуть забити 1 або більше голів).
  2. Події незалежні одна від одної (те, що забили перший гол, не повинно впливати на ймовірність іншого голу).
  3. Швидкість, з якою відбуваються події, є постійною (ймовірність досягнення мети в певний проміжок часу має бути однаковою для будь-якого часового проміжку такої ж довжини).
  4. Дві події не можуть відбутися в один і той самий момент часу (не можна одночасно забити два гола).

Без сумніву, припущення 1 і 4 виконуються, але 2 і 3 — частково вірні. Проте припустімо, що припущення 2 і 3 завжди вірні.

Коли я прогнозував переможців вищих європейських ліг, я побудував гістограму кількості голів у кожному матчі за останні п’ять років для чотирьох найкращих ліг:

Якщо ви подивитеся на відповідну криву будь-якої ліги, вона виглядає як розподіл Пуассона.

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

Ось формула розподілу Пуассона.

Щоб зробити передбачення, я розглянув:

lambda: медіана голів за 90 хвилин (Команда A і B)

x: кількість голів у матчі, які можуть забити команди A та B

Щоб обчислити lambda, нам потрібні середні результати, забиті/пропущені кожною національною командою. Тому переходимо до наступного пункту.

Голи, забиті / пропущені кожною збірною

Зібравши дані з усіх матчів чемпіонату світу з 1930 по 2018 рік, я підрахував середню кількість забитих і пропущених голів кожною національною командою:

У своєму прогнозі для чотирьох найкращих європейських ліг я врахував фактор «дома/виїзд», але оскільки на Чемпіонаті світу майже всі команди грають на нейтральному стадіоні, я не враховував його для цього аналізу.

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

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

Нижче наведено код, який я використав, щоб передбачити кількість очок, які кожна збірна отримає на груповому етапі:

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): #number of goals home team
            for y in range(0, 11): #number of goals away team
                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 для кожної команди за формулою середні_забиті_голи * середні_пропущені_голи.

Потім я змоделював всі можливі рахунки матчу від 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)

Якщо ми застосуємо цю функцію predict_points до всіх матчів на груповому етапі, ми отримаємо першу та другу позицію в кожній групі, отже, наступні матчі в нокаутах.

Прогнозування нокаутів

Для нокаутів мені потрібно прогнозувати не очки, а переможця кожного bracket. Ось чому я створив нову функцію 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 я можу отримати результати попередніх brackets:

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

Якщо я знову використаю get_winner, я зможу передбачити переможця чемпіонату світу. Я запускаю функцію ще раз, і дізнаюся, що переможцем буде… Бразилія!

Ось як я передбачив Чемпіонат світу з футболу 2022 року за допомогою Python і розподілу Пуассона. Щоб побачити повний код, перегляньте мій GitHub, а всі статті про цей проєкт можна знайти на моєму блозі Medium.

Автор: Френк Ендрейд

Примітка редакції: у п’ятницю, 9 грудня, у матчі ¼ фіналу зійшлися збірні Хорватії та Бразилії. В основний час та екстратаймах суперники не визначили найсильнішого (1:1), все вирішилося в серії пенальті, яка несподівано завершилася на користь хорватів — 4:2.

Текст адаптувала Євгенія Козловська

Останні статті

Більше 8 млрд грн податків. Стільки сплатили резиденти Дія.City в І кварталі 2025 року

Резиденти Дія.City сплатили до бюджету понад 8 млрд грн податків в І кварталі 2025 року.…

18.04.2025

Китайських офісних працівників закликають менше працювати. Це має допомогти місцевій економіці

У Китаї закликають офісних працівників не працювати надто багато — держава сподівається, що вільний час…

18.04.2025

ChatGPT значно покращив пошук місць по фото. Це посилює проблеми конфіденційності

Експерти звертають увагу на тривожну тенденцію: люди все частіше використовують ChatGPT, щоб визначити місцезнаходження, зображене…

18.04.2025

Середовище розробки IntelliJ IDEA оновлено до версії 2025.1

Компанія JetBrains випустила нову версію мультимовного середовища розробки IntelliJ IDEA 2025.1. Оновлена IDE отримала численні…

18.04.2025

Discord впроваджує функцію сканування обличчя для перевірки віку користувачів

Платформа обміну миттєвими повідомленнями Discord впроваджує функцію перевірки віку за допомогою сканування обличчя. Зараз вона…

18.04.2025

Wikipedia випустила спеціальний датасет, щоб відволікти увагу ботів

Wikipedia намагається захистити себе від тисяч різноманітних ботів-скрейперів, які сканують дані цієї платформи для навчання…

18.04.2025