Как очистить наборы данных и использовать логистическую регрессию в Python, чтобы сделать выводы о спорте №1 в Америке

Суперкубок, грандиозный финал сезона Национальной футбольной лиги (НФЛ), — это больше, чем просто игра; это культурный феномен, который объединяет спорт, развлечения и дух сообщества. Это ежегодное мероприятие, которое смотрят миллионы людей по всему миру, сталкивает чемпионов двух конференций НФЛ друг с другом в битве за желанный трофей Винса Ломбарди, где победитель получает все. Суперкубок — это зрелище атлетизма, стратегии и стойкости, где слагаются легенды и пишется история.

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

Цель этой статьи — предоставить простой анализ НФЛ для тех из нас, кто не может быть футбольным всезнайкой или аналитическим гением. Сначала я поделюсь несколькими дополнительными источниками данных, а затем мы будем использовать логистическую регрессию, чтобы предсказать победителя Суперкубка этого года после очистки данных.

Источники данных

Вот некоторые потенциальные онлайн-ресурсы, где вы можете найти данные о результатах игр НФЛ для нашего проекта машинного обучения:

1. [Справочник по футболу] (https://www.pro-football-reference.com/): на этом веб-сайте представлена ​​исчерпывающая футбольная статистика и история.

2. [Статистика красной зоны НФЛ] (https://www.fantasypros.com/nfl/red-zone-stats/te.php): этот сайт предлагает подробные данные НФЛ по каждой игре.

3. [API NFL.com] (https://www.nfl.com/apis): Официальный API NFL предоставляет доступ к игровым данным NFL в реальном времени.

Мы будем использовать данные по этой ссылке: https://drive.google.com/file/d/1WDUd2hQ0YG7SUvbLaAdjSB4sdMhh9yCS/view?usp=sharing

Очистка данных

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

#import the data
df_input = pd.read_csv("/filepath")

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

#check column values
#check the number of games and team in the data
#check number of columns
print(df_input.columns.values)
print(df_input.game_id.unique().size, df_input.vis_team.unique().size)
print(len(df_input.axes[1]))
#find columns with "ERROR" values
for column_name in df_input.columns:
    if 'ERROR - abbrev_team' in df_input[column_name].values.astype(str):
        print(column_name)
#create dataframe that checks columns with "ERROR" in and removes those rows
df_error = df_input[df_input['team'].str.contains('ERROR - abbrev_team') | 
                    df_input['Team_abbrev'].str.contains('ERROR - abbrev_team') |
                    df_input['Opponent_abbrev'].str.contains('ERROR - abbrev_team') |
                    df_input['vis_team'].str.contains('ERROR - abbrev_team') |
                    df_input['home_team'].str.contains('ERROR - abbrev_team') |
                    df_input['Vegas_Favorite'].str.contains('ERROR - abbrev_team') 
                   ]
#we will remove these lines by checking their unique game id
games_tobe_removed = df_error.game_id.unique()

#actual removing of "ERROR" values, ~ or - denotes not
#check new size of num games (orig was 255 unique game id)
df_input = df_input[~df_input['game_id'].isin(games_tobe_removed)]
df_input.game_id.unique().size
#check null values, but they're irrelevant columns in our case so it is unnecessary to drop
for column_name in df_input.columns:
    if df_input[column_name].isnull().any():
        print(column_name)

Теперь, когда данные очищены, мы организуем данные, начиная с определения общего счета каждой команды, затем среднего счета, среднего рейтинга пасов и, наконец, процента побед каждой команды (обратите внимание, что для прогноза не нужны эти показатели, они помогли лучше организовать наш набор данных — переходите к прогнозам, если вы хотите выбрать другой маршрут или используете другой набор данных!)

Манипулирование данными

#in order to do easily this, we create a new ID called TEAM_GAME_ID
df_input['team_game_id'] = df_input.apply(lambda x: x['team'] + '_' + x['game_id'], axis=1)
#vis_score and home_score need to be analyzed, duplicates dropped since only one row of the same values is necessary
df_scores = df_input[['team_game_id', 'vis_team', 'home_team', 'vis_score', 'home_score', 'game_date', "pass_rating"]]
df_scores = df_scores.drop_duplicates()
# extract vis_score for vis_team, home_score for home_team
# using a similar method, isolate teams only and create a new column
# then just keep team_game_id, game_date and score
df_scores['score'] = df_scores.apply(lambda x: x['vis_score'] if x['vis_team'] in x['team_game_id'] else x['home_score'], axis=1)
df_scores['team'] = df_scores.apply(lambda x: x['vis_team'] if x['vis_team'] in x['team_game_id'] else x['home_team'], axis=1)
df_scores = df_scores.drop(columns=['vis_team', 'home_team', 'vis_score', 'home_score'])
df_scores.reset_index(inplace=True)
#now we have to aggregate/add up the scores corresponding to each team - sorted highest to lowest
agg_scores = df_scores[['team', 'score']].groupby(by=['team']).sum().sort_values(by=['score'], ascending=False)
agg_scores
#another for mean score
mean_scores = df_scores[['team', 'score']].groupby(by=['team']).mean().sort_values(by=['score'], ascending=False)
#identify players with pass ratings and sort from highest to lowest
#use dataframe with already sorted game + team ids to find mean pass ratings for each team
#a lot of our prediction will rely on how good a team's passes are and cumulative points scored by the team
passes = df_scores[df_scores["pass_rating"] > 0].sort_values(by = ["pass_rating"], ascending = False)
passes = df_scores[['team', 'pass_rating']].groupby(by=['team']).mean().sort_values(by=['pass_rating'], ascending=False)
#one final important column addition before we start interpretating data: win rate
#the following calculates how many wins or losses a team has
def win_or_loss(row):
    if (row['vis_team'] in row['team_game_id']):
        score_delta = int(row['vis_score']) - int(row['home_score'])
        if score_delta > 0:
            return 1
        # count tie as 0
        else:
            return 0
    else:
        score_delta = int(row['home_score']) - int(row['vis_score'])
        if score_delta > 0:
            return 1
        # count tie as 0
        else:
            return 0

df_games = df_input[['team_game_id', 'game_id', 'vis_team', 'home_team', 'vis_score', 'home_score']].drop_duplicates()
df_games['win_loss'] = df_games.apply(lambda x: win_or_loss(x), axis=1)
#find win_rate
win_rate = {}
for team in df_games.vis_team.unique():
    df_tmp = df_games[df_games['team_game_id'].str.contains(team)]
    win_rate[team] = (df_tmp['win_loss'].sum() / df_tmp['win_loss'].count()) * 100
winrate = pd.Series(win_rate)
#combine passes, score, win rate data frame
#currently sorted by pass rating
passes["tot_score"] = agg_scores
passes["mean_score"] = mean_scores
passes["win_rate"] = winrate
passes

Прогнозы

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

1. **Логистическая регрессия**: это простой и эффективный алгоритм для задач бинарной классификации. Это может послужить хорошей отправной точкой.

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

3. **Случайный лес**: это метод ансамбля, который использует несколько деревьев решений для прогнозирования. Это более надежно и точно, чем одно дерево решений.

4. **Усиление градиента**. Как и случайные леса, повышение градиента — это ансамблевый метод. Он последовательно строит несколько слабых учеников (обычно деревья решений), при этом каждое новое дерево пытается исправить ошибки предыдущих.

5. **Машины опорных векторов (SVM)**: SVM могут быть эффективны в многомерных пространствах и универсальны, поскольку для функции принятия решений могут быть указаны различные функции ядра.

6. **Нейронные сети**: если у вас есть большой объем данных и вычислительных ресурсов, нейронная сеть потенциально может обеспечить высокую точность. Однако они могут быть более сложными в настройке и интерпретации.

Выбор алгоритма зависит от конкретных характеристик наших данных. В этой статье для демонстрации мы используем простой подход — логистическую регрессию.

Логистическая регрессия — это статистическая модель, которая в своей базовой форме использует логистическую функцию для моделирования бинарной зависимой переменной — в данном случае победителя Суперкубка. Это метод перехода к задачам бинарной классификации (проблемы с двумя значениями класса).

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

Вот схема шагов для логистической регрессии:

  1. **Выбор функций**. Сначала вы выбираете функции (также называемые переменными или атрибутами), которые вы будете включать в свою модель. Это могут быть такие вещи, как среднее количество очков команды за игру, общее количество набранных ярдов, разрешенное общее количество ярдов, разница в обороте и т. д.

2. **Обучение модели**. Затем вы используете алгоритм логистической регрессии для обучения модели с использованием выбранных функций. Алгоритм будет использовать исторические данные, чтобы узнать, как каждая функция влияет на вероятность победы команды в Суперкубке.

3. **Прогноз**: после обучения модели вы можете использовать ее для прогнозирования вероятности победы команды в Суперкубке. Например, если вы вводите функции для команды А и команды Б, модель может вывести вероятность победы команды А с вероятностью 70% и вероятность победы команды Б с вероятностью 30%.

4. **Интерпретация**: результат логистической регрессии — это вероятность того, что данная входная точка принадлежит определенному классу. В этом случае два класса: «выиграет Суперкубок» и «не выиграет Суперкубок». Полученные вероятности затем сопоставляются с дискретным классом на основе порога, обычно 0,5. Таким образом, если прогнозируемая вероятность больше 0,5, модель предсказывает, что команда выиграет Суперкубок.

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

Конечно, я могу предоставить базовый пример того, как вы можете настроить модель логистической регрессии в Python, используя библиотеку sklearn. Обратите внимание, что это упрощенный пример, который не включает в себя все шаги, которые могут вам понадобиться в реальном сценарии (например, очистка данных, разработка функций, проверка модели и т. д.). Приведенные выше очищенные данные должны позволить пользователям подключить и поиграть с моделью.

#import necessary libraries
import pandas as pd
from sklearn.model_selection import train_test_split
from sklearn.linear_model import LogisticRegression
from sklearn.metrics import accuracy_score

Допустим, ваш DataFrame включает столбцы для среднего количества очков каждой команды за игру (avg_points), общего количества набранных ярдов (total_yards) и двоичный столбец superbowl_winner, указывающий, выиграла ли команда Суперкубок в этом году (1 — да, 0 — нет). Вы бы разделили эти данные на матрицу признаков «X» и целевой вектор «y»:

#note that "data" is a template DataFrame
X = data[['avg_points', 'total_yards']]
y = data['superbowl_winner']
#split the data into a training set and a test set:
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.2, random_state=42)
#train model
model = LogisticRegression()
model.fit(X_train, y_train)
#make predictions on test set and calculate the accuracy of the model
predictions = model.predict(X_test)
print("Model accuracy: ", accuracy_score(y_test, predictions))

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

Спасибо за прочтение!!