import os
os.listdir("../input")
"""
# Метрические методы классификации
"""
# импортируем необходимые библиотеки
import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
import seaborn as sns
from sklearn.preprocessing import StandardScaler
from sklearn.model_selection import train_test_split
from sklearn.neighbors import KNeighborsClassifier
from sklearn.metrics import accuracy_score
from sklearn.model_selection import KFold
from sklearn.model_selection import cross_val_score
from sklearn.model_selection import GridSearchCV
from sklearn.neighbors import RadiusNeighborsClassifier
from sklearn.neighbors import NearestCentroid
sns.set(rc = {'figure.figsize':(10, 8)}) # более красивый внешний вид графиков по умолчанию
"""
# 1 Методы ближайших соседей
"""
"""
**1. Подключитесь к одному из наборов данных на Kaggle. Разберитесь в том, как устроен ваш датасет и какова постановка задачи.**
"""
# подключаемся к датасету и загружаем данные
df = pd.read_csv("../input/adult-income-dataset/adult.csv")
# просматриваем первые 10 строк таблицы
df.head(10)
# выводим общую информацию о датасете
# Смотрим тип каждого признака, а также есть ли в данных пропуски
df.info() 
# собираем некоторую статистику по каждому числовому признаку
df.describe()
"""
**2. Извлеките целевой признак (target). Какая из задач обучения с учителем рассмат-
ривается –– классификация или регрессия?**
"""
"""
Целевой признак (target) - income, категориальный. Всего две категории ">50K" и "<=50K". В данном случае рассматривается задача классификации.
"""
"""
**3. Каково распределение значений target-переменной? Постройте подходящую визуализацию. Прокомментируйте результат.**
"""
# смотрим процентное соотношение людей, зарабатывающих более 50К и менее 50К
df1 = df["income"].value_counts(normalize = True)
print(df1*100)
# визуализируем полученное распределение
df2 = df["income"].value_counts().plot(kind = 'bar',color = "blue")
print(df2)
"""
Как видим, людей, зарабатывающих менее 50К, значительно больше, чем тех, кто зарабатывает более 50К. Классы не сбалансированы, что создает проблему с оценкой точности.
"""
"""
**4. Проведите необходимую предобработку данных (preprocessing).**
"""
# Перекодируем нечисловые значения в числовые
df0 = df
df0['workclass'] = df0['workclass'].map({'Private':0, 'Self-emp-not-inc':1, 'Local-gov':2, '?':3, 'State-gov':4, 'Self-emp-inc':5, 'Federal-gov':6, 'Without-pay':7, 'Never-worked':8})
df0['marital-status'] = df0['marital-status'].map({'Married-civ-spouse':0, 'Never-married':1, 'Divorced':2, 'Separated':3, 'Widowed':4, 'Married-spouse-absent':5, 'Married-AF-spouse':6})
df0['occupation'] = df0['occupation'].map({'Prof-specialty':0, 'Craft-repair':1, 'Exec-managerial':2, 'Adm-clerical':3, 'Sales':4, 'Other-service':5, 'Machine-op-inspct':6, '?':7, 'Transport-moving':8, 'Handlers-cleaners':9, 'Farming-fishing':10, 'Tech-support':11, 'Protective-serv':12, 'Priv-house-serv':13, 'Armed-Forces':14})
df0['relationship'] = df0['relationship'].map({'Husband':0, 'Wife':1, 'Not-in-family':2, 'Own-child':3, 'Unmarried':4, 'Other-relative':5})
df0['race'] = df0['race'].map({'White':0, 'Black':1, 'Asian-Pac-Islander':2, 'Amer-Indian-Eskimo':3, 'Other':4})
df0['gender'] = df0['gender'].map({'Male':0, "Female":1})
df0['income'] = df0['income'].map({'<=50K':0, ">50K":1})
# При анализе данных в первой работе возникли проблемы с распределением доходов по странам, поскольку огромный процент людей в данных из США, поэтому распределим их на две категории
df0.loc[df0['native-country'] == 'United-States', 'native-country'] = 1
df0.loc[df0['native-country'] != 1, 'native-country'] = 0
# удаляем столбцы 'fnlwgt' и 'education'
df0 = df0.drop(['fnlwgt', 'education'], axis = 1)
#df0 = df0.drop(['education'], axis = 1)
df0 = df0.drop(['native-country'], axis = 1)
# смотрим первые 5 строк таблицы
df0.head()
# X - вся таблица без таргета
# y - таргет (целевая переменная)
y = df0['income'] # создаем у
X = StandardScaler().fit_transform(df0.drop(['income'], axis = 1)) # создаем Х и масштабируем данные 
print(X[:5]) # выводим результат масштабирования

