# This Python 3 environment comes with many helpful analytics libraries installed
# It is defined by the kaggle/python Docker image: https://github.com/kaggle/docker-python
# For example, here's several helpful packages to load

import numpy as np # linear algebra
import pandas as pd # data processing, CSV file I/O (e.g. pd.read_csv)

# Input data files are available in the read-only "../input/" directory
# For example, running this (by clicking run or pressing Shift+Enter) will list all files under the input directory

import os
for dirname, _, filenames in os.walk('/kaggle/input'):
    for filename in filenames:
        print(os.path.join(dirname, filename))

# You can write up to 20GB to the current directory (/kaggle/working/) that gets preserved as output when you create a version using "Save & Run All" 
# You can also write temporary files to /kaggle/temp/, but they won't be saved outside of the current session
df = pd.read_csv("/kaggle/input/heart-failure-clinical-data/heart_failure_clinical_records_dataset.csv")

import matplotlib.pyplot as plt
import seaborn as sns
import plotly.express as px
import plotly.graph_objs as go
import plotly.figure_factory as ff
from plotly.subplots import make_subplots
from plotly.offline import plot, iplot, init_notebook_mode
init_notebook_mode(connected=True)
import warnings
warnings.filterwarnings('ignore')
#Importing necessary packages
import xgboost
import lightgbm
from sklearn.svm import SVC
from sklearn.ensemble import GradientBoostingClassifier
from sklearn.ensemble import RandomForestClassifier
from sklearn.tree import DecisionTreeClassifier
from sklearn.neighbors import KNeighborsClassifier
from sklearn.linear_model import LogisticRegression
df.head()
df.info()
df.isnull().sum()
df.duplicated().any()
"""
There are no null values or dupicate values which is good 
"""
#Lets look at our target variable
sns.countplot(df['DEATH_EVENT'],data=df)
"""
## Visualizing the distribution of variables
"""
#Age Distribution
fig,ax=plt.subplots(1,2,figsize=(16,8))
ax[0].hist(df['age'],label='patients')
ax[0].set_xlabel('Age')
ax[0].set_ylabel('No of patients')
ax[0].set_yticks([5,10,15,20,25,30,35,40,45,50,55,60])
ax[0].legend()
ax[0].set_title('Age Distribution')
ax[1].hist(x=[df[df['DEATH_EVENT']==1]['age'],df[df['DEATH_EVENT']==0]['age']],stacked=True,label=['Dead','Survived'])
ax[1].set_xlabel('Age')
ax[1].set_ylabel('No of patients')
ax[1].set_yticks([5,10,15,20,25,30,35,40,45,50,55,60])
ax[1].legend()
ax[1].set_title('Age Distribution against death event')
#Creatinine Phoskinase Distribution
fig,ax=plt.subplots(1,2,figsize=(16,8))
ax[0].hist(df['creatinine_phosphokinase'],label = 'patients')
ax[0].set_xlabel('Creatinine phosphokinase')
ax[0].set_ylabel('Number of Patients')
ax[0].set_yticks([10,20,30,40,50,60,70,80,90,100,110,120,130,140,150,160,170,180,190,200,210,220,230,240,250])
ax[0].legend()
ax[0].set_title('Creatinine Phosphokinase Distribution')
ax[1].hist(x=[df[df['DEATH_EVENT']==1]['creatinine_phosphokinase'],df[df['DEATH_EVENT']==0]['creatinine_phosphokinase']],stacked=True,label=['Dead','Survived'])
ax[1].set_xlabel('Creatinine Phosphokinase')
ax[1].set_ylabel('Number of patients')
ax[1].set_yticks([10,20,30,40,50,60,70,80,90,100,110,120,130,140,150,160,170,180,190,200,210,220,230,240,250])
ax[1].set_title('Distribution of Creatinine phosphokinase against Death_event')
ax[1].legend()
#Ejection fraction Distribution:
fig,ax = plt.subplots(1,2,figsize=(16,8))
ax[0].hist(df['ejection_fraction'], label = 'patients')
ax[0].set_xlabel('Ejection_fraction')
ax[0].set_ylabel('Number of Patients')
ax[0].set_yticks([10,20,30,40,50,60,70,80,90,100,110,120,130,140,150])
ax[0].legend()
ax[0].set_title('Ejection_fraction Distribution')
ax[1].hist(x = [df[df['DEATH_EVENT']==1]['ejection_fraction'],df[df['DEATH_EVENT']==0]['ejection_fraction']], stacked=True, label=['Dead','Survived'])
ax[1].set_xlabel('Ejection_fraction')
ax[1].set_ylabel('Number of patients')
ax[1].set_yticks([10,20,30,40,50,60,70,80,90,100,110,120,130,140,150])
ax[1].set_title('Distribution of ejection_fraction against Death_event')
ax[1].legend()
#Platelets distribution
fig,ax = plt.subplots(1,2,figsize=(16,8))
ax[0].hist(df['platelets'], label = 'patients')
ax[0].set_xlabel('platelets')
ax[0].set_ylabel('Number of Patients')
ax[0].set_yticks([10,20,30,40,50,60,70,80,90,100,110,120,130,140,150])
ax[0].legend()
ax[0].set_title('Platelets Distribution')
ax[1].hist(x = [df[df['DEATH_EVENT']==1]['platelets'],df[df['DEATH_EVENT']==0]['platelets']], stacked=True, label=['Dead','Survived'])
ax[1].set_xlabel('platelets')
ax[1].set_ylabel('Number of patients')
ax[1].set_yticks(([10,20,30,40,50,60,70,80,90,100,110,120,130,140,150,160,170,180,190,200,210,220]))
ax[1].set_title('Distribution of Platelets against Death_event')
ax[1].legend()
#Serum creatinine distribution
fig,ax = plt.subplots(1,2,figsize=(16,8))
ax[0].hist(df['serum_creatinine'], label = 'patients')
ax[0].set_xlabel('serum_creatinine')
ax[0].set_ylabel('Number of Patients')
ax[0].set_yticks([10,20,30,40,50,60,70,80,90,100,110,120,130,140,150])
ax[0].legend()
ax[0].set_title('serum_creatinine Distribution')
ax[1].hist(x = [df[df['DEATH_EVENT']==1]['serum_creatinine'],df[df['DEATH_EVENT']==0]['serum_creatinine']], stacked=True, label=['Dead','Survived'])
ax[1].set_xlabel('serum_creatinine')
ax[1].set_ylabel('Number of patients')
ax[1].set_yticks(([10,20,30,40,50,60,70,80,90,100,110,120,130,140,150,160,170,180,190,200,210,220]))
ax[1].set_title('Distribution of sodium_creatinine against Death_event')
ax[1].legend()
#Serum Sodium Distribution:
fig,ax = plt.subplots(1,2,figsize=(20,10))
ax[0].hist(df['serum_sodium'], label = 'patients')
ax[0].set_xlabel('serum_sodium')
ax[0].set_ylabel('Number of Patients')
ax[0].set_yticks([10,20,30,40,50,60,70,80,90,100,110,120,130,140,150])
ax[0].legend()
ax[0].set_title('Ejection_fraction Distribution')
ax[1].hist(x = [df[df['DEATH_EVENT']==1]['serum_sodium'],df[df['DEATH_EVENT']==0]['serum_sodium']], stacked=True, label=['Dead','Survived'])
ax[1].set_xlabel('serum_sodium')
ax[1].set_ylabel('Number of patients')
ax[1].set_yticks([10,20,30,40,50,60,70,80,90,100,110,120,130,140,150])
ax[1].set_title('Distribution of serum_sodium against Death_event')
ax[1].legend()
#Time Distribution
fig,ax = plt.subplots(1,2,figsize=(16,8))
ax[0].hist(df['time'], label = 'patients')
ax[0].set_xlabel('Time')
ax[0].set_ylabel('Number of Patients')
ax[0].set_yticks([10,20,30,40,50,60,70,80,90,100,110,120,130,140,150])
ax[0].legend()
ax[0].set_title('Time Distribution')
ax[1].hist(x = [df[df['DEATH_EVENT']==1]['time'],df[df['DEATH_EVENT']==0]['time']], stacked=True, label=['Dead','Survived'])
ax[1].set_xlabel('Time')
ax[1].set_ylabel('Number of patients')
ax[1].set_yticks([10,20,30,40,50,60,70,80,90,100,110,120,130,140,150])
ax[1].set_title('Distribution of Time against Death_event')
ax[1].legend()
#Gender Distributiion
d1=df[(df['DEATH_EVENT']==0)&(df['sex']==1)]
d2=df[(df['DEATH_EVENT']==1)&(df['sex']==1)]
d3=df[(df['DEATH_EVENT']==0)&(df['sex']==0)]
d4=df[(df['DEATH_EVENT']==1)&(df['sex']==0)]

