├── scaler.pkl ├── requirements.txt ├── heart_disease_model.pkl ├── README.md ├── app.py ├── styles.css ├── script.js └── index.html /scaler.pkl: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sabarnashinchu/Predicting-Heart-Disease-/HEAD/scaler.pkl -------------------------------------------------------------------------------- /requirements.txt: -------------------------------------------------------------------------------- 1 | Flask>=2.0.1 2 | scikit-learn>=1.0.2 3 | numpy>=1.22.3 4 | pandas>=1.4.2 5 | flask-cors>=3.0.10 -------------------------------------------------------------------------------- /heart_disease_model.pkl: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sabarnashinchu/Predicting-Heart-Disease-/HEAD/heart_disease_model.pkl -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Heart Disease Prediction Application 2 | 3 | A simple web application that predicts the risk of heart disease based on medical attributes. 4 | 5 | ## Project Structure 6 | 7 | ``` 8 | ├── backend/ 9 | │ ├── app.py # Flask API 10 | │ ├── requirements.txt # Python dependencies 11 | │ └── README.md # Backend documentation 12 | └── frontend/ 13 | ├── index.html # HTML structure 14 | ├── styles.css # CSS styling 15 | └── script.js # JavaScript functionality 16 | ``` 17 | 18 | ## Features 19 | 20 | - User-friendly form for entering medical information 21 | - Machine learning model (RandomForest) for heart disease prediction 22 | - Real-time prediction with probability score 23 | - Responsive design that works on mobile and desktop 24 | 25 | ## Setup and Installation 26 | 27 | ### Backend 28 | 29 | 1. Navigate to the backend directory: 30 | ``` 31 | cd backend 32 | ``` 33 | 34 | 2. Install dependencies: 35 | ``` 36 | pip install -r requirements.txt 37 | ``` 38 | 39 | 3. Run the server: 40 | ``` 41 | python app.py 42 | ``` 43 | 44 | The API server will start on http://localhost:5000 45 | 46 | ### Frontend 47 | 48 | 1. Simply open the `frontend/index.html` file in a web browser, or serve it using a simple HTTP server: 49 | ``` 50 | cd frontend 51 | python -m http.server 8000 52 | ``` 53 | 54 | Then navigate to http://localhost:8000 in your browser. 55 | 56 | ## How to Use 57 | 58 | 1. Fill in the form with patient medical data 59 | 2. Click "Predict" button 60 | 3. View prediction result showing risk level and probability 61 | 4. Click "Make Another Prediction" to start over 62 | 63 | ## Medical Features Explanation 64 | 65 | - **Age**: Age in years 66 | - **Sex**: Gender (male/female) 67 | - **CP**: Chest pain type (0-3) 68 | - **Trestbps**: Resting blood pressure (mm Hg) 69 | - **Chol**: Serum cholesterol (mg/dl) 70 | - **FBS**: Fasting blood sugar > 120 mg/dl 71 | - **Restecg**: Resting electrocardiographic results (0-2) 72 | - **Thalach**: Maximum heart rate achieved 73 | - **Exang**: Exercise-induced angina (yes/no) 74 | - **Oldpeak**: ST depression induced by exercise 75 | - **Slope**: Slope of the peak exercise ST segment (0-2) 76 | - **CA**: Number of major vessels colored by fluoroscopy (0-3) 77 | - **Thal**: Thalassemia (1-3) 78 | 79 | ## Technical Details 80 | 81 | - Backend: Python Flask with scikit-learn 82 | - Frontend: HTML, CSS, JavaScript (vanilla) 83 | - Model: RandomForest classifier trained on sample heart disease data 84 | - Communication: RESTful API with JSON 85 | 86 | ## Disclaimer 87 | 88 | This application is for educational purposes only and should not be used for actual medical diagnosis. Always consult with a qualified healthcare professional for medical advice. -------------------------------------------------------------------------------- /app.py: -------------------------------------------------------------------------------- 1 | from flask import Flask, request, jsonify 2 | from flask_cors import CORS 3 | import numpy as np 4 | import pickle 5 | import os 6 | from sklearn.ensemble import RandomForestClassifier 7 | from sklearn.preprocessing import StandardScaler 8 | 9 | app = Flask(__name__) 10 | CORS(app) 11 | 12 | # If model doesn't exist, train a simple one 13 | model_path = 'heart_disease_model.pkl' 14 | scaler_path = 'scaler.pkl' 15 | 16 | def train_model(): 17 | # Sample data (in a real app, you'd use a proper dataset) 18 | # Features: age, sex, cp, trestbps, chol, fbs, restecg, thalach, exang, oldpeak, slope, ca, thal 19 | X = np.array([ 20 | [63, 1, 3, 145, 233, 1, 0, 150, 0, 2.3, 0, 0, 1], 21 | [37, 1, 2, 130, 250, 0, 1, 187, 0, 3.5, 0, 0, 2], 22 | [41, 0, 1, 130, 204, 0, 0, 172, 0, 1.4, 2, 0, 2], 23 | [56, 1, 1, 120, 236, 0, 1, 178, 0, 0.8, 2, 0, 2], 24 | [57, 0, 0, 120, 354, 0, 1, 163, 1, 0.6, 2, 0, 2], 25 | [57, 1, 0, 140, 192, 0, 1, 148, 0, 0.4, 1, 0, 1], 26 | [56, 0, 1, 140, 294, 0, 0, 153, 0, 1.3, 1, 0, 2], 27 | [44, 1, 1, 120, 263, 0, 1, 173, 0, 0, 2, 0, 3], 28 | [52, 1, 2, 172, 199, 1, 1, 162, 0, 0.5, 2, 0, 3], 29 | [57, 1, 2, 150, 168, 0, 1, 174, 0, 1.6, 2, 0, 2], 30 | [54, 1, 0, 140, 239, 0, 1, 160, 0, 1.2, 2, 0, 2], 31 | [48, 0, 2, 130, 275, 0, 1, 139, 0, 0.2, 2, 0, 2], 32 | [49, 1, 1, 130, 266, 0, 1, 171, 0, 0.6, 2, 0, 2], 33 | [64, 1, 3, 110, 211, 0, 0, 144, 1, 1.8, 1, 0, 2], 34 | [58, 0, 3, 150, 283, 1, 0, 162, 0, 1, 2, 0, 2] 35 | ]) 36 | 37 | # Labels: 0 = no heart disease, 1 = heart disease 38 | y = np.array([1, 1, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 0, 1, 0]) 39 | 40 | # Scale the features 41 | scaler = StandardScaler() 42 | X_scaled = scaler.fit_transform(X) 43 | 44 | # Train model 45 | model = RandomForestClassifier(n_estimators=100, random_state=42) 46 | model.fit(X_scaled, y) 47 | 48 | # Save model and scaler 49 | with open(model_path, 'wb') as f: 50 | pickle.dump(model, f) 51 | with open(scaler_path, 'wb') as f: 52 | pickle.dump(scaler, f) 53 | 54 | return model, scaler 55 | 56 | # Load or train model 57 | if os.path.exists(model_path) and os.path.exists(scaler_path): 58 | with open(model_path, 'rb') as f: 59 | model = pickle.load(f) 60 | with open(scaler_path, 'rb') as f: 61 | scaler = pickle.load(f) 62 | else: 63 | model, scaler = train_model() 64 | 65 | @app.route('/', methods=['GET']) 66 | def index(): 67 | return jsonify({ 68 | 'status': 'running', 69 | 'message': 'Heart Disease Prediction API is up and running', 70 | 'endpoints': { 71 | '/predict': 'POST - Make heart disease predictions', 72 | '/health': 'GET - Check API health status' 73 | }, 74 | 'usage': 'Access the frontend application at http://localhost:8000' 75 | }) 76 | 77 | @app.route('/predict', methods=['POST']) 78 | def predict(): 79 | data = request.json 80 | 81 | # Extract features from request 82 | features = [ 83 | float(data['age']), 84 | 1 if data['sex'] == 'male' else 0, 85 | float(data['cp']), 86 | float(data['trestbps']), 87 | float(data['chol']), 88 | 1 if float(data['fbs']) > 120 else 0, 89 | float(data['restecg']), 90 | float(data['thalach']), 91 | 1 if data['exang'] == 'yes' else 0, 92 | float(data['oldpeak']), 93 | float(data['slope']), 94 | float(data['ca']), 95 | float(data['thal']) 96 | ] 97 | 98 | # Scale features 99 | features_scaled = scaler.transform(np.array(features).reshape(1, -1)) 100 | 101 | # Make prediction 102 | prediction = model.predict(features_scaled)[0] 103 | probability = model.predict_proba(features_scaled)[0][1] 104 | 105 | return jsonify({ 106 | 'prediction': int(prediction), 107 | 'probability': float(probability), 108 | 'message': 'High risk of heart disease' if prediction == 1 else 'Low risk of heart disease' 109 | }) 110 | 111 | @app.route('/health', methods=['GET']) 112 | def health_check(): 113 | return jsonify({'status': 'healthy'}) 114 | 115 | if __name__ == '__main__': 116 | app.run(debug=True, port=5000) -------------------------------------------------------------------------------- /styles.css: -------------------------------------------------------------------------------- 1 | /* Global Styles */ 2 | * { 3 | margin: 0; 4 | padding: 0; 5 | box-sizing: border-box; 6 | } 7 | 8 | body { 9 | font-family: 'Roboto', sans-serif; 10 | line-height: 1.6; 11 | background-color: #f8f9fa; 12 | color: #333; 13 | } 14 | 15 | .container { 16 | max-width: 800px; 17 | margin: 0 auto; 18 | padding: 20px; 19 | } 20 | 21 | /* Header */ 22 | header { 23 | text-align: center; 24 | margin-bottom: 40px; 25 | padding: 20px 0; 26 | border-bottom: 1px solid #ddd; 27 | } 28 | 29 | h1 { 30 | color: #3498db; 31 | margin-bottom: 10px; 32 | } 33 | 34 | /* Form Styles */ 35 | .form-container { 36 | background-color: white; 37 | border-radius: 10px; 38 | padding: 30px; 39 | box-shadow: 0 4px 8px rgba(0, 0, 0, 0.1); 40 | margin-bottom: 30px; 41 | } 42 | 43 | .form-group { 44 | margin-bottom: 20px; 45 | } 46 | 47 | label { 48 | display: block; 49 | margin-bottom: 8px; 50 | font-weight: 500; 51 | } 52 | 53 | input[type="number"], 54 | select { 55 | width: 100%; 56 | padding: 10px 15px; 57 | border: 1px solid #ddd; 58 | border-radius: 5px; 59 | font-size: 16px; 60 | } 61 | 62 | .radio-group { 63 | display: flex; 64 | gap: 20px; 65 | } 66 | 67 | .radio-group label { 68 | font-weight: normal; 69 | display: flex; 70 | align-items: center; 71 | gap: 5px; 72 | cursor: pointer; 73 | } 74 | 75 | .predict-btn { 76 | display: block; 77 | width: 100%; 78 | padding: 12px; 79 | background-color: #3498db; 80 | color: white; 81 | border: none; 82 | border-radius: 5px; 83 | font-size: 18px; 84 | cursor: pointer; 85 | transition: background-color 0.3s; 86 | } 87 | 88 | .predict-btn:hover { 89 | background-color: #2980b9; 90 | } 91 | 92 | /* Result Styles */ 93 | .result-container { 94 | background-color: white; 95 | border-radius: 10px; 96 | padding: 30px; 97 | box-shadow: 0 4px 8px rgba(0, 0, 0, 0.1); 98 | text-align: center; 99 | } 100 | 101 | .result-container h2 { 102 | color: #3498db; 103 | margin-bottom: 20px; 104 | } 105 | 106 | #result-content { 107 | font-size: 20px; 108 | margin-bottom: 20px; 109 | padding: 20px; 110 | border-radius: 5px; 111 | } 112 | 113 | .high-risk { 114 | background-color: #ffebee; 115 | color: #e53935; 116 | border: 1px solid #ffcdd2; 117 | } 118 | 119 | .low-risk { 120 | background-color: #e8f5e9; 121 | color: #43a047; 122 | border: 1px solid #c8e6c9; 123 | } 124 | 125 | .progress-container { 126 | height: 30px; 127 | background-color: #f0f0f0; 128 | border-radius: 5px; 129 | margin-bottom: 20px; 130 | overflow: hidden; 131 | } 132 | 133 | .progress-bar { 134 | height: 100%; 135 | background-color: #3498db; 136 | transition: width 0.5s ease-in-out; 137 | text-align: right; 138 | padding-right: 10px; 139 | color: white; 140 | line-height: 30px; 141 | } 142 | 143 | .reset-btn { 144 | padding: 10px 20px; 145 | background-color: #f0f0f0; 146 | color: #333; 147 | border: none; 148 | border-radius: 5px; 149 | cursor: pointer; 150 | transition: background-color 0.3s; 151 | } 152 | 153 | .reset-btn:hover { 154 | background-color: #ddd; 155 | } 156 | 157 | /* Loading Styles */ 158 | .loading-container { 159 | position: fixed; 160 | top: 0; 161 | left: 0; 162 | width: 100%; 163 | height: 100%; 164 | background-color: rgba(255, 255, 255, 0.8); 165 | display: flex; 166 | flex-direction: column; 167 | justify-content: center; 168 | align-items: center; 169 | z-index: 1000; 170 | } 171 | 172 | .loader { 173 | border: 8px solid #f3f3f3; 174 | border-top: 8px solid #3498db; 175 | border-radius: 50%; 176 | width: 60px; 177 | height: 60px; 178 | animation: spin 1s linear infinite; 179 | margin-bottom: 20px; 180 | } 181 | 182 | @keyframes spin { 183 | 0% { transform: rotate(0deg); } 184 | 100% { transform: rotate(360deg); } 185 | } 186 | 187 | /* Utility */ 188 | .hidden { 189 | display: none; 190 | } 191 | 192 | /* Footer */ 193 | footer { 194 | text-align: center; 195 | padding: 20px 0; 196 | color: #888; 197 | font-size: 14px; 198 | } 199 | 200 | /* Responsive Styles */ 201 | @media (max-width: 768px) { 202 | .container { 203 | padding: 10px; 204 | } 205 | 206 | .form-container, .result-container { 207 | padding: 20px; 208 | } 209 | } -------------------------------------------------------------------------------- /script.js: -------------------------------------------------------------------------------- 1 | document.addEventListener('DOMContentLoaded', function() { 2 | const form = document.getElementById('prediction-form'); 3 | const resultContainer = document.getElementById('result'); 4 | const resultContent = document.getElementById('result-content'); 5 | const probabilityBar = document.getElementById('probability-bar'); 6 | const resetButton = document.getElementById('reset-btn'); 7 | const loadingContainer = document.getElementById('loading'); 8 | 9 | // Set default values for the form 10 | function setDefaultValues() { 11 | document.getElementById('age').value = 45; 12 | document.getElementById('trestbps').value = 120; 13 | document.getElementById('chol').value = 200; 14 | document.getElementById('fbs').value = 110; 15 | document.getElementById('thalach').value = 150; 16 | document.getElementById('oldpeak').value = 1.0; 17 | } 18 | 19 | // Set default values when page loads 20 | setDefaultValues(); 21 | 22 | // Handle form submission 23 | form.addEventListener('submit', function(event) { 24 | event.preventDefault(); 25 | 26 | // Show loading screen 27 | loadingContainer.classList.remove('hidden'); 28 | 29 | // Collect form data 30 | const formData = { 31 | age: document.getElementById('age').value, 32 | sex: document.querySelector('input[name="sex"]:checked').value, 33 | cp: document.getElementById('cp').value, 34 | trestbps: document.getElementById('trestbps').value, 35 | chol: document.getElementById('chol').value, 36 | fbs: document.getElementById('fbs').value, 37 | restecg: document.getElementById('restecg').value, 38 | thalach: document.getElementById('thalach').value, 39 | exang: document.querySelector('input[name="exang"]:checked').value, 40 | oldpeak: document.getElementById('oldpeak').value, 41 | slope: document.getElementById('slope').value, 42 | ca: document.getElementById('ca').value, 43 | thal: document.getElementById('thal').value 44 | }; 45 | 46 | // Send data to API 47 | predictHeartDisease(formData); 48 | }); 49 | 50 | // Reset prediction 51 | resetButton.addEventListener('click', function() { 52 | form.reset(); 53 | setDefaultValues(); 54 | resultContainer.classList.add('hidden'); 55 | form.classList.remove('hidden'); 56 | }); 57 | 58 | // Send prediction request to API 59 | function predictHeartDisease(data) { 60 | // API endpoint (update with your actual backend URL) 61 | const apiUrl = 'http://localhost:5000/predict'; 62 | 63 | fetch(apiUrl, { 64 | method: 'POST', 65 | headers: { 66 | 'Content-Type': 'application/json' 67 | }, 68 | body: JSON.stringify(data) 69 | }) 70 | .then(response => { 71 | if (!response.ok) { 72 | throw new Error('Network response was not ok'); 73 | } 74 | return response.json(); 75 | }) 76 | .then(result => { 77 | displayResult(result); 78 | }) 79 | .catch(error => { 80 | console.error('Error:', error); 81 | // Hide loading screen 82 | loadingContainer.classList.add('hidden'); 83 | 84 | // Show error message 85 | resultContent.textContent = 'Error connecting to the server. Please try again later.'; 86 | resultContent.className = 'error'; 87 | form.classList.add('hidden'); 88 | resultContainer.classList.remove('hidden'); 89 | }); 90 | } 91 | 92 | // Display prediction result 93 | function displayResult(result) { 94 | // Hide loading screen 95 | loadingContainer.classList.add('hidden'); 96 | 97 | // Format the result message 98 | const probability = Math.round(result.probability * 100); 99 | const riskClass = result.prediction === 1 ? 'high-risk' : 'low-risk'; 100 | 101 | // Update the UI 102 | resultContent.textContent = result.message; 103 | resultContent.className = riskClass; 104 | 105 | // Update probability bar 106 | probabilityBar.style.width = `${probability}%`; 107 | probabilityBar.textContent = `${probability}%`; 108 | 109 | // If high risk, make the bar red 110 | if (result.prediction === 1) { 111 | probabilityBar.style.backgroundColor = '#e53935'; 112 | } else { 113 | probabilityBar.style.backgroundColor = '#43a047'; 114 | } 115 | 116 | // Hide form and show result 117 | form.classList.add('hidden'); 118 | resultContainer.classList.remove('hidden'); 119 | } 120 | }); -------------------------------------------------------------------------------- /index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 |
4 | 5 | 6 |Enter your medical information to assess your heart disease risk
15 |