Project: Predicting Heart Disease with Classification Machine Learning Algorithms | by Jarar Zaidi | Towards Data Science

1. Introduction: Scenario & Goals, Features & Predictor
2. Data Wrangling
3. Exploratory Data Analysis: Correlations, Violin & Box Plots, Filtering data by positive & negative Heart Disease patient
4. Machine Learning + Predictive Analytics: Prepare Data for Modeling, Modeling/Training, Confusion Matrix, Feature Importance, Predictions‍ ‍ ‍ ‍ ‍ ‍ ‍ ‍ ‍ ‍ ‍ ‍ ‍ ‍ ‍ ‍ ‍ ‍ ‍ ‍ ‍ ‍ ‍ ‍ ‍ ‍ ‍ ‍ ‍ ‍ ‍
5. Conclusions
You have just been hired as a Data Scientist at a Hospital with an alarming number of patients coming in reporting various cardiac symptoms. A cardiologist measures vitals & hands you this data to perform Data Analysis and predict whether certain patients have Heart Disease. We would like to make a Machine Learning algorithm where we can train our AI to learn & improve from experience. Thus, we would want to classify patients as either positive or negative for Heart Disease.
Predict whether a patient should be diagnosed with Heart Disease. This is a binary outcome.  Positive (+) = 1, patient diagnosed with Heart Disease  Negative (-) = 0, patient not diagnosed with Heart Disease
Experiment with various Classification Models & see which yields greatest accuracy.
Examine trends & correlations within our data
Determine which features are most important to Positive/Negative Heart Disease diagnosis
Our Predictor (Y, Positive or Negative diagnosis of Heart Disease) is determined by 13 features (X):
1. age (#)2. sex : 1= Male, 0= Female (Binary)3. (cp)chest pain type (4 values -Ordinal):Value 1: typical angina ,Value 2: atypical angina, Value 3: non-anginal pain , Value 4: asymptomatic 4. (trestbps) resting blood pressure (#)5. (chol) serum cholesterol in mg/dl (#)6. (fbs)fasting blood sugar > 120 mg/dl(Binary)(1 = true; 0 = false)7. (restecg) resting electrocardiography results(values 0,1,2)8. (thalach) maximum heart rate achieved (#)9. (exang) exercise induced angina (binary) (1 = yes; 0 = no) 10. (oldpeak) = ST depression induced by exercise relative to rest (#) 11. (slope) of the peak exercise ST segment (Ordinal) (Value 1: up sloping , Value 2: flat , Value 3: down sloping )12. (ca) number of major vessels (0–3, Ordinal) colored by fluoroscopy 13. (thal) maximum heart rate achieved — (Ordinal): 3 = normal; 6 = fixed defect; 7 = reversible defect
Note: Our data has 3 types of data:
Continuous (#): which is quantitative data that can be measured
Ordinal Data: Categorical data that has a order to it (0,1,2,3, etc)
Binary Data: data whose unit can take on only two possible states ( 0 &1 )
import numpy as npimport pandas as pdimport matplotlib as pltimport seaborn as snsimport matplotlib.pyplot as pltfilePath = '/Users/jarar_zaidi/Downloads/datasets-33180-43520-heart.csv'data = pd.read_csv(filePath)data.head(5)
print("(Rows, columns): " + str(data.shape))data.columns
data.nunique(axis=0)# returns the number of unique values for each variable.
#summarizes the count, mean, standard deviation, min, and max for numeric variables.data.describe()
# Display the Missing Valuesprint(data.isna().sum())
Lets see if theirs a good proportion between our positive & negative binary predictor.
data['target'].value_counts()
Correlation Matrix- let’s you see correlations between all variables.
Within seconds, you can see whether something is positively or negatively correlated with our predictor (target).
# calculate correlation matrixcorr = data.corr()plt.subplots(figsize=(15,10))sns.heatmap(corr, xticklabels=corr.columns, yticklabels=corr.columns, annot=True, cmap=sns.diverging_palette(220, 20, as_cmap=True))sns.heatmap(corr, xticklabels=corr.columns,            yticklabels=corr.columns,             annot=True,            cmap=sns.diverging_palette(220, 20, as_cmap=True))
We can see there is a positive correlation between chest pain (cp) & target (our predictor). This makes sense since, the greater amount of chest pain results in a greater chance of having heart disease. Cp (chest pain), is a ordinal feature with 4 values: Value 1: typical angina ,Value 2: atypical angina, Value 3: non-anginal pain , Value 4: asymptomatic.
In addition, we see a negative correlation between exercise induced angina (exang) & our predictor. This makes sense because when you excercise, your heart requires more blood, but narrowed arteries slow down blood flow.
Pairplots are also a great way to immediately see the correlations between all variables. But you will see me make it with only continuous columns from our data, because with so many features, it can be difficult to see each one. So instead I will make a pairplot with only our continuous features.
subData = data[['age','trestbps','chol','thalach','oldpeak']]sns.pairplot(subData)
sns.catplot(x="target", y="oldpeak", hue="slope", kind="bar", data=data);plt.title('ST depression (induced by exercise relative to rest) vs. Heart Disease',size=25)plt.xlabel('Heart Disease',size=20)plt.ylabel('ST depression',size=20)
ST segment depression occurs because when the ventricle is at rest and therefore repolarized. If the trace in the ST segment is abnormally low below the baseline, this can lead to this Heart Disease. This is supports the plot above because low ST Depression yields people at greater risk for heart disease. While a high ST depression is considered normal & healthy. The “slope” hue, refers to the peak exercise ST segment, with values: 0: upsloping , 1: flat , 2: downsloping). Both positive & negative heart disease patients exhibit equal distributions of the 3 slope categories.
The advantages of showing the Box & Violin plots is that it shows the basic statistics of the data, as well as its distribution. These plots are often used to compare the distribution of a given variable across some categories.
It shows the median, IQR, & Tukey’s fence. (minimum, first quartile (Q1), median, third quartile (Q3), and maximum).
In addition it can provide us with outliers in our data.
plt.figure(figsize=(12,8))sns.violinplot(x= 'target', y= 'oldpeak',hue="sex", inner='quartile',data= data )plt.title("Thalach Level vs. Heart Disease",fontsize=20)plt.xlabel("Heart Disease Target", fontsize=16)plt.ylabel("Thalach Level", fontsize=16)
plt.figure(figsize=(12,8))sns.boxplot(x= 'target', y= 'thalach',hue="sex", data=data )plt.title("ST depression Level vs. Heart Disease", fontsize=20)plt.xlabel("Heart Disease Target",fontsize=16)plt.ylabel("ST depression induced by exercise relative to rest", fontsize=16)
# Filtering data by POSITIVE Heart Disease patientpos_data = data[data['target']==1]pos_data.describe()
# Filtering data by NEGATIVE Heart Disease patientpos_data = data[data['target']==0]pos_data.describe()
print("(Positive Patients ST depression): " + str(pos_data['oldpeak'].mean()))print("(Negative Patients ST depression): " + str(neg_data['oldpeak'].mean()))
print("(Positive Patients thalach): " + str(pos_data['thalach'].mean()))print("(Negative Patients thalach): " + str(neg_data['thalach'].mean()))
From comparing positive and negative patients we can see there are vast differences in means for many of our 13 Features. From examining the details, we can observe that positive patients experience heightened maximum heart rate achieved (thalach) average. In addition, positive patients exhibit about 1/3rd the amount of ST depression induced by exercise relative to rest (oldpeak).
To prepare data for modeling, just remember ASN (Assign,Split, Normalize).
Assign the 13 features to X, & the last column to our classification predictor, y
X = data.iloc[:, :-1].valuesy = data.iloc[:, -1].values
Split: the data set into the Training set and Test set
from sklearn.model_selection import train_test_splitx_train, x_test, y_train, y_test = train_test_split(X,y,test_size = 0.2, random_state = 1)
Normalize: Standardizing the data will transform the data so that its distribution will have a mean of 0 and a standard deviation of 1.
from sklearn.preprocessing import StandardScalersc = StandardScaler()x_train = sc.fit_transform(x_train)x_test = sc.transform(x_test)
Now we’ll Train various Classification Models on the Training set & see which yields the highest accuracy. We will compare the accuracy of Logistic Regression, K-NN (k-Nearest Neighbours), SVM (Support Vector Machine), Naives Bayes Classifier, Decision Trees, Random Forest, and XGBoost.
Note: these are all supervised learning models.
Model 1: Logistic Regression
from sklearn.metrics import classification_report from sklearn.linear_model import LogisticRegressionmodel1 = LogisticRegression(random_state=1) # get instance of modelmodel1.fit(x_train, y_train) # Train/Fit model y_pred1 = model1.predict(x_test) # get y predictionsprint(classification_report(y_test, y_pred1)) # output accuracy
Model 2: K-NN (K-Nearest Neighbors)
from sklearn.metrics import classification_report from sklearn.neighbors import KNeighborsClassifiermodel2 = KNeighborsClassifier() # get instance of modelmodel2.fit(x_train, y_train) # Train/Fit model y_pred2 = model2.predict(x_test) # get y predictionsprint(classification_report(y_test, y_pred2)) # output accuracy
Model 3: SVM (Support Vector Machine)
from sklearn.metrics import classification_report from sklearn.svm import SVCmodel3 = SVC(random_state=1) # get instance of modelmodel3.fit(x_train, y_train) # Train/Fit model y_pred3 = model3.predict(x_test) # get y predictionsprint(classification_report(y_test, y_pred3)) # output accuracy
Model 4: Naives Bayes Classifier
from sklearn.metrics import classification_report from sklearn.naive_bayes import GaussianNBmodel4 = GaussianNB() # get instance of modelmodel4.fit(x_train, y_train) # Train/Fit model y_pred4 = model4.predict(x_test) # get y predictionsprint(classification_report(y_test, y_pred4)) # output accuracy
Model 5: Decision Trees
from sklearn.metrics import classification_report from sklearn.tree import DecisionTreeClassifiermodel5 = DecisionTreeClassifier(random_state=1) # get instance of modelmodel5.fit(x_train, y_train) # Train/Fit model y_pred5 = model5.predict(x_test) # get y predictionsprint(classification_report(y_test, y_pred5)) # output accuracy
Model 6: Random Forest 🏆
from sklearn.metrics import classification_report from sklearn.ensemble import RandomForestClassifiermodel6 = RandomForestClassifier(random_state=1)# get instance of modelmodel6.fit(x_train, y_train) # Train/Fit model y_pred6 = model6.predict(x_test) # get y predictionsprint(classification_report(y_test, y_pred6)) # output accuracy
Model 7: XGBoost
from xgboost import XGBClassifiermodel7 = XGBClassifier(random_state=1)model7.fit(x_train, y_train)y_pred7 = model7.predict(x_test)print(classification_report(y_test, y_pred7))
From comparing the 7 models, we can conclude that Model 6: Random Forest yields the highest accuracy. With an accuracy of 80%. 🏆
Precision, Recall, F1-score and Support:
Precision : be “how many are correctly classified among that class”
Recall : “how many of this class you find over the whole number of element of this class”
F1-score : harmonic mean of precision and recall values.  F1 score reaches its best value at 1 and worst value at 0.  F1 Score = 2 x ((precision x recall) / (precision + recall)) Support: # of samples of the true response that lie in that class.
from sklearn.metrics import confusion_matrix, accuracy_scorecm = confusion_matrix(y_test, y_pred6)print(cm)accuracy_score(y_test, y_pred6)
How To Interpret Confusion Matrix:
21 is the amount of True Positives in our data, while 28 is the amount of True Negatives.
9 & 3 are the number of errors.
There are 9 Type 1 errors (False Positives)- You predicted positive and it’s false.
There are 3 Type 2 errors (False Negatives)- You predicted negative and it’s false.
Hence, if we calculate the accuracy its # Correct Predicted/ # Total.In other words, where TP, FN, FP and TN represent the number of true positives, false negatives, false positives and true negatives.
Accuracy = (TP + TN)/(TP + TN + FP + FN).Accuracy =(21+28)/(21+28+9+3) = 0.80 = 80% accuracy
Feature Importance provides a score that indicates how helpful each feature was in our model.
The higher the Feature Score, the more that feature is used to make key decisions & thus the more important it is.
# get importanceimportance = model6.feature_importances_# summarize feature importancefor i,v in enumerate(importance):    print('Feature: %0d, Score: %.5f' % (i,v))
index= data.columns[:-1]importance = pd.Series(model6.feature_importances_, index=index)importance.nlargest(13).plot(kind='barh', colormap='winter')
Scenario: A patient develops cardiac symptoms & you input his vitals into the Machine Learning Algorithm.
He is a 20 year old male, with a chest pain value of 2 (atypical angina), with resting blood pressure of 110.
In addition he has a serum cholestoral of 230 mg/dl.
He is fasting blood sugar > 120 mg/dl.
He has a resting electrocardiographic result of 1.
The patients maximum heart rate achieved is 140.
Also, he was exercise induced angina.
His ST depression induced by exercise relative to rest value was 2.2.
The slope of the peak exercise ST segment is flat.
He has no major vessels colored by fluoroscopy, and in addition his maximum heart rate achieved is a reversible defect.
Based on this information, can you classify this patient with Heart Disease?
print(model6.predict(sc.transform([[20,1,2,110,230,1,1,140,1,2.2,2,0,2]])))
Predicting the Test set results:
First value represents our predicted value, Second value represents our actual value.
If the values match, then we predicted correctly.
y_pred = model6.predict(x_test)print(np.concatenate((y_pred.reshape(len(y_pred),1), y_test.reshape(len(y_test),1)),1))
We can see that our results are very accurate (80%) !
1. Out of the 13 features we examined, the top 4 significant features that helped us classify between a positive & negative Diagnosis were chest pain type (cp), maximum heart rate achieved (thalach), number of major vessels (ca), and ST depression induced by exercise relative to rest (oldpeak).
2. Our machine learning algorithm can now classify patients with Heart Disease. Now we can properly diagnose patients, & get them the help they needs to recover. By diagnosing detecting these features early, we may prevent worse symptoms from arising later.
3. Our Random Forest algorithm yields the highest accuracy, 80%. Any accuracy above 70% is considered good, but be careful because if your accuracy is extremely high, it may be too good to be true (an example of Over fitting). Thus, 80% is the ideal accuracy!
Here is access to the data set & code from my GitHub page:
https://github.com/jzaidi143/Project-Predicting-Heart-Disease-with-Classification-Machine-Learning-Algorithms
Recommendations & comments are welcomed!
Creators:
Hungarian Institute of Cardiology. Budapest: Andras Janosi, M.D.University Hospital, Zurich, Switzerland: William Steinbrunn, M.D.University Hospital, Basel, Switzerland: Matthias Pfisterer, M.D.V.A. Medical Center, Long Beach and Cleveland Clinic Foundation: Robert Detrano, M.D., Ph.D.
Hungarian Institute of Cardiology. Budapest: Andras Janosi, M.D.
University Hospital, Zurich, Switzerland: William Steinbrunn, M.D.
University Hospital, Basel, Switzerland: Matthias Pfisterer, M.D.
V.A. Medical Center, Long Beach and Cleveland Clinic Foundation: Robert Detrano, M.D., Ph.D.
Donor:  David W. Aha (aha ‘@’ ics.uci.edu) (714) 856–8779