label1 = ["Male","Female"]
label2 = ['Male - Survived','Male - Died', "Female -  Survived", "Female - Died"]
values1 = [(len(d1)+len(d2)), (len(d3)+len(d4))]
values2 = [len(d1),len(d2),len(d3),len(d4)]

# Create subplots: use 'domain' type for Pie subplot
fig = make_subplots(rows=1, cols=2, specs=[[{'type':'domain'}, {'type':'domain'}]])
fig.add_trace(go.Pie(labels=label1,values=values1,hole=0.4,name='Gender'),1,1)
fig.add_trace(go.Pie(labels=label2,values=values2,hole=0.4,name="GENDER VS DEATH_EVENT"),1, 2)

# Use `hole` to create a donut-like pie chart
#fig.update_traces(hole=.4, hoverinfo="label+percent")

fig.update_layout(
    title_text="GENDER DISTRIBUTION IN THE DATASET  \
                   GENDER VS DEATH_EVENT",
    # Add annotations in the center of the donut pies.
    annotations=[dict(text='GENDER', x=0.19, y=0.5, font_size=10, showarrow=False),
                 dict(text='GENDER VS DEATH_EVENT', x=0.84, y=0.5, font_size=9, showarrow=False)],
    autosize=False,width=1200, height=500, paper_bgcolor="white")

