<jupyter_start><jupyter_text>Открытый курс по машинному обучению. Сессия № 3  Автор материала: <NAME>   Индивидуальный проект по анализу данных:   Прогноз успешного выступления на соревнованиях по Пауэрлифтингу<jupyter_code>## подключаемые библиотеки
import pandas as pd
import numpy as np
import seaborn as sns
import matplotlib.pyplot as plt
from sklearn import preprocessing<jupyter_output><empty_output><jupyter_text>1. Описание набора данных и признаков (2 балла)Описан процесс сбора данных (если применимо), есть подробное описание решаемой задачи, в чем ее ценность, дано описание целевого и прочих признаков; __Процесс сбора данных:__Данные для анализа взяты с Каггл:https://www.kaggle.com/open-powerlifting/powerlifting-databaseСкачать их можно по ссылке:https://www.kaggle.com/open-powerlifting/powerlifting-database/downloads/powerlifting-database.zip/1__Подробное описание решаемой задачи:__Пауэрли́фтинг (англ. powerlifting; power — «сила, мощь» + lifting — «поднятие») или силовое троеборье — силовой вид спорта, суть которого заключается в преодолении сопротивления максимально тяжёлого для спортсмена веса.Пауэрлифтинг также называют силовым троеборьем. Связано это с тем, что в качестве соревновательных дисциплин в него входят три упражнения (приседания со штангой на спине (точнее на верхней части лопаток), жим штанги лежа на горизонтальной скамье и тяга штанги), которые в сумме определяют квалификацию спортсмена.На основе данных сайта "Openpowerlifting.org" постараемся дать ответ на следующие вопросы:    1. Влияет ли категория, в которой выступает пауэрлифтер на результаты соревнований?    2. Как экипировка влияет на достижение результата?    3. Можно ли спрогнозировать победу чемпионата на основе первых 2 испытаний (присед и жим)?__Ценность задачи:__    У некоторых начинающих спортсменов результаты по отдельным дисциплинам в среднем лучше, чем по другим. Следовательно, необходимо понять, наксколько велик шанс спортсмена достигнуть призового места.__Описание целевого и прочих признаков:__Целевой признак:- Place - место в соревнованияхПрочие признаки:- MeetID - ID проведенного мероприятия;- Name - имя спортсмена (Имя Фамилия на англ. языке);- Sex - пол спортсмена (male - мужской /female - женский);- Equipment - экипировка (Multi-ply - многослойная, Raw - без экипировки, Single-ply - однослойная, Straps - бинты на кисти рук, Straps - бинты на колени ног);- Age - возраст спортсмена в полных годах;- Division - дивизион в котором выступает спортсмен (возрастная, проверка на допинг и пр.);- BodyweightKg - вес спортсмена (кг);- WeightClassKg - весовая категория спортсмена (его вес меньше или равен данной категории, кг);- BestSquatKg - лучший результат в дисциплине "Присед со штангой" (кг);- BestBenchKg - лучший результат в дисциплине "Жим штанги лежа" (кг);- BestDeadliftKg - лучший результат в дисциплине "Становая тяга штанги" (кг);- TotalKg - сумма трех испытаний (кг);- Wilks - коэффициент Вилкса, который позволяет на соревнованиях определить абсолютного чемпионата вне зависимости от весовой категории.<jupyter_code>op = pd.read_csv('../../data/openpowerlifting.csv')
op.shape<jupyter_output><empty_output><jupyter_text>2. Первичный анализ признаковИсследованы признаки, их взаимодействия, влияние на целевой признак. Исследовано распределение целевого признака (в случае задачи регрессии проведены стат-тесты на нормальность и скошенность (skewness) распределения). Если необходимо, объясняется, почему и как можно преобразовать целевой признак. Изучены выбросы и пропуски в данных;<jupyter_code>op.head()
print(op.info())<jupyter_output><empty_output><jupyter_text>7. Предобработка данных (4 балла)Проведена предобработка данных для конкретной модели. При необходимости есть и описано масштабирование признаков, заполнение пропусков, замены строк на числа, OheHotEncoding, обработка выбросов, отбор признаков с описанием используемых для этого методов. Корректно сделано разбиение данных на обучающую и отложенную части; Проверим данные на наличие пропусков. Определим долю пропусков от общего числа данных<jupyter_code>op.isnull().sum() / op.shape[0]<jupyter_output><empty_output><jupyter_text>Столцы Squat4Kg, Bench4Kg, Deadlift4Kg в большинстве отсутствуют (>99%) и age в 62% случаев, следовательно данные признаки удаляем из dataset<jupyter_code>op=op.drop(["Squat4Kg", "Bench4Kg", "Deadlift4Kg", "Age"], axis=1)
op.describe()<jupyter_output><empty_output><jupyter_text>Также имеем в столбцах BestSquatKg,	BestBenchKg, BestDeadliftKg отрицательные значения, что по физической природе является выбросом. Спишем это на ошибки ввода, следовательно данные параметры необходимо взять по модулю<jupyter_code>op['BestSquatKg']=np.abs(op['BestSquatKg'])
op['BestBenchKg']=np.abs(op['BestBenchKg'])
op['BestDeadliftKg']=np.abs(op['BestDeadliftKg'])<jupyter_output><empty_output><jupyter_text>Заменим все NaN в столбцах BestSquatKg, BestBenchKg, BestDeadliftKg, TotalKg, Wilks на "нули" (спортсмену не удалось выполнить начальный вес в состязаниях, поэтому у него суммарный вес и Вилкс тоже "ноль"<jupyter_code>op['BestSquatKg']=op['BestSquatKg'].fillna(0)
op['BestBenchKg']=op['BestBenchKg'].fillna(0)
op['BestDeadliftKg']=op['BestDeadliftKg'].fillna(0)
op['TotalKg']=op['TotalKg'].fillna(0)
op['Wilks']=op['Wilks'].fillna(0)<jupyter_output><empty_output><jupyter_text>Проверим также на корректность итоговую сумму трех дисциплин. Создадим отдельную колонку, где покажем процент ошибки ввода данных. Затем удалим строчки, где ошибка больше 1%<jupyter_code>op['TotalKg_check']=(op['TotalKg']-(op['BestSquatKg']+op['BestBenchKg']+op['BestDeadliftKg']))/op['TotalKg']
op=op.drop(op[np.abs(op['TotalKg_check'])>0.01].index)

op=op.drop(['TotalKg_check'], axis=1)
op.describe()<jupyter_output><empty_output><jupyter_text>Заметим, что еще остались значения Nan в признаке "BodyweightKg", следовательно удалим все строчки, где данный признак не указан, чтобы количество элементов в каждом признаке было одинаковым.<jupyter_code>op=op.drop(op[op["BodyweightKg"].isnull()].index)
op.describe()<jupyter_output><empty_output><jupyter_text>Получили "очищенные данные", которые демонстрируют статистику по итоговым места в соревнованиях из 370219 строчек<jupyter_code>op.groupby('Place')
place_stat=op['Place'].value_counts()
place_stat[0:10]
op['Place'].hist(bins=25)<jupyter_output><empty_output><jupyter_text>"Место" в соревнованиях определим как целевой признак. Чтобы в дальнейшем можно было провести прогноз, необходимо выполнить еще несколько подготовительных пунктов:- создание новых признаков- перевести текстовые значения в числовые для возможностей анализа и прогноза  9. Создание новых признаков и описание этого процесса (4 балла)Созданы новые признаки. Дано обоснование: логическое (например, у птиц температура тела на несколько градусов выше человеческой, значит вирус ХХХ не выживет в такой среде), физическое (например, радуга означает, что источник света расположен сзади; расчет величины по физическому закону с использованием данных признаков) или другое (скажем, признак построен после визуализации данных). Обоснование разумно описано. Полезность новых признаков подтверждена статистически или с помощью соответствующей модели; Создаем новый признак: "Число участий в соревнований" - 'MeetCount'. Каждый спортсмен будет иметь данные по сумме соревнований, что в дальнейшем позволяет понять насколько он опытный.<jupyter_code>op2=op.drop(['Sex','Equipment','Division','BodyweightKg','WeightClassKg','BestSquatKg','BestBenchKg','BestDeadliftKg','TotalKg','Wilks','Place'], axis=1)
op3=op2.groupby('Name').agg(np.count_nonzero)
op4=op3.rename(columns={'MeetID': 'MeetCount'})
op4['Name']=op4.index
op = op.merge(op4, 'left', on='Name')
op.head()<jupyter_output><empty_output><jupyter_text>В дальнейшем признак 'Name' нам не понадобится, поэтому его удаляем.<jupyter_code>op=op.drop(['Name'], axis=1)<jupyter_output><empty_output><jupyter_text>Переводим категориальные признаки в числовые<jupyter_code>op['Division'] = pd.factorize(op.Division)[0]
op['Equipment'] = pd.factorize(op.Equipment)[0]
op['Sex'] = pd.factorize(op.Sex)[0]
op['WeightClassKg'] = pd.factorize(op.Place)[0]
op['Place'] = pd.factorize(op.Place)[0]
op.head()<jupyter_output><empty_output><jupyter_text>Признак "place" имеет признак: "место" в числовом значении и "дисквалификация" в буквенном. Нас не интересует не 1 место, следовательно "place" который не равен 1 (после факторризации 0) будет равен 0, а остальное 1<jupyter_code>op['Place'][op['Place']==0]=0
op['Place'][op['Place']!=0]=1
op.head()<jupyter_output><empty_output><jupyter_text>3. Первичный визуальный анализ данных (4 балла)Построены визуализации (распределения признаков, матрица корреляций и т.д.), описана связь с анализом данным (п. 2). Присутствуют выводы;<jupyter_code>sns.heatmap(op.corr())
corr = op.corr()
corr<jupyter_output><empty_output><jupyter_text>4. Инсайты, найденные зависимости (4 балла)Найдены и выдвинуты предположения о природе различных корреляций/пропусков/закономерностей и выбросов, найденных в предыдущих пунктах. Есть пояснение, почему они важны для решаемой задачи; Насколько видим из матрицы корреляции, место ('Place' - целевой признак) коррелирует лучше всего с WeightClassKg (весовая категория, в которой выступает спортсмен). Чем выше категория, тем меньше вероятность победы.С экипировкой никакой корреляции нет.Для прогнозы победы на основе двух первых испытаний (присед и жим) необходимо удалить данные по становой тяге, суммарный взятый вес и Вилкс, так как они являются результатом соревнований и напрямую прогнозируют победу.<jupyter_code>op=op.drop(['BestDeadliftKg', 'TotalKg','Wilks'], axis=1)
op.head()<jupyter_output><empty_output><jupyter_text>5. Выбор метрики (3 балла)Есть разумное обоснование выбора метрики качества модели. Описаны моменты, влияющие на выбор метрики качества (решаемая задача, цель решения, количество классов, дисбаланс классов, прочее);<jupyter_code>target = op['Place']
train = op.drop(['Place'], axis=1) 
from sklearn.tree import DecisionTreeClassifier
from sklearn.model_selection import GridSearchCV, StratifiedKFold
from sklearn.cross_validation import cross_val_score
dt = DecisionTreeClassifier(random_state=17, class_weight='balanced')
max_depth_values = range(1, 10)
max_features_values = range(1, 10)
tree_params = {'max_depth': max_depth_values,
               'max_features': max_features_values}
skf = StratifiedKFold(n_splits=5, shuffle=True, random_state=17)
from sklearn.metrics import accuracy_score
X = train
y = target
grid=GridSearchCV(dt,tree_params,cv=skf, n_jobs=-1,scoring='roc_auc',verbose=1)
grid.fit(X, y)
from sklearn.ensemble import RandomForestClassifier
dt2 = RandomForestClassifier(n_jobs=1, random_state=17)
max_depth_values = range(1, 10)
max_features_values = range(1, 10)
tree_params = {'max_depth': max_depth_values,
               'max_features': max_features_values}
grid2=GridSearchCV(dt2,tree_params,cv=skf, n_jobs=-1,scoring='roc_auc',verbose=1)
grid2.fit(X, y)<jupyter_output><empty_output><jupyter_text>8. Кросс-валидация и настройка гиперпараметров модели (4 балла)Кросс-валидация выполнена технически верно, нет утечек данных. Разумно выбрано количество фолдов и разбиение (Random/Stratified или иное), зафиксирован seed. Присутствует объяснение. Объяснены гиперпараметры модели и способ их выбора. Выбор основан на некотором исследовании гипрепараметров модели для данной задачи;<jupyter_code>clf_best_score = grid.best_score_
clf_best_params = grid.best_params_
clf_best = grid.best_estimator_
mean_validation_scores = []
print("Лучший результат в дереве решений", clf_best_score, 
      "лучшие параметры", clf_best_params)
clf_best_score2 = grid2.best_score_
clf_best_params2 = grid2.best_params_
clf_best2 = grid2.best_estimator_
print("Лучший результат в случайном лесе", clf_best_score2, 
      "лучшие параметры", clf_best_params2)<jupyter_output><empty_output><jupyter_text>6. Выбор модели (3 балла)Произведен выбор модели. Описан процесс выбора и связь с решаемой задачей; Как видно, "случайный лес" и "дерево решений" дают точный прогноз (100%) победы спортсмена  11. Прогноз для тестовой или отложенной выборке (2 балла)Указаны результаты на тестовой выборке или LB score. Результаты на тестовой выборке сравнимы с результатами на кросс-валидации. Если тестовая выборка создавалась автором проекта, то механизм создания должен быть непредвзят и объяснен (применен разумный механизм выборки, в простейшем случае – рандомизация); Выполним прогноз при помощи "случайного леса" с лучшими параметрами на тренировочной и отложенной выборке<jupyter_code>from sklearn.model_selection import train_test_split

X_train, X_valid, y_train, y_valid = train_test_split(train,target,test_size=0.3, random_state=17)
dt3 = RandomForestClassifier(n_jobs=1, max_depth=2, max_features=6,random_state=17)
dt3.fit(X_train, y_train)
dt3_pred = dt3.predict(X_valid)
accuracy_score(y_valid, dt3_pred)
dt4 = DecisionTreeClassifier(class_weight='balanced',max_depth=2, max_features=5,random_state=17)
dt4.fit(X_train, y_train)
dt4_pred = dt4.predict(X_valid)
accuracy_score(y_valid, dt4_pred)<jupyter_output><empty_output>
{'fraction_non_alphanumeric': 0.09289909236518953, 'fraction_numerical': 0.011288231256197087, 'mean_word_length': 7.48124191461837, 'pattern_counts': {'":': 0, '<': 78, '<?xml version=': 0, '>': 80, 'https://': 2, 'lorem ipsum': 0, 'www.': 2, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 2, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'chain_length': '19', 'dir': 'jupyter-structured-clean-dedup', 'id': '38151', 'license': 'Unlicense', 'n_tokens_mistral': 5331, 'n_tokens_neox': 6200, 'n_words': 1206, 'path': 'jupyter_russian/projects_individual/project_demo_openpowerlifting.ipynb', 'repo_name': 'salman394/AI-ml--course', 'size': '13127'}