<jupyter_start><jupyter_text>Stroke Prediction by Machine Learning<NAME>DSC680-T301 Stroke is the one of the leading causes of death according to the World Health Organization (WHO) and CDC data. According to WHO, almost 11% of total deaths are caused by stroke. CDC data suggests that, in United States, someone has a stroke in every 40sec, and one person dies in every 4 minutes because of stroke. From these statistics, we can understand stroke is a major health issue around the world.The aim of this project is to build an effective model to detect high risk patients who have more chances of having stroke. They can take proper precaution and address the concerns and avoid this dangerous health problem.<jupyter_code>#import required libraries
import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
from scipy import stats
import sys
from sklearn.model_selection import train_test_split
from sklearn.preprocessing import RobustScaler
from sklearn.preprocessing import LabelEncoder
import seaborn as sns
from sklearn.model_selection import GridSearchCV
from sklearn.metrics import make_scorer, f1_score, average_precision_score, auc, precision_recall_curve, log_loss
from sklearn.ensemble import RandomForestClassifier
from sklearn.linear_model import LogisticRegression
from sklearn.dummy import DummyClassifier
from sklearn.neural_network import MLPClassifier
from sklearn.metrics import precision_recall_curve
from sklearn.pipeline import Pipeline
import joblib
import pandas_profiling as pp
from sklearn.compose import ColumnTransformer
from sklearn.feature_selection import SelectKBest
import keras
from keras.models import Sequential
from keras.layers import Dense
from keras.layers import Dropout
from sklearn.metrics import accuracy_score
from sklearn.metrics import confusion_matrix, accuracy_score, precision_score, f1_score, recall_score
%matplotlib inline
seed = 824
# read file and load into a dataframe
df1 = pd.read_csv('healthcare-dataset-stroke-data.csv')
df1
df1["gender"].value_counts()
df1["ever_married"].value_counts()
df1["work_type"].value_counts()
df1["Residence_type"].value_counts()
df1["smoking_status"].value_counts()
change_cat = {"gender":     {"Male": '1', "Female": '2'},
              "ever_married": {"Yes": '1', "No": '0'},
              "work_type": {"Private": '1', "Self-employed": '2', "children": '3',
                            "Govt_job": '4', "Never_worked" : '5'},
              "Residence_type": {"Urban": '1', "Rural": '2'},
              "smoking_status": {"never smoked": '1', "Unknown": '2', "formerly smoked": '3',
                               "smokes": '4'}
             }
df1 = df1.replace(change_cat)
df1.head()

profile = pp.ProfileReport(df1)
profile.to_file("output.html")
x_class = df1['stroke'].value_counts()
print(x_class)
print('Patients with Stroke are: {0:.2f} %'.format((x_class[1]/len(df1))*100))
print('Patients with no Stroke are: {0:.2f} %'.format((x_class[0]/len(df1))*100))
plt.bar(x=['no stroke', 'stroke'], height = x_class, width = 0.5)
plt.show()
# fill the missing values
df1['bmi'] = df1['bmi'].fillna(df1['bmi'].mean())
# check for null values again
df1.isna().sum() 
df2=df1.drop(['id'], axis=1)
df2
plt.style.use('fivethirtyeight')
outlier= df2.plot(kind='box',figsize=(20,7));
plt.xticks(rotation=70);
plt.title('Outlier in data');
# Stroke v/s Age
plt.figure(figsize=(12,10))

sns.distplot(df2[df2['stroke'] == 0]["age"], color='green') # No Stroke - green
sns.distplot(df2[df2['stroke'] == 1]["age"], color='red') # Stroke - Red

plt.title('No Stroke vs Stroke by Age', fontsize=15)
plt.xlim([18,100])
plt.show()
# Stroke v/s glucose level
plt.figure(figsize=(12,10))

sns.distplot(df2[df2['stroke'] == 0]["avg_glucose_level"], color='green') # No Stroke - green
sns.distplot(df2[df2['stroke'] == 1]["avg_glucose_level"], color='red') # Stroke - Red

plt.title('No Stroke vs Stroke by Average Glucose Level', fontsize=15)
plt.xlim([18,400])
plt.show()
# Stroke v/s bmi
plt.figure(figsize=(12,10))

sns.distplot(df2[df2['stroke'] == 0]["bmi"], color='green') # No Stroke - green
sns.distplot(df2[df2['stroke'] == 1]["bmi"], color='red') # Stroke - Red