fig.show()
#Diabetes Distribution
#Diabetes Distribution 
d1=df[(df['DEATH_EVENT']==0)&(df['diabetes']==1)]
d2=df[(df['DEATH_EVENT']==1)&(df['diabetes']==1)]
d3=df[(df['DEATH_EVENT']==0)&(df['diabetes']==0)]
d4=df[(df['DEATH_EVENT']==1)&(df['diabetes']==0)]

label1 = ["Diabetes","No Diabetes"]
label2 = ['Diabetes - Survived','Diabetes - Died', "No Diabetes -  Survived", "No Diabetes - Died"]
values1 = [(len(d1)+len(d2)), (len(d3)+len(d4))]
values2 = [len(d1),len(d2),len(d3),len(d4)]

# Create subplots: use 'domain' type for Pie subplot
fig = make_subplots(rows=1, cols=2, specs=[[{'type':'domain'}, {'type':'domain'}]])
fig.add_trace(go.Pie(labels=label1,values=values1,hole=0.4,name='Diabetes'),1,1)
fig.add_trace(go.Pie(labels=label2,values=values2,hole=0.4,name="Diabetes VS DEATH_EVENT"),1, 2)

# Use `hole` to create a donut-like pie chart
#fig.update_traces(hole=.4, hoverinfo="label+percent")

fig.update_layout(
    title_text="Diabetes DISTRIBUTION IN THE DATASET  \
                   Diabetes VS DEATH_EVENT",
    # Add annotations in the center of the donut pies.
    annotations=[dict(text='Diabetes', x=0.19, y=0.5, font_size=10, showarrow=False),
                 dict(text='Diabetes VS DEATH_EVENT', x=0.85, y=0.5, font_size=9, showarrow=False)],
    autosize=False,width=1200, height=500, paper_bgcolor="white")

fig.show()
#high_blood_pressure Distribution 
d1=df[(df['DEATH_EVENT']==0)&(df['high_blood_pressure']==1)]
d2=df[(df['DEATH_EVENT']==1)&(df['high_blood_pressure']==1)]
d3=df[(df['DEATH_EVENT']==0)&(df['high_blood_pressure']==0)]
d4=df[(df['DEATH_EVENT']==1)&(df['high_blood_pressure']==0)]

label1 = ["High BP","No High BP"]
label2 = ['High BP - Survived','High BP - Died', "No High BP -  Survived", "No High BP - Died"]
values1 = [(len(d1)+len(d2)), (len(d3)+len(d4))]
values2 = [len(d1),len(d2),len(d3),len(d4)]

