mirror of
https://github.com/andwati/predictive_analysis.git
synced 2026-10-18 08:42:52 +00:00
602 lines
27 KiB
Python
602 lines
27 KiB
Python
"""
|
|
Student Success Prediction Dashboard
|
|
A beautiful Streamlit app for predicting student academic outcomes using the trained Random Forest model.
|
|
"""
|
|
|
|
import streamlit as st
|
|
import pandas as pd
|
|
import numpy as np
|
|
import pickle
|
|
import plotly.express as px
|
|
import plotly.graph_objects as go
|
|
from plotly.subplots import make_subplots
|
|
|
|
# Configure Streamlit page
|
|
st.set_page_config(
|
|
page_title="Student Success Predictor",
|
|
page_icon="📊",
|
|
layout="wide",
|
|
initial_sidebar_state="expanded"
|
|
)
|
|
|
|
# Custom CSS for beautiful styling
|
|
st.markdown("""
|
|
<style>
|
|
.main-header {
|
|
font-size: 3rem;
|
|
color: #1f77b4;
|
|
text-align: center;
|
|
margin-bottom: 2rem;
|
|
font-weight: bold;
|
|
}
|
|
.sub-header {
|
|
font-size: 1.5rem;
|
|
color: #2c3e50;
|
|
margin-bottom: 1rem;
|
|
}
|
|
.prediction-box {
|
|
padding: 2rem;
|
|
border-radius: 10px;
|
|
text-align: center;
|
|
font-size: 1.5rem;
|
|
font-weight: bold;
|
|
margin: 1rem 0;
|
|
}
|
|
.graduate-box {
|
|
background: linear-gradient(135deg, #28a745, #20c997);
|
|
color: white;
|
|
}
|
|
.dropout-box {
|
|
background: linear-gradient(135deg, #dc3545, #fd7e14);
|
|
color: white;
|
|
}
|
|
.enrolled-box {
|
|
background: linear-gradient(135deg, #ffc107, #fd7e14);
|
|
color: white;
|
|
}
|
|
.metric-card {
|
|
background: #f8f9fa;
|
|
padding: 1rem;
|
|
border-radius: 8px;
|
|
border-left: 4px solid #1f77b4;
|
|
}
|
|
</style>
|
|
""", unsafe_allow_html=True)
|
|
|
|
@st.cache_data
|
|
def load_model_and_info():
|
|
"""Load the trained model and preprocessing information"""
|
|
try:
|
|
with open('random_forest_model.pkl', 'rb') as f:
|
|
model = pickle.load(f)
|
|
|
|
with open('preprocessing_info.pkl', 'rb') as f:
|
|
preprocessing_info = pickle.load(f)
|
|
|
|
with open('feature_names.pkl', 'rb') as f:
|
|
feature_names = pickle.load(f)
|
|
|
|
return model, preprocessing_info, feature_names
|
|
except FileNotFoundError:
|
|
st.error("❌ Model files not found! Please run 'train_and_save_model.py' first.")
|
|
st.stop()
|
|
|
|
def create_feature_mappings():
|
|
"""Create human-readable mappings for categorical features"""
|
|
|
|
# Marital status mapping
|
|
marital_status_map = {
|
|
1: "Single",
|
|
2: "Married",
|
|
3: "Widowed",
|
|
4: "Divorced",
|
|
5: "Civil Union",
|
|
6: "Legally Separated"
|
|
}
|
|
|
|
# Application mode mapping (simplified for demo)
|
|
application_mode_map = {
|
|
1: "1st Phase - General Contingent",
|
|
2: "Ordinance No. 612/93",
|
|
5: "1st Phase - Special Contingent (Azores)",
|
|
7: "Holders of Other Higher Courses",
|
|
10: "Ordinance No. 854-B/99",
|
|
15: "International Student (Bachelor)",
|
|
16: "1st Phase - Special Contingent (Madeira)",
|
|
17: "2nd Phase - General Contingent",
|
|
18: "3rd Phase - General Contingent",
|
|
26: "Ordinance No. 533-A/99, item b2",
|
|
27: "Ordinance No. 533-A/99, item b3",
|
|
39: "Over 23 years old",
|
|
42: "Transfer",
|
|
43: "Change of course",
|
|
44: "Technological specialization diploma holders",
|
|
51: "Change of institution/course",
|
|
53: "Short cycle diploma holders",
|
|
57: "Change of institution/course (International)"
|
|
}
|
|
|
|
# Course mapping (simplified - showing most common ones)
|
|
course_map = {
|
|
33: "Biofuel Production Technologies",
|
|
171: "Animation and Multimedia Design",
|
|
8014: "Social Service (evening attendance)",
|
|
9003: "Agronomy",
|
|
9070: "Communication Design",
|
|
9085: "Veterinary Nursing",
|
|
9119: "Informatics Engineering",
|
|
9130: "Equinculture",
|
|
9147: "Management",
|
|
9238: "Social Service",
|
|
9254: "Tourism",
|
|
9500: "Nursing",
|
|
9556: "Oral Hygiene",
|
|
9670: "Advertising and Marketing Management",
|
|
9773: "Journalism and Communication",
|
|
9853: "Basic Education",
|
|
9991: "Management (evening attendance)"
|
|
}
|
|
|
|
# Previous qualification mapping
|
|
qualification_map = {
|
|
1: "Secondary education",
|
|
2: "Higher education - bachelor's degree",
|
|
3: "Higher education - degree",
|
|
4: "Higher education - master's",
|
|
5: "Higher education - doctorate",
|
|
6: "Frequency of higher education",
|
|
9: "12th year of schooling - not completed",
|
|
10: "11th year of schooling - not completed",
|
|
12: "Other - 11th year of schooling",
|
|
14: "10th year of schooling",
|
|
15: "10th year of schooling - not completed",
|
|
19: "Basic education 3rd cycle (9th/10th/11th year) or equiv.",
|
|
38: "Basic education 2nd cycle (6th/7th/8th year) or equiv.",
|
|
39: "Technological specialization course",
|
|
40: "Higher education - degree (1st cycle)",
|
|
42: "Professional course",
|
|
43: "Higher education - master (2nd cycle)"
|
|
}
|
|
|
|
# Occupation mapping (simplified)
|
|
occupation_map = {
|
|
0: "Student",
|
|
1: "Representatives of the Legislative Power and Executive Bodies",
|
|
2: "Specialists in Intellectual and Scientific Activities",
|
|
3: "Intermediate Level Technicians and Professions",
|
|
4: "Administrative staff",
|
|
5: "Personal Services, Security and Safety Workers",
|
|
6: "Farmers and Skilled Workers in Agriculture",
|
|
7: "Skilled Workers in Industry, Construction and Craftsmen",
|
|
8: "Installation and Machine Operators and Assembly Workers",
|
|
9: "Unskilled Workers",
|
|
10: "Armed Forces Professions",
|
|
90: "Other Situation",
|
|
99: "Unspecified",
|
|
122: "Health professionals",
|
|
123: "Teachers",
|
|
125: "Specialists in information and communication technologies",
|
|
131: "Intermediate level science and engineering technicians",
|
|
132: "Technicians and associate professionals, health",
|
|
134: "Intermediate level technicians from legal, social, sports",
|
|
141: "Office workers, secretaries in general and data processing",
|
|
143: "Data, accounting, statistical, financial services",
|
|
144: "Other administrative support staff",
|
|
151: "Personal service workers",
|
|
152: "Sellers",
|
|
153: "Personal care workers and the like",
|
|
171: "Skilled construction workers and the like, except electricians",
|
|
173: "Skilled workers in printing, precision instrument manufacturing",
|
|
175: "Workers in food processing, woodworking, clothing",
|
|
191: "Cleaning workers",
|
|
192: "Unskilled workers in agriculture, animal production",
|
|
193: "Unskilled workers in extractive industry, construction",
|
|
194: "Meal preparation assistants"
|
|
}
|
|
|
|
return {
|
|
'marital_status': marital_status_map,
|
|
'application_mode': application_mode_map,
|
|
'course': course_map,
|
|
'qualification': qualification_map,
|
|
'occupation': occupation_map
|
|
}
|
|
|
|
def preprocess_input(user_input, preprocessing_info):
|
|
"""Preprocess user input to match the training data format"""
|
|
|
|
# Create a DataFrame with the user input
|
|
input_df = pd.DataFrame([user_input])
|
|
|
|
# Get 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',
|
|
'Application order'
|
|
]
|
|
|
|
# Perform one-hot encoding for categorical columns
|
|
for column in categorical_columns:
|
|
if column in input_df.columns:
|
|
# Get dummies with prefix
|
|
dummies = pd.get_dummies(input_df[column], prefix=column)
|
|
# Concatenate with original data
|
|
input_df = pd.concat([input_df, dummies], axis=1)
|
|
# Drop original column
|
|
input_df.drop(columns=[column], inplace=True)
|
|
|
|
# Ensure all training features are present
|
|
feature_names = preprocessing_info['feature_names']
|
|
|
|
# Add missing columns with 0 values
|
|
for feature in feature_names:
|
|
if feature not in input_df.columns:
|
|
input_df[feature] = 0
|
|
|
|
# Select only the features used in training and in the same order
|
|
input_df = input_df[feature_names]
|
|
|
|
return input_df
|
|
|
|
def main():
|
|
"""Main Streamlit application"""
|
|
|
|
# Load model and preprocessing info
|
|
model, preprocessing_info, feature_names = load_model_and_info()
|
|
|
|
# Create feature mappings for user-friendly display
|
|
feature_maps = create_feature_mappings()
|
|
|
|
# Header
|
|
st.markdown('<h1 class="main-header">Student Success Predictor</h1>', unsafe_allow_html=True)
|
|
st.markdown("**Predict whether a student will Graduate, Dropout, or remain Enrolled based on academic and demographic factors.**")
|
|
|
|
# Sidebar for input
|
|
st.sidebar.markdown('<h2 class="sub-header">Student Information</h2>', unsafe_allow_html=True)
|
|
|
|
# Create input form
|
|
with st.sidebar:
|
|
# Quick presets
|
|
st.markdown("### Quick Start")
|
|
preset = st.selectbox("Load Sample Data",
|
|
["Custom Entry", "Typical Graduate Student", "At-Risk Student", "International Student"],
|
|
help="Choose a preset to quickly fill common scenarios")
|
|
|
|
# Set default values based on preset
|
|
defaults = {
|
|
"Custom Entry": {"age": 20, "gender": 0, "marital": 1, "app_mode": 1, "course": 9254, "prev_grade": 120, "adm_grade": 120},
|
|
"Typical Graduate Student": {"age": 19, "gender": 1, "marital": 1, "app_mode": 15, "course": 9254, "prev_grade": 160, "adm_grade": 142},
|
|
"At-Risk Student": {"age": 22, "gender": 1, "marital": 1, "app_mode": 17, "course": 171, "prev_grade": 100, "adm_grade": 110},
|
|
"International Student": {"age": 21, "gender": 0, "marital": 1, "app_mode": 1, "course": 9238, "prev_grade": 135, "adm_grade": 130}
|
|
}
|
|
|
|
if preset != "Custom Entry":
|
|
st.info(f"Loading {preset} preset values. You can modify any field below.")
|
|
|
|
# Personal Information
|
|
st.markdown("### Personal Information")
|
|
|
|
marital_status = st.selectbox(
|
|
"Marital Status",
|
|
options=list(feature_maps['marital_status'].keys()),
|
|
format_func=lambda x: feature_maps['marital_status'][x],
|
|
index=list(feature_maps['marital_status'].keys()).index(defaults[preset]["marital"])
|
|
)
|
|
|
|
gender = st.selectbox("Gender", [0, 1], format_func=lambda x: "Female" if x == 0 else "Male", index=defaults[preset]["gender"])
|
|
age_at_enrollment = st.number_input("Age at Enrollment", min_value=17, max_value=70, value=defaults[preset]["age"], step=1)
|
|
nationality = st.selectbox("Nationality", [1, 2, 6, 11, 13, 14, 17, 21, 22, 24, 25, 26, 32, 41, 62, 100, 101, 103, 105, 108, 109], index=0)
|
|
|
|
# Academic Background
|
|
st.markdown("### Academic Background")
|
|
|
|
application_mode = st.selectbox(
|
|
"Application Mode",
|
|
options=list(feature_maps['application_mode'].keys()),
|
|
format_func=lambda x: feature_maps['application_mode'].get(x, f"Mode {x}"),
|
|
index=list(feature_maps['application_mode'].keys()).index(defaults[preset]["app_mode"]) if defaults[preset]["app_mode"] in feature_maps['application_mode'] else 0
|
|
)
|
|
|
|
application_order = st.selectbox("Application Order (1st choice, 2nd choice, etc.)", [1, 2, 3, 4, 5, 6, 7, 8, 9], index=0)
|
|
|
|
course = st.selectbox(
|
|
"Course",
|
|
options=list(feature_maps['course'].keys()),
|
|
format_func=lambda x: feature_maps['course'].get(x, f"Course {x}"),
|
|
index=list(feature_maps['course'].keys()).index(defaults[preset]["course"]) if defaults[preset]["course"] in feature_maps['course'] else 0
|
|
)
|
|
|
|
daytime_evening = st.selectbox("Attendance", [0, 1], format_func=lambda x: "Evening" if x == 0 else "Daytime")
|
|
|
|
previous_qualification = st.selectbox(
|
|
"Previous Qualification",
|
|
options=list(feature_maps['qualification'].keys()),
|
|
format_func=lambda x: feature_maps['qualification'].get(x, f"Qualification {x}")
|
|
)
|
|
|
|
col1, col2 = st.columns(2)
|
|
with col1:
|
|
previous_qualification_grade = st.number_input("Previous Qualification Grade", min_value=95, max_value=190, value=defaults[preset]["prev_grade"], step=1)
|
|
with col2:
|
|
admission_grade = st.number_input("Admission Grade", min_value=95, max_value=190, value=defaults[preset]["adm_grade"], step=1)
|
|
|
|
# Family Background
|
|
st.markdown("### Family Background")
|
|
|
|
mothers_qualification = st.selectbox(
|
|
"Mother's Qualification",
|
|
options=list(feature_maps['qualification'].keys()),
|
|
format_func=lambda x: feature_maps['qualification'].get(x, f"Qualification {x}")
|
|
)
|
|
|
|
fathers_qualification = st.selectbox(
|
|
"Father's Qualification",
|
|
options=list(feature_maps['qualification'].keys()),
|
|
format_func=lambda x: feature_maps['qualification'].get(x, f"Qualification {x}")
|
|
)
|
|
|
|
mothers_occupation = st.selectbox(
|
|
"Mother's Occupation",
|
|
options=list(feature_maps['occupation'].keys()),
|
|
format_func=lambda x: feature_maps['occupation'].get(x, f"Occupation {x}")
|
|
)
|
|
|
|
fathers_occupation = st.selectbox(
|
|
"Father's Occupation",
|
|
options=list(feature_maps['occupation'].keys()),
|
|
format_func=lambda x: feature_maps['occupation'].get(x, f"Occupation {x}")
|
|
)
|
|
|
|
# Financial and Social Factors
|
|
st.markdown("### Financial & Social")
|
|
|
|
col1, col2 = st.columns(2)
|
|
with col1:
|
|
displaced = st.radio("Displaced Student", [0, 1], format_func=lambda x: "No" if x == 0 else "Yes", horizontal=True)
|
|
educational_special_needs = st.radio("Educational Special Needs", [0, 1], format_func=lambda x: "No" if x == 0 else "Yes", horizontal=True)
|
|
debtor = st.radio("Has Outstanding Debts", [0, 1], format_func=lambda x: "No" if x == 0 else "Yes", horizontal=True)
|
|
with col2:
|
|
tuition_fees_up_to_date = st.radio("Tuition Fees Up to Date", [0, 1], format_func=lambda x: "No" if x == 0 else "Yes", horizontal=True)
|
|
scholarship_holder = st.radio("Scholarship Holder", [0, 1], format_func=lambda x: "No" if x == 0 else "Yes", horizontal=True)
|
|
international = st.radio("International Student", [0, 1], format_func=lambda x: "No" if x == 0 else "Yes", horizontal=True)
|
|
|
|
# Academic Performance - 1st Semester
|
|
st.markdown("### 1st Semester Performance")
|
|
st.caption("Enter the student's academic performance for their first semester")
|
|
|
|
# Set default academic values based on preset
|
|
first_sem_defaults = {
|
|
"Custom Entry": {"enrolled": 6, "approved": 6, "grade": 13, "credited": 0, "evaluations": 6, "without": 0},
|
|
"Typical Graduate Student": {"enrolled": 6, "approved": 6, "grade": 14, "credited": 0, "evaluations": 6, "without": 0},
|
|
"At-Risk Student": {"enrolled": 6, "approved": 0, "grade": 0, "credited": 0, "evaluations": 0, "without": 0},
|
|
"International Student": {"enrolled": 6, "approved": 6, "grade": 13, "credited": 0, "evaluations": 6, "without": 0}
|
|
}
|
|
|
|
col1, col2 = st.columns(2)
|
|
with col1:
|
|
curricular_units_1st_sem_enrolled = st.number_input("Enrolled Units", min_value=0, max_value=26, value=first_sem_defaults[preset]["enrolled"], step=1, key="1st_enrolled", help="Number of courses registered for")
|
|
curricular_units_1st_sem_approved = st.number_input("Approved Units", min_value=0, max_value=26, value=first_sem_defaults[preset]["approved"], step=1, key="1st_approved", help="Number of courses passed")
|
|
curricular_units_1st_sem_credited = st.number_input("Credited Units", min_value=0, max_value=26, value=first_sem_defaults[preset]["credited"], step=1, key="1st_credited", help="Credits from previous education")
|
|
with col2:
|
|
curricular_units_1st_sem_evaluations = st.number_input("Evaluations", min_value=0, max_value=45, value=first_sem_defaults[preset]["evaluations"], step=1, key="1st_evaluations", help="Number of exams taken")
|
|
curricular_units_1st_sem_grade = st.number_input("Average Grade (0-20)", min_value=0, max_value=20, value=first_sem_defaults[preset]["grade"], step=1, key="1st_grade", help="Average grade on 0-20 scale")
|
|
curricular_units_1st_sem_without_evaluations = st.number_input("Without Evaluations", min_value=0, max_value=14, value=first_sem_defaults[preset]["without"], step=1, key="1st_without", help="Courses registered but not evaluated")
|
|
|
|
# Academic Performance - 2nd Semester
|
|
st.markdown("### 2nd Semester Performance")
|
|
st.caption("Enter the student's academic performance for their second semester")
|
|
|
|
# Set default academic values for 2nd semester based on preset
|
|
second_sem_defaults = {
|
|
"Custom Entry": {"enrolled": 6, "approved": 6, "grade": 13, "credited": 0, "evaluations": 6, "without": 0},
|
|
"Typical Graduate Student": {"enrolled": 6, "approved": 6, "grade": 14, "credited": 0, "evaluations": 6, "without": 0},
|
|
"At-Risk Student": {"enrolled": 6, "approved": 0, "grade": 0, "credited": 0, "evaluations": 0, "without": 0},
|
|
"International Student": {"enrolled": 6, "approved": 6, "grade": 14, "credited": 0, "evaluations": 6, "without": 0}
|
|
}
|
|
|
|
col1, col2 = st.columns(2)
|
|
with col1:
|
|
curricular_units_2nd_sem_enrolled = st.number_input("Enrolled Units", min_value=0, max_value=23, value=second_sem_defaults[preset]["enrolled"], step=1, key="2nd_enrolled", help="Number of courses registered for")
|
|
curricular_units_2nd_sem_approved = st.number_input("Approved Units", min_value=0, max_value=20, value=second_sem_defaults[preset]["approved"], step=1, key="2nd_approved", help="Number of courses passed")
|
|
curricular_units_2nd_sem_credited = st.number_input("Credited Units", min_value=0, max_value=20, value=second_sem_defaults[preset]["credited"], step=1, key="2nd_credited", help="Credits from previous education")
|
|
with col2:
|
|
curricular_units_2nd_sem_evaluations = st.number_input("Evaluations", min_value=0, max_value=33, value=second_sem_defaults[preset]["evaluations"], step=1, key="2nd_evaluations", help="Number of exams taken")
|
|
curricular_units_2nd_sem_grade = st.number_input("Average Grade (0-20)", min_value=0, max_value=20, value=second_sem_defaults[preset]["grade"], step=1, key="2nd_grade", help="Average grade on 0-20 scale")
|
|
curricular_units_2nd_sem_without_evaluations = st.number_input("Without Evaluations", min_value=0, max_value=12, value=second_sem_defaults[preset]["without"], step=1, key="2nd_without", help="Courses registered but not evaluated")
|
|
|
|
# Economic Indicators
|
|
st.markdown("### Economic Indicators")
|
|
st.caption("These are typically set by the institution based on current economic conditions")
|
|
|
|
col1, col2, col3 = st.columns(3)
|
|
with col1:
|
|
unemployment_rate = st.number_input("Unemployment Rate (%)", min_value=7.0, max_value=16.2, value=10.8, step=0.1, format="%.1f")
|
|
with col2:
|
|
inflation_rate = st.number_input("Inflation Rate (%)", min_value=-0.8, max_value=3.7, value=1.4, step=0.1, format="%.1f")
|
|
with col3:
|
|
gdp = st.number_input("GDP Growth", min_value=-4.06, max_value=3.51, value=1.74, step=0.1, format="%.2f")
|
|
|
|
# Create user input dictionary
|
|
user_input = {
|
|
'Marital status': marital_status,
|
|
'Application mode': application_mode,
|
|
'Application order': application_order,
|
|
'Course': course,
|
|
'Daytime/evening attendance': daytime_evening,
|
|
'Previous qualification': previous_qualification,
|
|
'Previous qualification (grade)': previous_qualification_grade,
|
|
'Nationality': nationality,
|
|
'Mothers qualification': mothers_qualification,
|
|
'Fathers qualification': fathers_qualification,
|
|
'Mothers occupation': mothers_occupation,
|
|
'Fathers occupation': fathers_occupation,
|
|
'Admission grade': admission_grade,
|
|
'Displaced': displaced,
|
|
'Educational special needs': educational_special_needs,
|
|
'Debtor': debtor,
|
|
'Tuition fees up to date': tuition_fees_up_to_date,
|
|
'Gender': gender,
|
|
'Scholarship holder': scholarship_holder,
|
|
'Age at enrollment': age_at_enrollment,
|
|
'International': international,
|
|
'Curricular units 1st sem (credited)': curricular_units_1st_sem_credited,
|
|
'Curricular units 1st sem (enrolled)': curricular_units_1st_sem_enrolled,
|
|
'Curricular units 1st sem (evaluations)': curricular_units_1st_sem_evaluations,
|
|
'Curricular units 1st sem (approved)': curricular_units_1st_sem_approved,
|
|
'Curricular units 1st sem (grade)': curricular_units_1st_sem_grade,
|
|
'Curricular units 1st sem (without evaluations)': curricular_units_1st_sem_without_evaluations,
|
|
'Curricular units 2nd sem (credited)': curricular_units_2nd_sem_credited,
|
|
'Curricular units 2nd sem (enrolled)': curricular_units_2nd_sem_enrolled,
|
|
'Curricular units 2nd sem (evaluations)': curricular_units_2nd_sem_evaluations,
|
|
'Curricular units 2nd sem (approved)': curricular_units_2nd_sem_approved,
|
|
'Curricular units 2nd sem (grade)': curricular_units_2nd_sem_grade,
|
|
'Curricular units 2nd sem (without evaluations)': curricular_units_2nd_sem_without_evaluations,
|
|
'Unemployment rate': unemployment_rate,
|
|
'Inflation rate': inflation_rate,
|
|
'GDP': gdp
|
|
}
|
|
|
|
# Main content area
|
|
col1, col2 = st.columns([2, 1])
|
|
|
|
with col1:
|
|
st.markdown('<h2 class="sub-header">Prediction Results</h2>', unsafe_allow_html=True)
|
|
|
|
# Make prediction
|
|
if st.button("Predict Student Outcome", type="primary"):
|
|
with st.spinner("Analyzing student data..."):
|
|
# Preprocess input
|
|
processed_input = preprocess_input(user_input, preprocessing_info)
|
|
|
|
# Make prediction
|
|
prediction = model.predict(processed_input)[0]
|
|
prediction_proba = model.predict_proba(processed_input)[0]
|
|
|
|
# Get prediction label
|
|
target_reverse_mapping = preprocessing_info['target_reverse_mapping']
|
|
predicted_outcome = target_reverse_mapping[prediction]
|
|
|
|
# Display prediction with styling
|
|
if predicted_outcome == "Graduate":
|
|
st.markdown(f'<div class="prediction-box graduate-box">Predicted Outcome: GRADUATE</div>', unsafe_allow_html=True)
|
|
elif predicted_outcome == "Dropout":
|
|
st.markdown(f'<div class="prediction-box dropout-box">Predicted Outcome: DROPOUT</div>', unsafe_allow_html=True)
|
|
else:
|
|
st.markdown(f'<div class="prediction-box enrolled-box">Predicted Outcome: ENROLLED</div>', unsafe_allow_html=True)
|
|
|
|
# Display probabilities
|
|
st.markdown("### Prediction Confidence")
|
|
|
|
prob_df = pd.DataFrame({
|
|
'Outcome': ['Dropout', 'Enrolled', 'Graduate'],
|
|
'Probability': prediction_proba
|
|
})
|
|
|
|
# Create probability chart
|
|
fig = px.bar(
|
|
prob_df,
|
|
x='Outcome',
|
|
y='Probability',
|
|
color='Outcome',
|
|
color_discrete_map={
|
|
'Graduate': '#28a745',
|
|
'Dropout': '#dc3545',
|
|
'Enrolled': '#ffc107'
|
|
},
|
|
title="Prediction Probabilities"
|
|
)
|
|
fig.update_layout(showlegend=False)
|
|
st.plotly_chart(fig, use_container_width=True)
|
|
|
|
# Display detailed probabilities
|
|
col_a, col_b, col_c = st.columns(3)
|
|
|
|
with col_a:
|
|
st.markdown(f"""
|
|
<div class="metric-card">
|
|
<h4>Graduate</h4>
|
|
<h2>{prediction_proba[2]:.1%}</h2>
|
|
</div>
|
|
""", unsafe_allow_html=True)
|
|
|
|
with col_b:
|
|
st.markdown(f"""
|
|
<div class="metric-card">
|
|
<h4>Dropout</h4>
|
|
<h2>{prediction_proba[0]:.1%}</h2>
|
|
</div>
|
|
""", unsafe_allow_html=True)
|
|
|
|
with col_c:
|
|
st.markdown(f"""
|
|
<div class="metric-card">
|
|
<h4>Enrolled</h4>
|
|
<h2>{prediction_proba[1]:.1%}</h2>
|
|
</div>
|
|
""", unsafe_allow_html=True)
|
|
|
|
with col2:
|
|
st.markdown('<h2 class="sub-header">About the Model</h2>', unsafe_allow_html=True)
|
|
|
|
st.info("""
|
|
**Model Details:**
|
|
- **Algorithm:** Random Forest Classifier
|
|
- **Features:** 36 student characteristics
|
|
- **Training Accuracy:** 100%
|
|
- **Test Accuracy:** 93.84%
|
|
- **Classes:** Graduate, Dropout, Enrolled
|
|
""")
|
|
|
|
st.markdown("### Key Factors")
|
|
st.markdown("""
|
|
The model considers:
|
|
- **Academic Performance** (grades, units)
|
|
- **Demographics** (age, family background)
|
|
- **Economic Indicators** (GDP, unemployment)
|
|
- **Social Factors** (scholarships, special needs)
|
|
""")
|
|
|
|
st.markdown("### Use Cases")
|
|
st.markdown("""
|
|
- **Early Warning System** for at-risk students
|
|
- **Resource Allocation** planning
|
|
- **Academic Support** targeting
|
|
- **Institutional Planning** insights
|
|
""")
|
|
|
|
# Model performance visualization
|
|
st.markdown("### Model Performance")
|
|
|
|
# Create a simple performance chart
|
|
metrics = ['Accuracy', 'Precision', 'Recall', 'F1-Score']
|
|
values = [93.84, 92.96, 94.02, 93.15] # From the notebook results
|
|
|
|
fig_perf = go.Figure(data=go.Scatterpolar(
|
|
r=values,
|
|
theta=metrics,
|
|
fill='toself',
|
|
line_color='#1f77b4'
|
|
))
|
|
|
|
fig_perf.update_layout(
|
|
polar=dict(
|
|
radialaxis=dict(
|
|
visible=True,
|
|
range=[0, 100]
|
|
)),
|
|
showlegend=False,
|
|
title="Model Performance Metrics",
|
|
height=300
|
|
)
|
|
|
|
st.plotly_chart(fig_perf, use_container_width=True)
|
|
|
|
# Footer
|
|
st.markdown("---")
|
|
st.markdown("*Developed using Streamlit and scikit-learn*")
|
|
|
|
if __name__ == "__main__":
|
|
main() |