Created
July 4, 2018 08:19
-
-
Save antvconst/eb6ef9d05d04ce0d772459efce9677dc to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
{ | |
"cells": [ | |
{ | |
"cell_type": "code", | |
"execution_count": 15, | |
"metadata": {}, | |
"outputs": [], | |
"source": [ | |
"from pandas_profiling import ProfileReport as profile\n", | |
"import pandas as pd\n", | |
"import numpy as np\n", | |
"import seaborn as sns\n", | |
"import scipy.stats as stats\n", | |
"import matplotlib.pyplot as plt\n", | |
"\n", | |
"from sklearn.preprocessing import LabelEncoder, OneHotEncoder, Imputer, StandardScaler\n", | |
"from sklearn.base import BaseEstimator, TransformerMixin\n", | |
"from sklearn.model_selection import train_test_split, cross_val_score, cross_validate\n", | |
"from sklearn.pipeline import make_pipeline\n", | |
"from sklearn.metrics import f1_score\n", | |
"from sklearn.linear_model import LogisticRegression\n", | |
"from sklearn.neighbors import KNeighborsClassifier\n", | |
"\n", | |
"import warnings\n", | |
"warnings.filterwarnings('ignore')\n", | |
"\n", | |
"%matplotlib inline" | |
] | |
}, | |
{ | |
"cell_type": "markdown", | |
"metadata": {}, | |
"source": [ | |
"# EDA и подготовка данных" | |
] | |
}, | |
{ | |
"cell_type": "markdown", | |
"metadata": {}, | |
"source": [ | |
"Загрузим данные." | |
] | |
}, | |
{ | |
"cell_type": "code", | |
"execution_count": 3, | |
"metadata": {}, | |
"outputs": [], | |
"source": [ | |
"df = pd.read_csv('dataset_57_hypothyroid.csv', na_values='?')" | |
] | |
}, | |
{ | |
"cell_type": "markdown", | |
"metadata": {}, | |
"source": [ | |
"Датасет содержит множество неинформативных признаков вроде **X_measured**. Эта же информация кодируется пропусками в соответствующих столбцах, так что можно их просто дропнуть. Аналогичным образом поступим с признаком **TBG**, содержащим 100% пропусков. Также удалим признак **referral_source**, не имеющий отношения, собственно, к предсказаниям." | |
] | |
}, | |
{ | |
"cell_type": "code", | |
"execution_count": 4, | |
"metadata": {}, | |
"outputs": [], | |
"source": [ | |
"df.drop(columns=['TSH_measured',\n", | |
" 'T3_measured',\n", | |
" 'TT4_measured',\n", | |
" 'T4U_measured',\n", | |
" 'FTI_measured',\n", | |
" 'TBG_measured',\n", | |
" 'TBG',\n", | |
" 'referral_source'], inplace=True)" | |
] | |
}, | |
{ | |
"cell_type": "markdown", | |
"metadata": {}, | |
"source": [ | |
"Численные признаки сделаем численными. Категориальные - категориальными." | |
] | |
}, | |
{ | |
"cell_type": "code", | |
"execution_count": 5, | |
"metadata": {}, | |
"outputs": [], | |
"source": [ | |
"numeric_cols = ['age', 'TSH', 'T3', 'TT4', 'T4U', 'FTI']\n", | |
"categorical_cols = [col for i, col in enumerate(df.columns) if col not in numeric_cols][:-1]\n", | |
"categorical_indices = [i-1 for i, col in enumerate(df.columns) if col not in numeric_cols][:-1]\n", | |
"\n", | |
"for col in numeric_cols:\n", | |
" df[col] = pd.to_numeric(df[col], errors='coerce')\n", | |
" \n", | |
"for col in categorical_cols:\n", | |
" df[col] = df[col].astype('category')" | |
] | |
}, | |
{ | |
"cell_type": "markdown", | |
"metadata": {}, | |
"source": [ | |
"Посмотрим, что покажет профайлер." | |
] | |
}, | |
{ | |
"cell_type": "code", | |
"execution_count": null, | |
"metadata": { | |
"scrolled": true | |
}, | |
"outputs": [], | |
"source": [ | |
"profile(df)" | |
] | |
}, | |
{ | |
"cell_type": "markdown", | |
"metadata": {}, | |
"source": [ | |
"В признаке **sex** 4% пропусков, выделим их в отдельный класс. Также удалим одинаковые строки." | |
] | |
}, | |
{ | |
"cell_type": "code", | |
"execution_count": 7, | |
"metadata": {}, | |
"outputs": [], | |
"source": [ | |
"df.drop_duplicates(inplace=True)\n", | |
"df['sex'] = df['sex'].replace(np.NaN, 'NA')" | |
] | |
}, | |
{ | |
"cell_type": "markdown", | |
"metadata": {}, | |
"source": [ | |
"Заполнять пропуски в количественных признаках будем медианой, так как, к сожалению, никакой осмысленной информации о значении переменных у нас нет. Так как заполнять пропуски полагается только информацией с трейна, а мы собираемся использовать пайплайны, то напишем подходящий класс-трансформер. Заодно добавим в него нормализацию наших численных признаков." | |
] | |
}, | |
{ | |
"cell_type": "code", | |
"execution_count": 8, | |
"metadata": {}, | |
"outputs": [], | |
"source": [ | |
"class NumericTransformer(BaseEstimator, TransformerMixin):\n", | |
" def __init__(self):\n", | |
" self.__imputer = Imputer(strategy='median')\n", | |
" self.__scaler = StandardScaler()\n", | |
" \n", | |
" def fit(self, X, y=None):\n", | |
" self.__imputer.fit(X[:6])\n", | |
" self.__scaler.fit(X[:6])\n", | |
" return self\n", | |
" \n", | |
" def fit_transform(self, X, y=None):\n", | |
" X_ = X.copy()\n", | |
" X_[:, :6] = self.__imputer.fit_transform(X_[:, :6])\n", | |
" X_[:, :6] = self.__scaler.fit_transform(X_[:, :6])\n", | |
" return X_\n", | |
" \n", | |
" def transform(self, X):\n", | |
" X_ = X.copy()\n", | |
" X_[:, :6] = self.__imputer.transform(X_[:, :6])\n", | |
" X_[:, :6] = self.__scaler.transform(X_[:, :6])\n", | |
" return X_\n", | |
" \n", | |
"num_transform = NumericTransformer()" | |
] | |
}, | |
{ | |
"cell_type": "markdown", | |
"metadata": {}, | |
"source": [ | |
"Закодируем все категории числами при помощи `LabelEncoder`." | |
] | |
}, | |
{ | |
"cell_type": "code", | |
"execution_count": 9, | |
"metadata": {}, | |
"outputs": [], | |
"source": [ | |
"for col in categorical_cols:\n", | |
" df[col] = LabelEncoder().fit_transform(df[col])" | |
] | |
}, | |
{ | |
"cell_type": "markdown", | |
"metadata": {}, | |
"source": [ | |
"Разделим датасет на матрицу предикторов и вектор меток классов." | |
] | |
}, | |
{ | |
"cell_type": "code", | |
"execution_count": 10, | |
"metadata": {}, | |
"outputs": [], | |
"source": [ | |
"X = df.drop(columns=['Class'])\n", | |
"y = df['Class']" | |
] | |
}, | |
{ | |
"cell_type": "markdown", | |
"metadata": {}, | |
"source": [ | |
"Для кодирования категориальных признаков будем использовать `OneHotEncoder`." | |
] | |
}, | |
{ | |
"cell_type": "code", | |
"execution_count": 11, | |
"metadata": {}, | |
"outputs": [], | |
"source": [ | |
"onehot_enc = OneHotEncoder(sparse=False)\n", | |
"onehot_enc.fit(X[categorical_cols])\n", | |
"\n", | |
"Xt = np.hstack((df[numeric_cols], onehot_enc.transform(X[categorical_cols])))" | |
] | |
}, | |
{ | |
"cell_type": "markdown", | |
"metadata": {}, | |
"source": [ | |
"В качестве метрики будет использовать $F$-меру, большие значения которой означают хороший баланс метрик _precision_ и _recall_." | |
] | |
}, | |
{ | |
"cell_type": "code", | |
"execution_count": 16, | |
"metadata": { | |
"scrolled": false | |
}, | |
"outputs": [ | |
{ | |
"data": { | |
"text/plain": [ | |
"{'fit_time': array([0.05388451, 0.03124237, 0.04690003]),\n", | |
" 'score_time': array([0.02098584, 0.01562357, 0.03063941]),\n", | |
" 'test_acc': array([0.94184168, 0.9450727 , 0.94898785]),\n", | |
" 'test_f1_macro': array([0.45254065, 0.50640623, 0.6767808 ]),\n", | |
" 'test_prec_macro': array([0.45278928, 0.64027706, 0.86434473]),\n", | |
" 'test_rec_macro': array([0.453125 , 0.47905069, 0.67982161]),\n", | |
" 'train_acc': array([0.9470279 , 0.94985847, 0.94668821]),\n", | |
" 'train_f1_macro': array([0.50359137, 0.51794097, 0.50498074]),\n", | |
" 'train_prec_macro': array([0.66993358, 0.66299574, 0.65684393]),\n", | |
" 'train_rec_macro': array([0.486901 , 0.50472936, 0.48923541])}" | |
] | |
}, | |
"execution_count": 16, | |
"metadata": {}, | |
"output_type": "execute_result" | |
} | |
], | |
"source": [ | |
"lr = LogisticRegression() # w/o regularization\n", | |
"\n", | |
"X_train, X_test, y_train, y_test = train_test_split(Xt, y, shuffle=False)\n", | |
"\n", | |
"estimator = make_pipeline(num_transform, lr)\n", | |
"\n", | |
"scoring = {'acc': 'accuracy',\n", | |
" 'prec_macro': 'precision_macro',\n", | |
" 'rec_macro': 'recall_macro',\n", | |
" 'f1_macro': 'f1_macro'}\n", | |
"\n", | |
"cross_validate(estimator, Xt, y, scoring=scoring)" | |
] | |
} | |
], | |
"metadata": { | |
"kernelspec": { | |
"display_name": "Python 3", | |
"language": "python", | |
"name": "python3" | |
}, | |
"language_info": { | |
"codemirror_mode": { | |
"name": "ipython", | |
"version": 3 | |
}, | |
"file_extension": ".py", | |
"mimetype": "text/x-python", | |
"name": "python", | |
"nbconvert_exporter": "python", | |
"pygments_lexer": "ipython3", | |
"version": "3.6.4" | |
} | |
}, | |
"nbformat": 4, | |
"nbformat_minor": 2 | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment