Heart Disease Classification Project — Part I | by Muriel Kosaka | Towards Data Science

Cardiovascular disease or heart disease is the leading cause of death amongst women and men and amongst most racial/ethnic groups in the United States. Heart disease describes a range of conditions that affect your heart. Diseases under the heart disease umbrella include blood vessel diseases, such as coronary artery disease. From the CDC, roughly every 1 in 4 deaths each year are due to heart disease. The WHO states that human life style is the main reason behind this heart problem. Apart from this there are many key factors which warns that the person may/may not getting chance of heart disease.
We will be using UCI’s heart disease dataset found here conducting exploratory data analysis (EDA), in part II we will building classification models and using ensembling methods to produce a highly accurate model. In the current dataset, publications of this study included 303 patients and chose 14 out of 76 features that are relevant in predicting heart disease.
age: age in yearssex: sex (1 = male; 0 = female)cp: chest pain type — Value 1: typical angina, Value 2: atypical angina, Value 3: non-anginal pain, Value 4: asymptomatictrestbps: resting blood pressure (in mm Hg on admission to the hospital)chol: serum cholestoral in mg/dlfbs: (fasting blood sugar > 120 mg/dl) (1 = true; 0 = false)restecg: resting electrocardiographic results- Value 0: normal, Value 1: having ST-T wave abnormality (T wave inversions and/or ST elevation or depression of > 0.05 mV), Value 2: showing probable or definite left ventricular hypertrophy by Estes’ criteriathalach: maximum heart rate achievedexang: exercise induced angina (1 = yes; 0 = no)oldpeak = ST depression induced by exercise relative to restslope: the slope of the peak exercise ST segment- Value 1: upsloping, Value 2: flat, Value 3: downslopingca: number of major vessels (0–3) colored by flourosopythal: 3 = normal; 6 = fixed defect; 7 = reversable defecttarget: 1 = disease, 0 = no disease
age: age in years
sex: sex (1 = male; 0 = female)
cp: chest pain type — Value 1: typical angina, Value 2: atypical angina, Value 3: non-anginal pain, Value 4: asymptomatic
trestbps: resting blood pressure (in mm Hg on admission to the hospital)
chol: serum cholestoral in mg/dl
fbs: (fasting blood sugar > 120 mg/dl) (1 = true; 0 = false)
restecg: resting electrocardiographic results- Value 0: normal, Value 1: having ST-T wave abnormality (T wave inversions and/or ST elevation or depression of > 0.05 mV), Value 2: showing probable or definite left ventricular hypertrophy by Estes’ criteria
thalach: maximum heart rate achieved
exang: exercise induced angina (1 = yes; 0 = no)
oldpeak = ST depression induced by exercise relative to rest
slope: the slope of the peak exercise ST segment- Value 1: upsloping, Value 2: flat, Value 3: downsloping
ca: number of major vessels (0–3) colored by flourosopy
thal: 3 = normal; 6 = fixed defect; 7 = reversable defect
target: 1 = disease, 0 = no disease
Continuous — age, trestbps, chol, thalach, oldpeakBinary — sex, fbs, exang, targetCategorical — cp, restecg, slope, ca, thal
Continuous — age, trestbps, chol, thalach, oldpeak
Binary — sex, fbs, exang, target
Categorical — cp, restecg, slope, ca, thal
Let’s get started with some EDA!
First, we import the necessary libraries and read in our dataset
import pandas as pdimport numpy as npimport matplotlib.pyplot as pltimport seaborn as snsimport plotly.graph_objects as go # To Generate Graphsimport plotly.express as px # To Generate box plot for statistical representation%matplotlib inlineimport plotly.express as pxdf=pd.read_csv('heart.csv')df.head()
Descriptions of the variables are provided above, just to reiterate, target is our dependent variable where 0 = No Heart Disease and 1 = Heart Disease.
Let’s check if there are any errors data values, such that the categorical variables have the correct number of categories as described above. After checking all variables, we found that thal and ca had one additional category within each. We will replace these values with NaN’s and then replace those values with median values
df.thal.value_counts()#3 = normal; 6 = fixed defect; 7 = reversable defect# OUTPUT2    1663    1171     180      2Name: thal, dtype: int64# Replace 0 with NaNdf.loc[df['thal']==0, 'thal'] = np.NaNdf.ca.value_counts()# number of major vessels (0-3) colored by flourosopy# OUTPUT0    1751     652     383     204      5Name: ca, dtype: int64# Replace 4 with NaNdf.loc[df['ca']==4, 'ca'] = np.NaN# Replace NaN with median valuesdf=df.fillna(df.median())
Next, let’s check for outliers by visualizing a boxplot
From this visualization we can see that chol has an extreme value which we will replace with its median value.
Now we will create some more visualizations to understand the data more, using plotly. First let’s look at the distribution of our target variable:
y=df.target.value_counts()x=['Disease','No Disease']fig=go.Figure(    data=[go.Bar(x=x,y=y,text=y, textposition='auto',)],                    layout=go.Layout(title=go.layout.Title(text='Target Variable (Heart Disease) Distribution')))fig.update_xaxes(title_text='Target')fig.update_yaxes(title_text='Number of Individuals')fig.show()
There is a slight class imbalance, but not severe enough to require upsampling/downsampling methods.
Let’s see the distribution of age in our dataset:
fig = px.histogram(df, x='age',color_discrete_sequence=['coral'])fig.update_xaxes(title_text='Age')fig.update_yaxes(title_text='Count')fig.update_layout(title_text='Distribution of Age')fig.show()
From the above graph, we can see that most patients in the current dataset are between 50–60 years old.
Let’s see the distribution of sex across our target variable:
female=df.loc[df['sex']==0]female_values=female.target.value_counts()male=df.loc[df['sex']==1]male_values=male.target.value_counts()target=['No Disease','Disease']fig = go.Figure(data=[    go.Bar(name='female', x=female_values.index, y=female_values, text=female_values, textposition='auto'),    go.Bar(name='male', x=male_values.index, y=male_values, text=male_values, textposition='auto'),])fig.update_xaxes(title_text='Target')fig.update_yaxes(title_text='Count')fig.update_layout(title_text='Distribution of Sex According to Target Variable')fig.update_layout(barmode='group')fig.show()
From the above graph we can see that in heart disease group (1), there are more male patients than female patients.
Next, let’s see how chest pain or angina (cps) varies amongst our target variable. Angina occurs when heart muscles don’t receive enough oxygen rich blood, causing major discomfort in the chest, often also spreading to the shoulders, arms, and neck.
cp=['Typical Angina','Atypical Angina','Non-Anginal Pain','Asymptomatic']y1=df.loc[df['target']==0].cp.value_counts()y2=df.loc[df['target']==1].cp.value_counts()fig = go.Figure(data=[    go.Bar(name='Disease', x=cp, y=y2),    go.Bar(name='No Disease', x=cp, y=y1)])fig.update_layout(barmode='group')fig.update_xaxes(title_text='Chest Pain Type')fig.update_yaxes(title_text='Count')fig.update_layout(title_text='Distribution of Target Variable According to Chest Pain Type')fig.show()
From the above graph, a majority of non-disease patients experience typical angina which is described as tightness in the chest compared to disease patients. There also seems to be a balance in disease patients who’ve experienced atypical angina and non-anginal pain.
Now let’s see how fasting blood sugar (fbs) varies amongst the target variable. Fbs is a diabetes indicator with fbs >120 mg/d is considered diabetic (True):
dis=df.loc[df['target']==1]dis_values=dis.fbs.value_counts()nodis=df.loc[df['target']==0]nodis_values=nodis.fbs.value_counts()target=['No Disease','Disease']d=['False','True']fig = go.Figure(data=[    go.Bar(name='Disease', x=d, y=dis_values, text=dis_values, textposition='auto'),    go.Bar(name='No Disease', x=d, y=nodis_values, text=nodis_values, textposition='auto'),])fig.update_layout(barmode='group')fig.update_xaxes(title_text='Fasting Blood Sugar (fbs)')fig.update_yaxes(title_text='Count')fig.update_layout(title_text='Distribution of Target Variable According to Fasting Blood Sugar')fig.show()
Here, we see that the number for class true, is lower compared to class false. This provides an indication that fbs might not be a strong feature differentiating between heart disease an non-disease patient.
In this article, we demonstrated some EDA techniques that can be conducted on UCI’s Heart Disease dataset in preparation for building a classification model. We’ve also created some visualizations to further understand the data and determine which variables may be influential in distinguishing between disease and non-disease patients. In Part II, I will be building a classification model and using ensemble methods to create a more accurate model.
Thank you for reading :) All code is provided on my Github.