Панды в Премьер‑лиге
Очень жаль, но панды не играют в футбол. Поэтому в этой статье речь пойдёт о возможностях библиотеки “Pandas”, название которой происходит от термина “panel data”. Сегодня мы расскажем, как с её помощью можно проанализировать статистику игр футбольных команд в Премьер-лиге.
Английская Премьер-лига — одно из самых обсуждаемых событий в футбольном мире. Фанаты делают предположения, а букмекерские конторы принимают ставки на то, кто же займёт призовые места. В этом сезоне чемпионом второй раз подряд стал "Манчестер Сити". Насколько вероятным такой исход является с точки зрения Data Science?
Исследователь Стивен Фордхэм (Stephen Fordham) из Борнмутского университета решил продемонстрировать, как можно использовать Python-библиотеку Pandas для анализа таблицы результатов футбольных клубов.
Парсинг сайтов
Для начала добудем саму таблицу с результатами. В Pandas есть встроенная функция read_html, которая использует библиотеки lxml и Beautiful Soup для автоматического сбора данных из HTML-файлов в виде объектов DataFrame. Установим несколько дополнительных модулей, введя в терминале команды:
1 2 |
pip install lxml pip install beautifulsoup4 html5lib |
Метод read_html имеет ряд настраиваемых параметров, но по умолчанию он пытается найти и проанализировать любые табличные данные в html-тегах <table>, собирая их в список DataFrame.
Далее импортируем библиотеку pandas и используем функцию read_html, чтобы проанализировать таблицу Премьер-лиги и присвоить её переменной prem_table. Так мы получим список, из которого возьмём первый элемент — он будет содержать в себе последние доступные результаты.
Поскольку основное внимание сосредоточено на гонке за 4 призовых места, мы отобразим 6 первых записей в таблице с помощью метода .head. Чтобы убедиться, что с данными всё в порядке, можно использовать атрибут shape. Он показывает размер таблицы, в нашем случае в ней 21 строка и 12 столбцов. Проверка оказалась полезной, так как Python начинает считать строки с индекса 0, а в Премьер-лиге всего 20 команд. Похоже, в нашей таблице есть лишние данные. Раз с первыми значениями всё хорошо, проблема должна быть в какой-то дополнительной строке в конце DataFrame.
1 2 3 4 5 6 7 8 9 10 11 12 13 |
import pandas as pd prem_table = pd.read_html('https://www.bbc.co.uk/sport/football/premier-league/table') Premier_table = prem_table[0] print(len(prem_table)) print(type(prem_table)) print(Premier_table.head(6)) # Output 1 <class 'list'> |
Перед тем как начать предварительный анализ, мы почистим данные. Столбец “Unnamed: 1” явно не несёт в себе полезной информации, поэтому удалим его с помощью метода drop с аргументом axis=1:
1 2 3 4 5 6 7 |
Premier_table.columns Index(['Unnamed: 0', 'Unnamed: 1', 'Team', 'P', 'W', 'D', 'L', 'F', 'A', 'GD', 'Pts', 'Form'], dtype='object') Premier_league = Premier_table.drop(['Unnamed: 1'], axis=1) Premier_league.head(6) |
Результат:
Типы данных в DataFrame
Каждый столбец в DataFrame можно представить как Series, где должны быть только однотипные данные. Для дальнейшего анализа попробуем расшифровать тип каждой Series.
Взглянув на таблицу, можно сразу предположить, что столбец “Team” содержит в себе строки, а “P” (количество сыгранных игр) — целочисленные значения. Но лучше не доверять предположениям и использовать атрибут dtypes или метод info.
Видим, что все Series содержат в себе данные “object”, то есть строковые значения. Для выполнения численных расчётов нам необходимо преобразовать типы некоторых из них. В Python это тривиальная задача: мы просто используем метод pandas.to_numeric.
Изменим тип данных нескольких столбцов на float:
Очищаем DataFrame
Вернёмся к нашим лишним строкам и посмотрим на три последних записи в DataFrame:
В последней строке содержатся ненужные метаданные. Чтобы удалить их, снова используем метод drop, но на этот раз без параметра axis=1 (поскольку обрабатываем строку, а не столбец). С помощью атрибута shape убедимся, что всё прошло успешно:
1 2 3 4 5 6 7 8 |
Table.drop(Table.tail(1).index, inplace=True) Table.tail(3) Table.shape # Output (20, 12) |
Теперь посмотрим на столбец “Unnamed: 0”. Его название явно не согласуется с содержимым (с позицией команды в общем зачёте). Исправим это методом Pandas Dataframe.rename с аргументом Inplace=True, чтобы применить изменения к исходному DataFrame.
1 2 3 4 5 6 7 |
Table.columns Index(['Unnamed: 0', 'Team', 'P', 'W', 'D', 'L', 'F', 'A', 'GD', 'Pts', 'Form'], dtype='object') Table.rename(columns={'Unnamed: 0':'Position'}, inplace=True) Table.head() |
Создаём новые столбцы
Наш DataFrame теперь настроен. На этом этапе стоит попробовать создать несколько новых столбцов и проверить, будет ли для них работать преобразование строк в числа, сделанное нами ранее.
Сформируем столбец под названием «Соотношение голов» (“Goal Ratio”), показывающий отношение забитых мячей к пропущенным. Для этого нужно поделить столбец “F” на столбец “A” и округлить значения до одного знака после запятой. По умолчанию новые данные добавляются в конец DataFrame:
Чтобы переместить столбец, можно просто переопределить таблицу, указав нужный порядок:
Мы удалили столбец “Form”, чтобы данные выглядели более аккуратными.
Фильтруем DataFrame
Теперь наша таблица оформлена подобающим образом. Давайте начнём анализ с простого вопроса, например: «Какие команды сыграли на одну игру меньше своих соперников?». Так как все команды на момент написания статьи сыграли 34 или 35 игр, мы просто выводим те, у которых значение столбца “P” равно 34. Для фильтрации используем логическое индексирование:
Теперь давайте определим, у какой команды было больше всего результатов «ничья» (столбец “D”).
Клуб "Саутгемптон" изо всех сил пытается избежать вылета, повлияет ли это на их позицию?
Наконец, создадим ещё два столбца: число забитых и пропущенных голов за игру (“Goals/game” и “Goal conceded/game”). Отсортируем значения методом sort_values:
1 2 3 |
Table['Goals/game'] = round((Table.F / Table.P), 1) Table['Goals conceded/game'] = round((Table['A'] / Table['P']), 1) Table.sort_values(['Goals/game', 'Goal Ratio']).head(5) |
После «Хаддерсфилда» и «Фулхэма» у клуба «Кардифф» третье худшее соотношение забитых и пропущенных голов. Смогут ли они продолжать бороться за более высокое место в таблице со своим ближайшим соперником, клубом «Брайтон»? Достаточное ли у них нападение, уязвима ли защита?
Мы можем выяснить это с помощью Pandas!
Что ж, похоже, их шансы невысоки.
Хотя английская Премьер-лига завершилась, в России она ещё продолжается — вы можете оценить статистику главных претендентов на первенство и предположить, кто же станет чемпионом. А если вы уже давно знакомы с анализом данных, то для больших вычислений рекомендуем воспользоваться нашими серверами с GPU NVIDIA Tesla V100.
Заключение
Надеемся, что представленные в этой статье функции Pandas помогут вам в анализе данных. Напоследок один совет: не анализируйте статистику Премьер-лиги во время соревнований, поскольку пока вы пишете код, данные успеют поменяться. Все расчёты стоит выполнять после матчей. А пока — устраивайтесь поудобнее и наблюдайте за игрой!
С оригинальной статьёй можно ознакомиться на портале towardsdatascience.com.