# Create subplots: use 'domain' type for Pie subplot
fig = make_subplots(rows=1, cols=2, specs=[[{'type':'domain'}, {'type':'domain'}]])
fig.add_trace(go.Pie(labels=label1,values=values1,hole=0.4,name='High BP'),1,1)
fig.add_trace(go.Pie(labels=label2,values=values2,hole=0.4,name="High BP VS DEATH_EVENT"),1, 2)

# Use `hole` to create a donut-like pie chart
#fig.update_traces(hole=.4, hoverinfo="label+percent")

fig.update_layout(
    title_text="High BP DISTRIBUTION IN THE DATASET  \
                   High BP VS DEATH_EVENT",
    # Add annotations in the center of the donut pies.
    annotations=[dict(text='High BP', x=0.19, y=0.5, font_size=10, showarrow=False),
                 dict(text='High BP VS DEATH_EVENT', x=0.85, y=0.5, font_size=9, showarrow=False)],
    autosize=False,width=1200, height=500, paper_bgcolor="white")

fig.show()
#Smoking Distribution 
d1=df[(df['DEATH_EVENT']==0)&(df['smoking']==1)]
d2=df[(df['DEATH_EVENT']==1)&(df['smoking']==1)]
d3=df[(df['DEATH_EVENT']==0)&(df['smoking']==0)]
d4=df[(df['DEATH_EVENT']==1)&(df['smoking']==0)]

label1 = ["Smoking","No Smoking"]
label2 = ['Smoking - Survived','Smoking - Died', "No Smoking -  Survived", "No Smoking - Died"]
values1 = [(len(d1)+len(d2)), (len(d3)+len(d4))]
values2 = [len(d1),len(d2),len(d3),len(d4)]

# Create subplots: use 'domain' type for Pie subplot
fig = make_subplots(rows=1, cols=2, specs=[[{'type':'domain'}, {'type':'domain'}]])
fig.add_trace(go.Pie(labels=label1,values=values1,hole=0.4,name='Smoking'),1,1)
fig.add_trace(go.Pie(labels=label2,values=values2,hole=0.4,name="Smoking VS DEATH_EVENT"),1, 2)

# Use `hole` to create a donut-like pie chart
#fig.update_traces(hole=.4, hoverinfo="label+percent")

fig.update_layout(
    title_text="Smoking DISTRIBUTION IN THE DATASET  \
                   Smoking VS DEATH_EVENT",
    # Add annotations in the center of the donut pies.
    annotations=[dict(text='Smoking', x=0.19, y=0.5, font_size=10, showarrow=False),
                 dict(text='Smoking VS DEATH_EVENT', x=0.85, y=0.5, font_size=9, showarrow=False)],
    autosize=False,width=1200, height=500, paper_bgcolor="white")

fig.show()
"""
### Feature Selection
"""
# Heat map Distribution
plt.figure(figsize=(16,8))
sns.heatmap(df.corr(),annot=True)
df.corr()['DEATH_EVENT'].sort_values(ascending=False)
X=df.iloc[:,:-1]
y=df.iloc[:,-1]
from sklearn.ensemble import ExtraTreesClassifier
et=ExtraTreesClassifier()
et.fit(X,y)

et.feature_importances_
plt.figure(figsize=(14,8))
feat_importances=pd.Series(et.feature_importances_,index=X.columns)
feat_importances.nlargest(12).plot(kind='barh')
plt.show()
"""
We shall select top 4 features from above graph and use it for our model building
"""
#We shall select only 4 features
X = df[['time', 'ejection_fraction', 'serum_creatinine','age']]
y = df['DEATH_EVENT']
from sklearn.model_selection import train_test_split
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size= 0.2, random_state=0)
from sklearn.preprocessing import StandardScaler
scaler=StandardScaler()
X_train=scaler.fit_transform(X_train)
X_test=scaler.transform(X_test)
classifier = LogisticRegression()
classifier.fit(X_train, y_train)
lpred=classifier.predict(X_test)
from sklearn.metrics import confusion_matrix,classification_report
confusion_matrix(y_test,lpred)
print(classification_report(y_test,lpred))
"""
###  Mmm...Our accuracy and F1  score is  less Lets try some outlier removal process and check and then we can explore further models
"""
sns.boxplot(x = df.ejection_fraction, color = 'teal')
plt.show()