plt.title('No Stroke vs Stroke by bmi', fontsize=15)
plt.xlim([1,100])
plt.show()
temp_df1= df2[df2.stroke == 1]   # no stroke
temp_df0= df2[df2.stroke == 0]   # stroke
# prior to removing stroke outlier
temp_df1.shape
# We will consider outlier is zscore is > 3 or < -3 and remove all of them
zscore_glucose = stats.zscore(temp_df1.avg_glucose_level) # calculate zscore 
temp_df1 = temp_df1[(abs(zscore_glucose) < 3)] # drop rows with zscore > 3 or < -3

zscore_bmi = stats.zscore(temp_df1.bmi) # calculate zscore 
temp_df1 = temp_df1[(abs(zscore_bmi) < 3)] # drop rows with zscore > 3 or < -3

# prior to removing stroke outlier
temp_df1.shape
# prior to removing no-stroke outlier
temp_df0.shape
# We will consider outlier is zscore is > 3 or < -3 and remove all of them
zscore_glucose = stats.zscore(temp_df0.avg_glucose_level) # calculate zscore 
temp_df0 = temp_df0[(abs(zscore_glucose) < 3)] # drop rows with zscore > 3 or < -3

zscore_bmi = stats.zscore(temp_df0.bmi) # calculate zscore 
temp_df0 = temp_df0[(abs(zscore_bmi) < 3)] # drop rows with zscore > 3 or < -3

# prior to removing no-stroke outlier
temp_df0.shape
df3 = pd.concat([temp_df1, temp_df0], axis=0, sort=False)  # concatenate both Classes
df3.shape
# hot encoding
X = df3.drop('stroke', axis=1)  # get only independent variables
y = df3['stroke']  # get output variable

num_cols = X.select_dtypes(include = ['int64', 'float64']).columns.to_list()
cat_cols = X.select_dtypes(include = ['object']).columns.to_list()
def label_encoder(df):
    for i in cat_cols:
        le = LabelEncoder()
        df[i] = le.fit_transform(df[i])
    return df
# robust scaler
rs = RobustScaler()
X[num_cols] = rs.fit_transform(X[num_cols])

# Label encoding
X = label_encoder(X)

X.head()
# Train Test Split

X_train, X_test, y_train, y_test = train_test_split(X, y, stratify = y, test_size=0.25, random_state=seed)
# over sample the minority class
from imblearn.over_sampling import SMOTE

smote = SMOTE()
X_train_balanced, y_train_balanced = smote.fit_resample(X_train, y_train)
# Output class count after oversampling
y_class = y_train_balanced.value_counts()
print(y_class)
params = {'n_estimators': [50, 100, 150, 200],
          'max_depth': [10,20,30,40,50],
          'max_features': ['sqrt', 'log2'],
          'class_weight': ['balanced', None],
           'bootstrap': [True, False]}


rf_grid = GridSearchCV(RandomForestClassifier(random_state=seed),
                       param_grid=params,
                       scoring={'avr': make_scorer(average_precision_score, needs_proba=True),
                                'll': make_scorer(log_loss, greater_is_better=False, needs_proba=True)},
                       n_jobs=-1,
                       cv=5,
                       refit='avr')
rf_grid.fit(X_train_balanced, y_train_balanced)
rf_file = 'rf_model.sav'  # file name to store RF model
joblib.dump(rf_grid, rf_file)  # stores the model
rf_grid.best_score_, rf_grid.best_params_
from sklearn.calibration import CalibratedClassifierCV, calibration_curve

predictions = rf_grid.predict_proba(X_train_balanced)[:, 1]
binned_true_p, binned_predict_p = calibration_curve(y_train_balanced, predictions, n_bins=10)
plt.scatter(binned_true_p, binned_predict_p)
m, b = np.polyfit(binned_true_p, binned_predict_p, 1)
plt.plot(binned_true_p, m*binned_true_p + b)
plt.show()<jupyter_output><empty_output><jupyter_text>Predicted probabilty and actual predicted value are close. So, no calibration is required.<jupyter_code>lr_pipeline = Pipeline([('select_best', SelectKBest()),
                        ('logistic', LogisticRegression(class_weight='balanced', random_state=seed))])

params = {'logistic__C': [0.1, 1.0, 10.0],
          'select_best__k': [3,5,7,9,'all']}
lr_grid = GridSearchCV(lr_pipeline,
                       param_grid=params,
                       scoring={'avr': make_scorer(average_precision_score, needs_proba=True),
                                'll': make_scorer(log_loss, greater_is_better=False, needs_proba=True)},
                       n_jobs=-1,
                       cv=5,
                       refit='avr')
lr_grid.fit(X_train_balanced, y_train_balanced)
lr_file = 'lr_model.sav'  # file name to store LR model
joblib.dump(lr_grid, lr_file)  # stores the model
lr_grid.best_score_, lr_grid.best_params_
params = [{'solver': ['lbfgs'],
           'max_iter': [100, 200, 300]},
          {'solver': ['sgd'],
           'max_iter': [100, 200, 300],
           'learning_rate_init': [0.001,0.01,1]},
          {'solver': ['adam'],
           'max_iter': [100, 200, 300],
           'learning_rate_init': [0.001,0.01,1]}]

ann_grid = GridSearchCV(MLPClassifier(random_state=seed),
                       param_grid=params,
                       scoring={'avr': make_scorer(average_precision_score, needs_proba=True),
                                'll': make_scorer(log_loss, greater_is_better=False, needs_proba=True)},
                       n_jobs=-1,
                       cv=5,
                       refit='avr')
ann_grid.fit(X_train_balanced, y_train_balanced)
ann_file = 'ann_model.sav'  # file name to store svc model
joblib.dump(ann_grid, ann_file)  # stores the model
ann_grid.best_score_, ann_grid.best_params_
# Convert to np array for modeling
X_train_balanced_arr = np.array(X_train_balanced)
X_test_arr = np.array(X_test)
y_train_balanced_arr = np.array(y_train_balanced)
y_test_arr = np.array(y_test)
dnn_model = Sequential()
#add input layer
dnn_model.add(Dense(input_dim = 10, units = 16, activation = 'relu'))
#add 2nd hidden layer
dnn_model.add(Dense(units = 24, activation = 'relu'))
#add dropout layer
dnn_model.add(Dropout(0.5))
#add 3rd hidden layer
dnn_model.add(Dense(units = 20, activation = 'relu'))
#add 4th hidden layer
dnn_model.add(Dense(units = 24, activation = 'relu'))
#add ouptut layer
dnn_model.add(Dense(units = 1, activation = 'sigmoid'))
dnn_model.summary()
dnn_model.compile(optimizer = 'adam', loss = 'binary_crossentropy', metrics = ['accuracy'])
dnn_grid = dnn_model.fit(X_train_balanced_arr, y_train_balanced_arr, batch_size = 15, epochs = 10, validation_data=(X_test_arr, y_test_arr))
# get f1 matrix after predicting the test set
y_pred = rf_grid.predict(X_test)
from sklearn.metrics import classification_report
target_names = ['no-stroke', 'stroke']
print(classification_report(y_test, y_pred, target_names=target_names))
from sklearn.metrics import confusion_matrix
confusion_matrix(y_test, y_pred)
print(accuracy_score(y_test, y_pred))
print(precision_score(y_test, y_pred))
print(recall_score(y_test, y_pred))
print(f1_score(y_test, y_pred))
# plot precision recall curve
# predict probabilities
yhat = rf_grid.predict_proba(X_test)
# retrieve just the probabilities for the positive class
pos_probs = yhat[:, 1]
# calculate model precision-recall curve
precision, recall, _ = precision_recall_curve(y_test, pos_probs)
# plot the model precision-recall curve
plt.plot(recall, precision, marker='.', label='rf')
# axis labels
plt.xlabel('Recall')
plt.ylabel('Precision')
# show the legend
plt.legend()
# show the plot
plt.show()
average_precision = average_precision_score(y_test, pos_probs)

print('Average precision-recall score: {0:0.2f}'.format(
      average_precision))<jupyter_output>Average precision-recall score: 0.13
{'fraction_non_alphanumeric': 0.12459589340323285, 'fraction_numerical': 0.025600698995194408, 'mean_word_length': 4.416942735447232, 'pattern_counts': {'":': 19, '<': 19, '<?xml version=': 0, '>': 15, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'chain_length': '2', 'dir': 'jupyter-structured-clean-dedup', 'id': '86167', 'license': 'CC-BY-3.0', 'n_tokens_mistral': 4153, 'n_tokens_neox': 3863, 'n_words': 966, 'path': 'Stroke Prediction by Machine Learning/Code/Term_Project1.ipynb', 'repo_name': 'saurabhbiswas1985/Portfolio', 'size': '11453'}