"""
**5. Разбейте набор данных на обучающую и валидационную (тестовую) выборки
с помощью метода train_test_split .**
"""
# на (X_train, y_train) будем обучаем модель
# на X_valid делаем предсказания (y_pred)
# сравнивая y_valid и y_pred, будем вычислять качество предсказаний (метрику качества)
X_train, X_valid, y_train, y_valid = train_test_split(X, y, test_size = 0.25, random_state = 25) # разделение
# test_size - доля исходных данных, которую оставляем для валидации
# random_state - произвольное целое число, для воспроизводимости случайных результатов
"""
**6. Обучите алгоритм классификации kNeighborsClassifier или регрессии KNeighborsRegressor. Оцените качество каждой модели на валидационной выборке. Сравните результаты и сделайте выводы.**
"""
knn = KNeighborsClassifier() # классификатор кнн
knn.fit(X_train, y_train) # обучение модели (fit) на X_train, y_train
y_pred = knn.predict(X_valid) # предсказание (predict) для X_valid
print(y_pred[:5]) # смотрим предсказания y_pred
print(y_valid[:5]) # смотрим y_valid
print('Качество модели:', accuracy_score(y_valid, y_pred)) # cравнение результатов с истинными, вычисление метрик качества
"""
Получили достаточно высокую точность. Качество модели с 'fnlwgt' и 'native-country' : 0.8359675702235689. Качество модели без 'fnlwgt': 0.839243305216608. Качество модели без 'fnlwgt' и 'native-country' : 0.8412906395872574.
"""
"""
# 2 Настройка оптимального числа ближайших соседей в методе kNN
"""
"""
**1. Создайте генератор разбиений, который перемешивает выборку перед созданием блоков ( shuffle=True ). Число блоков n_splits равно 5. Задайте также параметр random_state для воспроизводимости результатов.**
"""
# делим (X,y) на K блоков равной длины и делаем K шагов предсказаний: на каждом шаге один из блоков играет роль (Xvalid,yvalid), все остальные - (Xtrain,ytrain)
kf = KFold(n_splits = 5, shuffle = True, random_state = 25) # n_splits играет роль K
#knn = KNeighborsClassifier()
scores = cross_val_score(knn, X, y, cv = kf, scoring = 'accuracy')# получаем массив из K значений метрики качества
print('Массив значений метрики:', scores) # выводим массив
print('Средняя метрика на кросс-валидации:', np.mean(scores)) # берём среднее значение массива как метрику на кросс-валидации
"""
**2. Осуществите кросс-валидацию модели при числе соседей k ∈ [1;50]. Используйте GridSearchCV . При каком k качество получилось наилучшим? Чему равна эта оценка качества? Постройте график значений метрики в зависимости от k.**
"""
%%time
knn_params = {'n_neighbors': np.arange(1, 51)} # словарь параметров (ключ: набор возможных значений)
knn_grid = GridSearchCV(knn, knn_params, cv = 5, scoring = 'accuracy') # кросс-валидация по 5 блокам
knn_grid.fit(X_train, y_train)
# Смотрим лучшие значения параметров
print(knn_grid.best_params_)
# Лучшая модель
print(knn_grid.best_estimator_)
results_df = pd.DataFrame(knn_grid.cv_results_) # результаты кросс-валидации в виде таблицы
# Рисуем валидационную кривую
# По оси х - значения гиперпараметров (param_n_neighbors)
# По оси y - значения метрики (mean_test_score)
plt.plot(results_df['param_n_neighbors'], results_df['mean_test_score'])
# Подписываем оси и график
plt.xlabel('n_neighbors')
plt.ylabel('Test accuracy')
plt.title('Validation curve')
plt.show()
"""
# 3 Выбор метрики в методе kNN
"""
"""
**1. Переберите разные варианты значений параметра p по сетке от 1 до 10 с таким шагом, чтобы всего было протестировано 200 вариантов (удобно использовать функцию numpy.linspace ). Используйте KNeighborsClassifier или
KNeighborsRegressor с оптимальным значением n_neighbors , найденнымранее. Задайте опцию weights='distance' – данный параметр добавляет в алгоритм веса, зависящие от расстояния до ближайших соседей. В качестве метрики качества снова используйте accuracy. Качество оценивайте с помощью кросс-валидации по 5 блокам.**
"""
%%time
#kf = KFold(n_splits = 5, shuffle = True, random_state = 12)
knn = KNeighborsClassifier(n_neighbors = 20, weights = 'distance') # классификатор кнн с оптимальным значением n_neighbors
scores = cross_val_score(knn, X, y, cv = kf, scoring = 'accuracy') # массив из K значений метрики качества
print('Массив значений метрики:', scores) # выводим массив
print('Средняя метрика на кросс-валидации:', np.mean(scores)) # берём среднее значение массива как метрику на кросс-валидации
"""
**2. Определите, при каком p качество на кросс-валидации оказалось оптимальным.
Обратите внимание, что cross_val_score возвращает массив показателей
качества по блокам; необходимо максимизировать среднее этих показателей.**
"""
%%time
knn_params = {'p': np.linspace(1, 10, num = 20)} # словарь параметров (ключ: набор возможных значений)
knn_grid = GridSearchCV(knn, knn_params, cv = 5, scoring = 'accuracy') # кросс-валидация по 5 блокам
knn_grid.fit(X_train, y_train) # Обучение модели (fit) на (X_train, y_train)
# Смотрим лучшие значения параметров
print(knn_grid.best_params_)
# Лучшая модель
print(knn_grid.best_estimator_)
"""
# 4 Другие метрические методы
"""
"""
**Поэкспериментируйте с другими метрическими методами для задач регрессии и классификации, представленными в библиотеке Scikit-learn**
"""
# RadiusNeighborsClassifier - это расширение алгоритма k-ближайших соседей, который делает прогнозы, используя все примеры в радиусе нового примера, а не k-ближайших соседей.
rnc = RadiusNeighborsClassifier(radius = 50) # классификатор
rnc.fit(X_train, y_train) # обучение модели (fit) на (X_train, y_train)
y_pred_1 = rnc.predict(X_valid) # предсказание (predict) для X_valid
print(y_pred_1[:5]) # смотрим предсказания y_pred_1
print(y_valid[:5]) # смотрим y_valid
print(accuracy_score(y_valid, y_pred_1)) # cравнение результатов с истинными, вычисление метрик качества
# NearestCentroid - ближайший центроидный классификатор. Каждый класс представлен своим центроидом,при этом тестовые образцы классифицируются к классу с ближайшим центроидом
nc = NearestCentroid() # классификатор
nc.fit(X_train, y_train) # обучение модели (fit) на (X_train, y_train)
y_pred_2 = nc.predict(X_valid) # предсказание (predict) для X_valid
print(y_pred_2[:5]) # смотрим предсказания y_pred_2
print(y_valid[:5]) # смотрим y_valid
print(accuracy_score(y_valid, y_pred_2)) # cравнение результатов с истинными, вычисление метрик качества