mirror of
https://github.com/andwati/predictive_analysis.git
synced 2026-11-03 23:52:16 +00:00
Add Streamlit app for student success prediction and model training script
- Created `streamlit_app.py` for a user-friendly dashboard to predict student outcomes using a Random Forest model. - Added `requirements.txt` to specify necessary Python packages for the application. - Implemented `train_and_save_model.py` to train the Random Forest model and save it along with preprocessing information. - Included detailed user input forms for various student characteristics and academic performance metrics. - Added custom styling for better UI/UX in the Streamlit app. - Integrated prediction results display with probability metrics and model performance insig
This commit is contained in:
@@ -0,0 +1,163 @@
|
||||
"""
|
||||
Model Training and Pickle Export Script
|
||||
This script recreates the Random Forest model training process and saves the model and preprocessing pipeline.
|
||||
"""
|
||||
|
||||
import pandas as pd
|
||||
import numpy as np
|
||||
import pickle
|
||||
from sklearn.ensemble import RandomForestClassifier
|
||||
from sklearn.model_selection import train_test_split
|
||||
from sklearn.preprocessing import LabelEncoder
|
||||
from sklearn.metrics import accuracy_score
|
||||
|
||||
def load_and_preprocess_data():
|
||||
"""Load and preprocess the data exactly as done in the notebook"""
|
||||
# Load data
|
||||
dataFileNamePath = "./data/data.csv"
|
||||
masterRawData = pd.read_csv(dataFileNamePath)
|
||||
data = masterRawData.copy()
|
||||
|
||||
# Fix column name typo
|
||||
data.rename(columns={'Nacionality': 'Nationality'}, inplace=True)
|
||||
|
||||
# Remove apostrophes from column names
|
||||
for column_name in data.columns:
|
||||
if "'" in column_name:
|
||||
new_column_name = column_name.replace("'", "")
|
||||
data.rename(columns={column_name: new_column_name}, inplace=True)
|
||||
|
||||
return data
|
||||
|
||||
def encode_target_variable(data):
|
||||
"""Encode the target variable as done in the notebook"""
|
||||
# Label mapping from the notebook analysis
|
||||
target_mapping = {'Dropout': 0, 'Enrolled': 1, 'Graduate': 2}
|
||||
data['Target_encoded'] = data['Target'].map(target_mapping)
|
||||
return data
|
||||
|
||||
def prepare_features_for_modeling(data):
|
||||
"""Prepare features exactly as done in the notebook"""
|
||||
# Get all categorical columns that need one-hot encoding
|
||||
categorical_columns = [
|
||||
'Marital status', 'Application mode', 'Course', 'Daytime/evening attendance',
|
||||
'Previous qualification', 'Nationality', 'Mothers qualification',
|
||||
'Fathers qualification', 'Mothers occupation', 'Fathers occupation',
|
||||
'Gender', 'Displaced', 'Educational special needs', 'Debtor',
|
||||
'Tuition fees up to date', 'Scholarship holder', 'International'
|
||||
]
|
||||
|
||||
# Additional columns that might be categorical (from notebook analysis)
|
||||
for col in data.columns:
|
||||
if col not in categorical_columns and col not in ['Target', 'Target_encoded']:
|
||||
unique_vals = data[col].nunique()
|
||||
# If low unique values, likely categorical
|
||||
if unique_vals <= 20 and col not in ['Age at enrollment']:
|
||||
categorical_columns.append(col)
|
||||
|
||||
# Create a copy for processing
|
||||
processed_data = data.copy()
|
||||
|
||||
# Perform one-hot encoding for categorical columns
|
||||
for column in categorical_columns:
|
||||
if column in processed_data.columns:
|
||||
# Get dummies with prefix
|
||||
dummies = pd.get_dummies(processed_data[column], prefix=column)
|
||||
# Concatenate with original data
|
||||
processed_data = pd.concat([processed_data, dummies], axis=1)
|
||||
# Drop original column
|
||||
processed_data.drop(columns=[column], inplace=True)
|
||||
|
||||
# Drop the original Target column, keep Target_encoded
|
||||
processed_data.drop(columns=['Target'], inplace=True)
|
||||
|
||||
return processed_data
|
||||
|
||||
def train_and_save_model():
|
||||
"""Train the Random Forest model and save both model and preprocessing info"""
|
||||
|
||||
# Load and preprocess data
|
||||
print("Loading and preprocessing data...")
|
||||
data = load_and_preprocess_data()
|
||||
|
||||
# Encode target variable
|
||||
data = encode_target_variable(data)
|
||||
|
||||
# Prepare features
|
||||
processed_data = prepare_features_for_modeling(data)
|
||||
|
||||
# Separate features and target
|
||||
X = processed_data.drop('Target_encoded', axis=1)
|
||||
y = processed_data['Target_encoded']
|
||||
|
||||
print(f"Final dataset shape: {X.shape}")
|
||||
print(f"Features: {X.columns.tolist()}")
|
||||
|
||||
# Split data (using 30% test size as in the notebook)
|
||||
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.30, random_state=42)
|
||||
|
||||
# Train Random Forest model
|
||||
print("Training Random Forest model...")
|
||||
rf_classifier = RandomForestClassifier(n_estimators=100, random_state=42)
|
||||
rf_classifier.fit(X_train, y_train)
|
||||
|
||||
# Evaluate model
|
||||
train_accuracy = rf_classifier.score(X_train, y_train)
|
||||
test_accuracy = rf_classifier.score(X_test, y_test)
|
||||
|
||||
print(f"Training Accuracy: {train_accuracy:.4f}")
|
||||
print(f"Test Accuracy: {test_accuracy:.4f}")
|
||||
|
||||
# Save the trained model
|
||||
with open('random_forest_model.pkl', 'wb') as f:
|
||||
pickle.dump(rf_classifier, f)
|
||||
|
||||
# Save feature names for consistency
|
||||
feature_names = X.columns.tolist()
|
||||
with open('feature_names.pkl', 'wb') as f:
|
||||
pickle.dump(feature_names, f)
|
||||
|
||||
# Save preprocessing information
|
||||
preprocessing_info = {
|
||||
'target_mapping': {'Dropout': 0, 'Enrolled': 1, 'Graduate': 2},
|
||||
'target_reverse_mapping': {0: 'Dropout', 1: 'Enrolled', 2: 'Graduate'},
|
||||
'categorical_columns': [
|
||||
'Marital status', 'Application mode', 'Course', 'Daytime/evening attendance',
|
||||
'Previous qualification', 'Nationality', 'Mothers qualification',
|
||||
'Fathers qualification', 'Mothers occupation', 'Fathers occupation',
|
||||
'Gender', 'Displaced', 'Educational special needs', 'Debtor',
|
||||
'Tuition fees up to date', 'Scholarship holder', 'International',
|
||||
'Application order' # Adding this based on notebook analysis
|
||||
],
|
||||
'feature_names': feature_names,
|
||||
'original_columns': [
|
||||
'Marital status', 'Application mode', 'Application order', 'Course',
|
||||
'Daytime/evening attendance', 'Previous qualification',
|
||||
'Previous qualification (grade)', 'Nationality', 'Mothers qualification',
|
||||
'Fathers qualification', 'Mothers occupation', 'Fathers occupation',
|
||||
'Admission grade', 'Displaced', 'Educational special needs', 'Debtor',
|
||||
'Tuition fees up to date', 'Gender', 'Scholarship holder',
|
||||
'Age at enrollment', 'International', 'Curricular units 1st sem (credited)',
|
||||
'Curricular units 1st sem (enrolled)', 'Curricular units 1st sem (evaluations)',
|
||||
'Curricular units 1st sem (approved)', 'Curricular units 1st sem (grade)',
|
||||
'Curricular units 1st sem (without evaluations)',
|
||||
'Curricular units 2nd sem (credited)', 'Curricular units 2nd sem (enrolled)',
|
||||
'Curricular units 2nd sem (evaluations)', 'Curricular units 2nd sem (approved)',
|
||||
'Curricular units 2nd sem (grade)', 'Curricular units 2nd sem (without evaluations)',
|
||||
'Unemployment rate', 'Inflation rate', 'GDP'
|
||||
]
|
||||
}
|
||||
|
||||
with open('preprocessing_info.pkl', 'wb') as f:
|
||||
pickle.dump(preprocessing_info, f)
|
||||
|
||||
print("Model and preprocessing info saved successfully!")
|
||||
print("Files created:")
|
||||
print("- random_forest_model.pkl")
|
||||
print("- feature_names.pkl")
|
||||
print("- preprocessing_info.pkl")
|
||||
|
||||
return rf_classifier, preprocessing_info
|
||||
|
||||
if __name__ == "__main__":
|
||||
train_and_save_model()
|
||||
Reference in New Issue
Block a user