<jupyter_start><jupyter_text>Python for DataScience assignementDataset used: https://archive.ics.uci.edu/ml/machine-learning-databases/00193/CTG.xls  1. Imports<jupyter_code>import pandas as pd
import matplotlib.pyplot as plt
import numpy as np
from sklearn import tree
from sklearn.ensemble import GradientBoostingClassifier
from sklearn.model_selection import train_test_split, cross_val_score, GridSearchCV
from sklearn.metrics import roc_curve
import pickle
import warnings
warnings.filterwarnings('ignore')
%matplotlib inline<jupyter_output><empty_output><jupyter_text>2. Loading and cleaning the dataset<jupyter_code>df = pd.read_excel("dataset.xls", 1, 1)
# Removed useless col
df = df.drop(["Unnamed: 9", "Unnamed: 31", "Unnamed: 42", "Unnamed: 44"], axis = 1)
df = df.dropna() # remove NaN 
print(df.head())<jupyter_output>b       e   AC   FM   UC   DL   DS   DP   DR     LB ...     C    D  \
0  240.0   357.0  0.0  0.0  0.0  0.0  0.0  0.0  0.0  120.0 ...  -1.0 -1.0   
1    5.0   632.0  4.0  0.0  4.0  2.0  0.0  0.0  0.0  132.0 ...  -1.0 -1.0   
2  177.0   779.0  2.0  0.0  5.0  2.0  0.0  0.0  0.0  133.0 ...  -1.0 -1.0   
3  411.0  1192.0  2.0  0.0  6.0  2.0  0.0  0.0  0.0  134.0 ...  -1.0 -1.0   
4  533.0  1147.0  4.0  0.0  5.0  0.0  0.0  0.0  0.0  132.0 ...  -1.0 -1.0   

     E   AD   DE   LD   FS  SUSP  CLASS  NSP  
0 -1.0 -1.0 -1.0 -1.0  1.0  -1.0    9.0  2.0  
1 -1.0  1.0 -1.0 -1.0 -1.0  -1.0    6.0  1.0  
2 -1.0  1.0 -1.0 -1.0 -1.0  -1.0    6.0  1.0  
3 -1.0  1.0 -1.0 -1.0 -1.0  -1.0    6.0  1.0  
4 -1.0 -1.0 -1.0 -1.0 -1.0  -1.0    2.0  1.0  

[5 rows x 42 columns]<jupyter_text>3. Dataset Visualisation Let's see the corelation matrix between the features and the ```NSP``` class.<jupyter_code>cor = df.iloc[:, 9:30]
cor['NSP'] = df['NSP']
print(list(cor.columns))
_ = plt.matshow(cor.corr())<jupyter_output>['LB', 'AC.1', 'FM.1', 'UC.1', 'DL.1', 'DS.1', 'DP.1', 'ASTV', 'MSTV', 'ALTV', 'MLTV', 'Width', 'Min', 'Max', 'Nmax', 'Nzeros', 'Mode', 'Mean', 'Median', 'Variance', 'Tendency', 'NSP']<jupyter_text>The target classes are ```NSP``` or ```CLASS``` we will do a classification on ```NSP```, let's see the classes repartition in the dataset<jupyter_code>_ = df['NSP'].value_counts().plot(kind='bar')
_ = df['CLASS'].value_counts().plot(kind='bar')<jupyter_output><empty_output><jupyter_text>The class 1 is far more populated than the others, given the nature of the dataset it can be because most of the tested population was healthy. It however will a problem to train a model. There are two things we can do at this point:* Add more data in the less represented classes, by generating it or crossing multiple datasets* Delete data in the over represented classes But the dataset is imposed and shouldn't be tempered with.  4. Preparing Dataset<jupyter_code># Split the dataset 
train_dataset, test_dataset = train_test_split(df)

X_train = train_dataset.iloc[:, 9:30] # Select the features
Y_train = train_dataset['NSP']

X_test  = test_dataset.iloc[:, 9:30]
Y_test  = test_dataset['NSP']

X_train.head()<jupyter_output><empty_output><jupyter_text>5. Models Because we have such an unbalanced dataset, we will try a decision tree with a grid search and a xgboost because these techniques are less affected than other techniques.<jupyter_code># We will use a simple decision tree to classify
tcl = tree.DecisionTreeClassifier()
#print(tcl.get_params().keys()) # helps to see what you can do
parameters = {'max_features': np.arange(1, 10), 'max_depth': np.arange(1, 10),
              'max_leaf_nodes': np.arange(2, 10), 'min_samples_leaf': np.arange(1, 10)}

tcl_grid = GridSearchCV(tcl, parameters)
tcl_grid = tcl_grid.fit(X_train, Y_train)
test_result  = np.array(cross_val_score(tcl_grid, X_test,  Y_test))

print("DecisionTree cross_val test  score: between "+str(int(min(test_result)*100))+"% and "+str(int(max(test_result)*100))+"% Mean: "+str(int(test_result.mean()*100))+"%")
xgb = GradientBoostingClassifier()
xgb = xgb.fit(X_train, Y_train)

xgb_score = np.array(cross_val_score(xgb, X_test, Y_test))

print("XGBoost cross_val test score: between "+str(int(min(xgb_score)*100))+"% and "+str(int(max(xgb_score)*100))+"% Mean: "+str(int(xgb_score.mean()*100))+"%")<jupyter_output>XGBoost cross_val test score: between 89% and 91% Mean: 90%<jupyter_text>Even with a unevenly distributed dataset, xgboost still manages to yield a 91% accuracy. Let's save the model to use it later in a web API.  ROC CurveBecause our model predicts 3 classes we cannot make a roc curve directly instead, we will make a roc curve on wether a tested person is healthy or sick (aggregating the class 2 and 3). This is a binary prediciton and the roc curve associated is:<jupyter_code>Y_test_bin = Y_test.replace(3.0, 2.0)
Y_pred_bin = xgb.predict(X_test)
Y_pred_bin[Y_pred_bin > 2.0] = 2.0
Y_test_bin -= 1
Y_pred_bin -= 1

fpr, tpr, _ = roc_curve(Y_test_bin, Y_pred_bin)
plt.title("ROC Curve")
_ = plt.plot(fpr, tpr)<jupyter_output><empty_output><jupyter_text>This ROC curve is almost ideal and confirms the model strength  6. Saving<jupyter_code>model = pickle.dumps(xgb)
with open('model.pck', 'wb') as fd:
    fd.write(model)<jupyter_output><empty_output>
{'fraction_non_alphanumeric': 0.1408151439057369, 'fraction_numerical': 0.06393664284334556, 'mean_word_length': 3.830223880597015, 'pattern_counts': {'":': 0, '<': 30, '<?xml version=': 0, '>': 31, 'https://': 1, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 1, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'chain_length': '8', 'dir': 'jupyter-structured-clean-dedup', 'id': '242390', 'license': 'MIT', 'n_tokens_mistral': 2175, 'n_tokens_neox': 2057, 'n_words': 590, 'path': 'Python for Datascience.ipynb', 'repo_name': 'WIN32GG/python-for-data-analysis', 'size': '5177'}