ML implemented
This commit is contained in:
@@ -0,0 +1,146 @@
|
||||
import json
|
||||
import logging
|
||||
import sys
|
||||
|
||||
import pandas as pd
|
||||
from sklearn.model_selection import train_test_split
|
||||
from sklearn.ensemble import RandomForestClassifier
|
||||
from sklearn.preprocessing import OneHotEncoder
|
||||
import numpy as np
|
||||
import re
|
||||
import os
|
||||
|
||||
|
||||
class DiseasePredictor:
|
||||
def __init__(self, csv_path):
|
||||
logging.info("\n\nInitializing DiseasePredictor")
|
||||
|
||||
self.model = RandomForestClassifier(random_state=42)
|
||||
self.data = pd.read_csv(csv_path)
|
||||
self.features = self.data.columns[0:-1] # Exclude the outcome variable
|
||||
self.outcome = self.data.columns[0] # The outcome variable
|
||||
self.categorical_features = self.data[self.features].select_dtypes(include=['object']).columns.tolist()
|
||||
self.numerical_features = self.data[self.features].select_dtypes(exclude=['object']).columns.tolist()
|
||||
self.one_hot_encoder = OneHotEncoder(handle_unknown='ignore') # Set handle_unknown to 'ignore'
|
||||
self.train()
|
||||
|
||||
def train(self):
|
||||
logging.info("Training model")
|
||||
|
||||
# Preprocess the data
|
||||
X = self.data[self.features].copy()
|
||||
y = self.data[self.outcome]
|
||||
|
||||
# One-hot encode the categorical variables
|
||||
X_encoded = pd.get_dummies(X, columns=self.categorical_features)
|
||||
# Use the DataFrame directly instead of converting to NumPy array
|
||||
# This maintains the feature names
|
||||
self.feature_names = X_encoded.columns.tolist()
|
||||
self.one_hot_encoder.fit(X[self.categorical_features])
|
||||
|
||||
# Split the data
|
||||
self.X_train, self.X_test, self.y_train, self.y_test = train_test_split(X_encoded, y, test_size=0.2,
|
||||
random_state=42)
|
||||
|
||||
# Train the model
|
||||
self.model.fit(self.X_train, self.y_train) # X_train is a DataFrame with feature names
|
||||
logging.info(f"Training accuracy: {self.model.score(self.X_test, self.y_test):.2f}")
|
||||
|
||||
|
||||
def extract_features(self, text):
|
||||
logging.info("Extracting features from text")
|
||||
|
||||
feature_values = {}
|
||||
for feature in self.features:
|
||||
if feature != 'Age' and feature != 'Gender':
|
||||
# These are the binary categorical features
|
||||
feature_values[feature] = 'Yes' if feature.lower() in text.lower() else 'No'
|
||||
|
||||
# Extract age if mentioned
|
||||
age_search = re.search(r'(\d+)\s*years?', text)
|
||||
feature_values['Age'] = int(age_search.group(1)) if age_search else np.nan
|
||||
|
||||
# Extract gender if mentioned
|
||||
if 'male' in text.lower():
|
||||
feature_values['Gender'] = 'Male'
|
||||
elif 'female' in text.lower():
|
||||
feature_values['Gender'] = 'Female'
|
||||
else:
|
||||
feature_values['Gender'] = np.nan # Missing value
|
||||
|
||||
logging.info(f"Tokens extracted: {feature_values}")
|
||||
|
||||
# Convert the features into a DataFrame to be consistent with the model's input format
|
||||
features_df = pd.DataFrame([feature_values])
|
||||
|
||||
# One-hot encode the extracted features
|
||||
aligned_features_df = pd.DataFrame(columns=self.feature_names)
|
||||
aligned_features_df = aligned_features_df._append(features_df, ignore_index=True)
|
||||
|
||||
# Fill missing columns with zeros
|
||||
missing_cols = set(self.feature_names) - set(aligned_features_df.columns)
|
||||
for c in missing_cols:
|
||||
aligned_features_df[c] = 0
|
||||
|
||||
# Ensure the order of columns matches the training data
|
||||
aligned_features_df = aligned_features_df[self.feature_names]
|
||||
|
||||
# Now we no longer need to one-hot encode 'Age' since it was not one-hot encoded during training
|
||||
return aligned_features_df
|
||||
|
||||
def predict(self, text):
|
||||
logging.info("Making a prediction")
|
||||
|
||||
features_df_encoded = self.extract_features(text)
|
||||
predictions = self.model.predict_proba(features_df_encoded)
|
||||
|
||||
# Create a dictionary to store the probabilities for each disease class
|
||||
disease_probabilities = {}
|
||||
for disease_class, probability in zip(self.model.classes_, predictions[0]):
|
||||
# Map probabilities from 0 to 100 and round to two decimal places
|
||||
mapped_probability = round(probability * 100, 2)
|
||||
disease_probabilities[disease_class.lower()] = mapped_probability
|
||||
|
||||
# Sort the disease probabilities in descending order and select the top three
|
||||
sorted_probabilities = dict(sorted(disease_probabilities.items(), key=lambda item: item[1], reverse=True)[:3])
|
||||
|
||||
# Generate JSON output
|
||||
json_output = {
|
||||
"StatusCode": 200,
|
||||
"Message": "Prediction successfully computed.",
|
||||
"Data": []
|
||||
}
|
||||
count = 0
|
||||
for key, value in sorted_probabilities.items():
|
||||
# Append each key and value as a dictionary to ensure proper JSON object format
|
||||
json_output["Data"].append({"disease": key, "probability": value})
|
||||
count += 1 # Increment the counter
|
||||
if count == 3:
|
||||
break
|
||||
# Return the JSON object
|
||||
return json.dumps(json_output, indent=4)
|
||||
|
||||
|
||||
# Example usage:
|
||||
|
||||
if __name__ == "__main__":
|
||||
if len(sys.argv) != 2:
|
||||
logging.error("Incorrect number of arguments provided.")
|
||||
json_output = {
|
||||
"StatusCode": 400,
|
||||
"Message": "Incorrect number of arguments provided.",
|
||||
"Data": []
|
||||
}
|
||||
print(json.dumps(json_output, indent=4))
|
||||
sys.exit(1)
|
||||
|
||||
logging.basicConfig(filename='disease_prediction.log', level=logging.INFO,
|
||||
format='%(asctime)s %(levelname)s:%(message)s')
|
||||
|
||||
text = sys.argv[1]
|
||||
script_directory = os.path.dirname(os.path.abspath(__file__))
|
||||
csv_path = os.path.join(script_directory, 'dataset.csv')
|
||||
|
||||
predictor = DiseasePredictor(csv_path)
|
||||
prediction_json = predictor.predict(text)
|
||||
print(prediction_json)
|
||||
Reference in New Issue
Block a user