Predicting Heart Attacks with Python: A Comprehensive Guide to Classification Algorithms
Heart disease is the leading cause of death worldwide, and heart attacks are one of its most severe manifestations. Early prediction of heart attacks can save lives by enabling timely interventions and lifestyle changes. In this article, we‘ll explore how to use classification algorithms in Python to predict the likelihood of heart attacks based on patient data.
We‘ll be working with a dataset from Kaggle containing various clinical and demographic features for patients, along with a binary label indicating whether they suffered a heart attack. Throughout this guide, we‘ll walk through the entire machine learning workflow, from data preprocessing to model evaluation, and share best practices and insights along the way.
Data Cleaning and Preprocessing
Before diving into modeling, it‘s crucial to ensure the quality and integrity of our data. Real-world datasets often contain missing values, outliers, and inconsistencies that can hinder the performance of machine learning algorithms. Let‘s start by loading our dataset and examining its structure:
import pandas as pd
data = pd.read_csv(‘heart_attack_data.csv‘)
print(data.head())
print(data.info())
Output:
age sex cp trestbps chol fbs restecg thalach exang oldpeak slope ca thal target
0 63 1 3 145 233 1 0 150 0 2.3 0 0 1 1
1 37 1 2 130 250 0 1 187 0 3.5 0 0 2 1
2 41 0 1 130 204 0 0 172 0 1.4 2 0 2 1
3 56 1 1 120 236 0 1 178 0 0.8 2 0 2 1
4 57 0 0 120 354 0 1 163 1 0.6 2 0 2 1
<class ‘pandas.core.frame.DataFrame‘>
RangeIndex: 303 entries, 0 to 302
Data columns (total 14 columns):
# Column Non-Null Count Dtype
--- ------ -------------- -----
0 age 303 non-null int64
1 sex 303 non-null int64
2 cp 303 non-null int64
3 trestbps 303 non-null int64
4 chol 303 non-null int64
5 fbs 303 non-null int64
6 restecg 303 non-null int64
7 thalach 303 non-null int64
8 exang 303 non-null int64
9 oldpeak 303 non-null float64
10 slope 303 non-null int64
11 ca 303 non-null int64
12 thal 303 non-null int64
13 target 303 non-null int64
dtypes: float64(1), int64(13)
memory usage: 33.3 KB
Luckily, our dataset doesn‘t have any missing values. However, let‘s check for duplicate rows and remove them if present:
print(f"Duplicate rows: {data.duplicated().sum()}")
data.drop_duplicates(inplace=True)
Next, we‘ll handle outliers using the interquartile range (IQR) method. For each feature, we‘ll define the lower and upper bounds as Q1 – 1.5 IQR and Q3 + 1.5 IQR, respectively, and remove data points falling outside this range.
Q1 = data.quantile(0.25)
Q3 = data.quantile(0.75)
IQR = Q3 - Q1
data = data[~((data < (Q1 - 1.5 * IQR)) | (data > (Q3 + 1.5 * IQR))).any(axis=1)]
Feature Selection and Correlation Analysis
Now that our data is clean, let‘s explore the relationships between features and the target variable. We‘ll compute the Pearson and Spearman correlation coefficients and visualize them using heatmaps.
import matplotlib.pyplot as plt
import seaborn as sns
plt.figure(figsize=(12, 10))
corr_matrix = data.corr()
mask = np.triu(np.ones_like(corr_matrix, dtype=bool))
cmap = sns.diverging_palette(230, 20, as_cmap=True)
sns.heatmap(corr_matrix, mask=mask, cmap=cmap, vmax=.3, center=0, annot=True,
square=True, linewidths=.5, cbar_kws={"shrink": .5})
plt.title(‘Pearson Correlation Matrix‘)
plt.show()

plt.figure(figsize=(12, 10))
corr_matrix = data.corr(method=‘spearman‘)
sns.heatmap(corr_matrix, mask=mask, cmap=cmap, vmax=.3, center=0, annot=True,
square=True, linewidths=.5, cbar_kws={"shrink": .5})
plt.title(‘Spearman Correlation Matrix‘)
plt.show()