"""
We can see there are two outliers. Lets remove them
"""
# ejection_fraction outliers
df[df['ejection_fraction']>=70]
#Creating new dataframe removing outliers
df1=df[df['ejection_fraction']<70]
sns.boxplot(x=df1.time, color = 'red')
plt.show()

"""
There are no outliers in time
"""
# Boxplot for serum_creatinine
sns.boxplot(x=df1.serum_creatinine, color = 'yellow')
plt.show()

"""
Before dealing with outliers we require knowledge about the outlier, the dataset and possibly some domain knowledge.

Removing outliers without a good reason will not always increase accuracy. Without a deep understanding of what are the possible ranges that
exist within each feature, removing outliers becomes tricky.

With little research we could see that all the values in serum_creatinine falls in possible range of values. So they are not outliers. 
They are actual data points that helps in predicting DEATH_EVENT. 

"""
#We shall select only 4 features from the outlier removed dataframe
X = df1[['time', 'ejection_fraction', 'serum_creatinine','age']]
y = df1['DEATH_EVENT']
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size= 0.2, random_state=0)
scaler=StandardScaler()
X_train = scaler.fit_transform(X_train)
X_test = scaler.transform(X_test)
classifier = LogisticRegression()
classifier.fit(X_train, y_train)

logpred=classifier.predict(X_test)
from sklearn.metrics import confusion_matrix, classification_report
confusion_matrix(y_test,logpred)
print(classification_report(y_test,logpred))
"""
Thats good..After removing outliers our accuracy and F1 score has increased significantly
"""
#KNeighbors Classification
from sklearn.neighbors import KNeighborsClassifier
from sklearn.metrics import accuracy_score
#Choosing K value
list1=[]
for k in range(3,10):
    classifier=KNeighborsClassifier(n_neighbors=k)
    classifier.fit(X_train,y_train)
    y_pred=classifier.predict(X_test)
    errors=1-accuracy_score(y_test,y_pred)
    list1.append(errors)
plt.plot(list(range(3,10)),list1)
plt.show()
classifier = KNeighborsClassifier(n_neighbors=5)
classifier.fit(X_train, y_train)

kpred=classifier.predict(X_test)
confusion_matrix(y_test,kpred)
from sklearn.svm import SVC
from sklearn.metrics import confusion_matrix, accuracy_score
list1 = []
for c in [0.5,0.6,0.7,0.8,0.9,1.0]:
    classifier = SVC(C = c, random_state=0, kernel = 'rbf')
    classifier.fit(X_train, y_train)
    y_pred = classifier.predict(X_test)
    list1.append(accuracy_score(y_test,y_pred))
plt.plot([0.5,0.6,0.7,0.8,0.9,1.0], list1)
plt.show()

from sklearn.svm import SVC
classifier = SVC(C = 0.8, random_state=0, kernel = 'rbf')
classifier.fit(X_train, y_train)
y_pred = classifier.predict(X_test)
confusion_matrix(y_test,y_pred)
#DecisionTree Classifier
list5 = []
for leaves in range(2,10):
    classifier = DecisionTreeClassifier(max_leaf_nodes = leaves, random_state=0, criterion='entropy')
    classifier.fit(X_train, y_train)
    y_pred=classifier.predict(X_test)
    list5.append(accuracy_score(y_test,y_pred))
#print(mylist)
plt.plot(list(range(2,10)), list5)
plt.show()

dt_clf = DecisionTreeClassifier(max_leaf_nodes=2, random_state=0, criterion='entropy')
dt_clf.fit(X_train, y_train)
dt_pred = dt_clf.predict(X_test)

confusion_matrix(y_test,dt_pred)
print(classification_report(y_test,dt_pred))
"""
I tried other classifier models as well..We were able to get good f1 score and accuracy with Decision tree.So lets consider this as our final model

"""
"""
Note: This F1 score is applicable for this train test split method . The accuracy and f1 score decreases if we perform cross validation. There is more scope for improvement if we have to implement in real life where we can try other feature engineering techniques and other models based on cross validation

Kindly upvote this kernel if you like. 
"""