The heatmaps reveal that features like cp (chest pain type), thalach (maximum heart rate achieved), and oldpeak (ST depression induced by exercise relative to rest) have a stronger correlation with the target variable. We can consider dropping weakly correlated features like fbs (fasting blood sugar) and chol (serum cholesterol) to reduce dimensionality and potentially improve model performance.
Training Classification Models
With our data preprocessed and features selected, it‘s time to train some classification models! We‘ll start by splitting our data into training and testing sets:
from sklearn.model_selection import train_test_split
X = data.drop(‘target‘, axis=1)
y = data[‘target‘]
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.2, random_state=42)
Now, let‘s implement and evaluate several popular classification algorithms:
Logistic Regression
from sklearn.linear_model import LogisticRegression
from sklearn.metrics import accuracy_score, precision_score, recall_score, f1_score
lr = LogisticRegression()
lr.fit(X_train, y_train)
y_pred = lr.predict(X_test)
print(f"Accuracy: {accuracy_score(y_test, y_pred):.3f}")
print(f"Precision: {precision_score(y_test, y_pred):.3f}")
print(f"Recall: {recall_score(y_test, y_pred):.3f}")
print(f"F1 Score: {f1_score(y_test, y_pred):.3f}")
Decision Trees
from sklearn.tree import DecisionTreeClassifier
dt = DecisionTreeClassifier(max_depth=5, random_state=42)
dt.fit(X_train, y_train)
y_pred = dt.predict(X_test)
print(f"Accuracy: {accuracy_score(y_test, y_pred):.3f}")
print(f"Precision: {precision_score(y_test, y_pred):.3f}")
print(f"Recall: {recall_score(y_test, y_pred):.3f}")
print(f"F1 Score: {f1_score(y_test, y_pred):.3f}")
Random Forest
from sklearn.ensemble import RandomForestClassifier
rf = RandomForestClassifier(n_estimators=100, max_depth=5, random_state=42)
rf.fit(X_train, y_train)
y_pred = rf.predict(X_test)
print(f"Accuracy: {accuracy_score(y_test, y_pred):.3f}")
print(f"Precision: {precision_score(y_test, y_pred):.3f}")
print(f"Recall: {recall_score(y_test, y_pred):.3f}")
print(f"F1 Score: {f1_score(y_test, y_pred):.3f}")
K-Nearest Neighbors
from sklearn.neighbors import KNeighborsClassifier
knn = KNeighborsClassifier(n_neighbors=5)
knn.fit(X_train, y_train)
y_pred = knn.predict(X_test)
print(f"Accuracy: {accuracy_score(y_test, y_pred):.3f}")
print(f"Precision: {precision_score(y_test, y_pred):.3f}")
print(f"Recall: {recall_score(y_test, y_pred):.3f}")
print(f"F1 Score: {f1_score(y_test, y_pred):.3f}")
We can continue implementing other algorithms like Support Vector Machines, Naive Bayes, XGBoost, and Neural Networks in a similar fashion.
Model Comparison and Hyperparameter Tuning
After training multiple models, we can compare their performance metrics to identify the best-performing ones. We can visualize the results using a bar chart:
models = [‘Logistic Regression‘, ‘Decision Tree‘, ‘Random Forest‘, ‘KNN‘]
accuracies = [0.85, 0.79, 0.87, 0.82]
plt.figure(figsize=(10, 6))
plt.bar(models, accuracies)
plt.title(‘Model Accuracy Comparison‘)
plt.xlabel(‘Model‘)
plt.ylabel(‘Accuracy‘)
plt.ylim(0, 1)
plt.show()

To further improve the performance of our top models, we can perform hyperparameter tuning using techniques like grid search or random search with cross-validation.
from sklearn.model_selection import GridSearchCV
param_grid = {
‘n_estimators‘: [50, 100, 200],
‘max_depth‘: [3, 5, 7],
‘min_samples_split‘: [2, 5, 10],
‘min_samples_leaf‘: [1, 2, 4]
}
rf_tuned = GridSearchCV(RandomForestClassifier(random_state=42), param_grid, cv=5, scoring=‘accuracy‘)
rf_tuned.fit(X_train, y_train)
print(f"Best parameters: {rf_tuned.best_params_}")
print(f"Best accuracy: {rf_tuned.best_score_:.3f}")
Feature Importance
Understanding the relative importance of features can provide valuable insights into the factors contributing to heart attack risk. We can visualize feature importance using a horizontal bar chart:
importances = rf_tuned.best_estimator_.feature_importances_
indices = np.argsort(importances)[::-1]
plt.figure(figsize=(10, 6))
plt.title("Feature Importance")
plt.barh(range(X.shape[1]), importances[indices])
plt.yticks(range(X.shape[1]), X.columns[indices])
plt.xlabel("Relative Importance")
plt.show()

Considerations and Real-World Applications
When selecting a classification algorithm for heart attack prediction, it‘s essential to consider factors such as interpretability, training time, and the ability to handle high-dimensional data. In a healthcare setting, interpretability is crucial for understanding the reasoning behind predictions and making informed decisions.
Logistic Regression and Decision Trees offer good interpretability, while ensemble methods like Random Forest can provide higher accuracy at the cost of some interpretability. Neural Networks, on the other hand, can handle complex non-linear relationships but are often considered "black boxes."
Real-world applications of heart attack prediction models include:
- Risk assessment and stratification: Identifying high-risk patients for targeted interventions and follow-up care.
- Personalized treatment planning: Tailoring treatment strategies based on individual risk factors.
- Resource allocation: Optimizing healthcare resources by prioritizing patients with higher predicted risk.
- Early warning systems: Integrating prediction models into electronic health records for real-time alerts.
Conclusion
In this comprehensive guide, we explored the use of classification algorithms in Python for heart attack prediction. We covered data preprocessing, feature selection, model training, evaluation, and hyperparameter tuning. We also discussed the importance of interpretability and potential real-world applications.
By leveraging machine learning techniques, healthcare professionals can make data-driven decisions and improve patient outcomes. However, it‘s crucial to remember that these models are intended to assist and augment human expertise, not replace it entirely.
For those interested in further learning, consider exploring advanced techniques like ensemble methods, deep learning, and model interpretability frameworks like SHAP or LIME.
Additional Resources
- Scikit-learn documentation: https://scikit-learn.org/
- Seaborn documentation: https://seaborn.pydata.org/
- "Hands-On Machine Learning with Scikit-Learn, Keras, and TensorFlow" by Aurélien Géron
- "Machine Learning for Healthcare" on Coursera: https://www.coursera.org/learn/machine-learning-for-healthcare