mirror of
https://github.com/andwati/predictive_analysis.git
synced 2026-10-18 08:42:52 +00:00
deploy to streamlit
This commit is contained in:
+437
-59
@@ -10,6 +10,7 @@ import pickle
|
|||||||
import plotly.express as px
|
import plotly.express as px
|
||||||
import plotly.graph_objects as go
|
import plotly.graph_objects as go
|
||||||
from plotly.subplots import make_subplots
|
from plotly.subplots import make_subplots
|
||||||
|
import time
|
||||||
|
|
||||||
# Configure Streamlit page
|
# Configure Streamlit page
|
||||||
st.set_page_config(
|
st.set_page_config(
|
||||||
@@ -19,46 +20,279 @@ st.set_page_config(
|
|||||||
initial_sidebar_state="expanded"
|
initial_sidebar_state="expanded"
|
||||||
)
|
)
|
||||||
|
|
||||||
# Custom CSS for beautiful styling
|
# Rose Pine Dark Theme CSS
|
||||||
st.markdown("""
|
st.markdown("""
|
||||||
<style>
|
<style>
|
||||||
|
/* Import Google Fonts */
|
||||||
|
@import url('https://fonts.googleapis.com/css2?family=Inter:wght@300;400;500;600;700&display=swap');
|
||||||
|
|
||||||
|
/* Rose Pine Color Palette */
|
||||||
|
:root {
|
||||||
|
--rp-base: #191724;
|
||||||
|
--rp-surface: #1f1d2e;
|
||||||
|
--rp-overlay: #26233a;
|
||||||
|
--rp-muted: #6e6a86;
|
||||||
|
--rp-subtle: #908caa;
|
||||||
|
--rp-text: #e0def4;
|
||||||
|
--rp-love: #eb6f92;
|
||||||
|
--rp-gold: #f6c177;
|
||||||
|
--rp-rose: #ebbcba;
|
||||||
|
--rp-pine: #31748f;
|
||||||
|
--rp-foam: #9ccfd8;
|
||||||
|
--rp-iris: #c4a7e7;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Global Styles */
|
||||||
|
.stApp {
|
||||||
|
font-family: 'Inter', sans-serif;
|
||||||
|
background-color: var(--rp-base);
|
||||||
|
color: var(--rp-text);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Override Streamlit's default colors */
|
||||||
|
.main .block-container {
|
||||||
|
background-color: var(--rp-base);
|
||||||
|
color: var(--rp-text);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Sidebar styling */
|
||||||
|
.css-1d391kg {
|
||||||
|
background-color: var(--rp-surface);
|
||||||
|
}
|
||||||
|
|
||||||
|
.css-1lcbmhc {
|
||||||
|
background-color: var(--rp-surface);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Header Styles */
|
||||||
.main-header {
|
.main-header {
|
||||||
font-size: 3rem;
|
font-size: 3.5rem;
|
||||||
color: #1f77b4;
|
background: linear-gradient(135deg, var(--rp-iris), var(--rp-foam));
|
||||||
|
-webkit-background-clip: text;
|
||||||
|
-webkit-text-fill-color: transparent;
|
||||||
|
background-clip: text;
|
||||||
text-align: center;
|
text-align: center;
|
||||||
margin-bottom: 2rem;
|
|
||||||
font-weight: bold;
|
|
||||||
}
|
|
||||||
.sub-header {
|
|
||||||
font-size: 1.5rem;
|
|
||||||
color: #2c3e50;
|
|
||||||
margin-bottom: 1rem;
|
margin-bottom: 1rem;
|
||||||
|
font-weight: 700;
|
||||||
|
letter-spacing: -1px;
|
||||||
}
|
}
|
||||||
.prediction-box {
|
|
||||||
padding: 2rem;
|
.subtitle {
|
||||||
border-radius: 10px;
|
|
||||||
text-align: center;
|
text-align: center;
|
||||||
font-size: 1.5rem;
|
font-size: 1.2rem;
|
||||||
font-weight: bold;
|
color: var(--rp-subtle);
|
||||||
margin: 1rem 0;
|
margin-bottom: 3rem;
|
||||||
|
font-weight: 400;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.sub-header {
|
||||||
|
font-size: 1.4rem;
|
||||||
|
color: var(--rp-text);
|
||||||
|
margin-bottom: 1.5rem;
|
||||||
|
font-weight: 600;
|
||||||
|
border-bottom: 2px solid var(--rp-overlay);
|
||||||
|
padding-bottom: 0.5rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Prediction Result Boxes */
|
||||||
|
.prediction-box {
|
||||||
|
padding: 2.5rem 2rem;
|
||||||
|
border-radius: 16px;
|
||||||
|
text-align: center;
|
||||||
|
font-size: 1.8rem;
|
||||||
|
font-weight: 700;
|
||||||
|
margin: 2rem 0;
|
||||||
|
box-shadow: 0 10px 30px rgba(0,0,0,0.3);
|
||||||
|
transform: translateY(0);
|
||||||
|
transition: transform 0.3s ease, box-shadow 0.3s ease;
|
||||||
|
border: 2px solid;
|
||||||
|
}
|
||||||
|
|
||||||
|
.prediction-box:hover {
|
||||||
|
transform: translateY(-5px);
|
||||||
|
box-shadow: 0 20px 40px rgba(0,0,0,0.4);
|
||||||
|
}
|
||||||
|
|
||||||
.graduate-box {
|
.graduate-box {
|
||||||
background: linear-gradient(135deg, #28a745, #20c997);
|
background: linear-gradient(135deg, var(--rp-foam), var(--rp-pine));
|
||||||
color: white;
|
color: var(--rp-base);
|
||||||
|
border-color: var(--rp-foam);
|
||||||
}
|
}
|
||||||
|
|
||||||
.dropout-box {
|
.dropout-box {
|
||||||
background: linear-gradient(135deg, #dc3545, #fd7e14);
|
background: linear-gradient(135deg, var(--rp-love), #c74665);
|
||||||
color: white;
|
color: var(--rp-text);
|
||||||
|
border-color: var(--rp-love);
|
||||||
}
|
}
|
||||||
|
|
||||||
.enrolled-box {
|
.enrolled-box {
|
||||||
background: linear-gradient(135deg, #ffc107, #fd7e14);
|
background: linear-gradient(135deg, var(--rp-gold), #e6a85c);
|
||||||
color: white;
|
color: var(--rp-base);
|
||||||
|
border-color: var(--rp-gold);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Metric Cards */
|
||||||
.metric-card {
|
.metric-card {
|
||||||
background: #f8f9fa;
|
background: linear-gradient(135deg, var(--rp-surface), var(--rp-overlay));
|
||||||
|
padding: 1.5rem;
|
||||||
|
border-radius: 12px;
|
||||||
|
border-left: 4px solid var(--rp-iris);
|
||||||
|
box-shadow: 0 4px 12px rgba(0,0,0,0.2);
|
||||||
|
transition: transform 0.2s ease;
|
||||||
|
}
|
||||||
|
|
||||||
|
.metric-card:hover {
|
||||||
|
transform: translateY(-2px);
|
||||||
|
box-shadow: 0 8px 20px rgba(0,0,0,0.3);
|
||||||
|
}
|
||||||
|
|
||||||
|
.metric-card h4 {
|
||||||
|
color: var(--rp-subtle);
|
||||||
|
font-weight: 600;
|
||||||
|
margin-bottom: 0.5rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.metric-card h2 {
|
||||||
|
color: var(--rp-iris);
|
||||||
|
font-weight: 700;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Sidebar Styling */
|
||||||
|
.sidebar-section {
|
||||||
|
background: var(--rp-overlay);
|
||||||
padding: 1rem;
|
padding: 1rem;
|
||||||
border-radius: 8px;
|
border-radius: 8px;
|
||||||
border-left: 4px solid #1f77b4;
|
margin-bottom: 1rem;
|
||||||
|
border-left: 3px solid var(--rp-iris);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Info boxes */
|
||||||
|
.info-box {
|
||||||
|
background: linear-gradient(135deg, var(--rp-overlay), var(--rp-surface));
|
||||||
|
padding: 1.5rem;
|
||||||
|
border-radius: 12px;
|
||||||
|
border-left: 4px solid var(--rp-foam);
|
||||||
|
margin: 1rem 0;
|
||||||
|
color: var(--rp-text);
|
||||||
|
}
|
||||||
|
|
||||||
|
.warning-box {
|
||||||
|
background: linear-gradient(135deg, var(--rp-overlay), var(--rp-surface));
|
||||||
|
padding: 1.5rem;
|
||||||
|
border-radius: 12px;
|
||||||
|
border-left: 4px solid var(--rp-gold);
|
||||||
|
margin: 1rem 0;
|
||||||
|
color: var(--rp-text);
|
||||||
|
}
|
||||||
|
|
||||||
|
.success-box {
|
||||||
|
background: linear-gradient(135deg, var(--rp-overlay), var(--rp-surface));
|
||||||
|
padding: 1.5rem;
|
||||||
|
border-radius: 12px;
|
||||||
|
border-left: 4px solid var(--rp-pine);
|
||||||
|
margin: 1rem 0;
|
||||||
|
color: var(--rp-text);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Button Styling */
|
||||||
|
.stButton > button {
|
||||||
|
background: linear-gradient(135deg, var(--rp-iris), var(--rp-love));
|
||||||
|
color: var(--rp-text);
|
||||||
|
border: none;
|
||||||
|
border-radius: 25px;
|
||||||
|
font-weight: 600;
|
||||||
|
font-size: 1.1rem;
|
||||||
|
padding: 0.75rem 2rem;
|
||||||
|
transition: transform 0.2s ease, box-shadow 0.2s ease;
|
||||||
|
}
|
||||||
|
|
||||||
|
.stButton > button:hover {
|
||||||
|
transform: translateY(-2px);
|
||||||
|
box-shadow: 0 8px 20px rgba(196, 167, 231, 0.4);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Streamlit widget overrides */
|
||||||
|
.stSelectbox > div > div {
|
||||||
|
background-color: var(--rp-surface);
|
||||||
|
color: var(--rp-text);
|
||||||
|
border-color: var(--rp-overlay);
|
||||||
|
}
|
||||||
|
|
||||||
|
.stNumberInput > div > div > input {
|
||||||
|
background-color: var(--rp-surface);
|
||||||
|
color: var(--rp-text);
|
||||||
|
border-color: var(--rp-overlay);
|
||||||
|
}
|
||||||
|
|
||||||
|
.stRadio > div {
|
||||||
|
background-color: var(--rp-surface);
|
||||||
|
border-radius: 8px;
|
||||||
|
padding: 0.5rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Progress indicator */
|
||||||
|
.progress-container {
|
||||||
|
background: var(--rp-overlay);
|
||||||
|
border-radius: 10px;
|
||||||
|
height: 8px;
|
||||||
|
margin: 1rem 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.progress-bar {
|
||||||
|
background: linear-gradient(90deg, var(--rp-iris), var(--rp-foam));
|
||||||
|
height: 100%;
|
||||||
|
border-radius: 10px;
|
||||||
|
transition: width 0.3s ease;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Section dividers */
|
||||||
|
.section-divider {
|
||||||
|
height: 2px;
|
||||||
|
background: linear-gradient(90deg, transparent, var(--rp-iris), transparent);
|
||||||
|
margin: 2rem 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Text color overrides */
|
||||||
|
.markdown-text-container {
|
||||||
|
color: var(--rp-text);
|
||||||
|
}
|
||||||
|
|
||||||
|
h1, h2, h3, h4, h5, h6 {
|
||||||
|
color: var(--rp-text);
|
||||||
|
}
|
||||||
|
|
||||||
|
.stMarkdown {
|
||||||
|
color: var(--rp-text);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Animations */
|
||||||
|
@keyframes fadeInUp {
|
||||||
|
from {
|
||||||
|
opacity: 0;
|
||||||
|
transform: translateY(30px);
|
||||||
|
}
|
||||||
|
to {
|
||||||
|
opacity: 1;
|
||||||
|
transform: translateY(0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.fade-in {
|
||||||
|
animation: fadeInUp 0.6s ease forwards;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Mobile responsiveness */
|
||||||
|
@media (max-width: 768px) {
|
||||||
|
.main-header {
|
||||||
|
font-size: 2.5rem !important;
|
||||||
|
}
|
||||||
|
.prediction-box {
|
||||||
|
font-size: 1.4rem !important;
|
||||||
|
padding: 1.5rem 1rem !important;
|
||||||
|
}
|
||||||
|
.metric-card {
|
||||||
|
padding: 1rem !important;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
""", unsafe_allow_html=True)
|
""", unsafe_allow_html=True)
|
||||||
@@ -252,7 +486,10 @@ def main():
|
|||||||
|
|
||||||
# Header
|
# Header
|
||||||
st.markdown('<h1 class="main-header">Student Success Predictor</h1>', unsafe_allow_html=True)
|
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.**")
|
st.markdown('<p class="subtitle">Predict whether a student will Graduate, Dropout, or remain Enrolled based on academic and demographic factors using advanced machine learning.</p>', unsafe_allow_html=True)
|
||||||
|
|
||||||
|
# Progress indicator (shows completion percentage)
|
||||||
|
progress_placeholder = st.empty()
|
||||||
|
|
||||||
# Sidebar for input
|
# Sidebar for input
|
||||||
st.sidebar.markdown('<h2 class="sub-header">Student Information</h2>', unsafe_allow_html=True)
|
st.sidebar.markdown('<h2 class="sub-header">Student Information</h2>', unsafe_allow_html=True)
|
||||||
@@ -260,10 +497,12 @@ def main():
|
|||||||
# Create input form
|
# Create input form
|
||||||
with st.sidebar:
|
with st.sidebar:
|
||||||
# Quick presets
|
# Quick presets
|
||||||
st.markdown("### Quick Start")
|
st.markdown('<div class="sidebar-section">', unsafe_allow_html=True)
|
||||||
|
st.markdown("### 🚀 Quick Start")
|
||||||
preset = st.selectbox("Load Sample Data",
|
preset = st.selectbox("Load Sample Data",
|
||||||
["Custom Entry", "Typical Graduate Student", "At-Risk Student", "International Student"],
|
["Custom Entry", "Typical Graduate Student", "At-Risk Student", "International Student"],
|
||||||
help="Choose a preset to quickly fill common scenarios")
|
help="Choose a preset to quickly fill common scenarios")
|
||||||
|
st.markdown('</div>', unsafe_allow_html=True)
|
||||||
|
|
||||||
# Set default values based on preset
|
# Set default values based on preset
|
||||||
defaults = {
|
defaults = {
|
||||||
@@ -274,10 +513,16 @@ def main():
|
|||||||
}
|
}
|
||||||
|
|
||||||
if preset != "Custom Entry":
|
if preset != "Custom Entry":
|
||||||
st.info(f"Loading {preset} preset values. You can modify any field below.")
|
st.markdown(f'<div class="success-box">✅ Loading <strong>{preset}</strong> preset values. You can modify any field below.</div>', unsafe_allow_html=True)
|
||||||
|
|
||||||
|
# Form progress tracking
|
||||||
|
form_sections = ["Personal Information", "Academic Background", "Family Background", "Financial & Social", "1st Semester Performance", "2nd Semester Performance", "Economic Indicators"]
|
||||||
|
current_section = 0
|
||||||
|
|
||||||
# Personal Information
|
# Personal Information
|
||||||
st.markdown("### Personal Information")
|
st.markdown('<div class="section-divider"></div>', unsafe_allow_html=True)
|
||||||
|
st.markdown("### 👤 Personal Information")
|
||||||
|
st.markdown('<div class="info-box">Basic demographic information about the student</div>', unsafe_allow_html=True)
|
||||||
|
|
||||||
marital_status = st.selectbox(
|
marital_status = st.selectbox(
|
||||||
"Marital Status",
|
"Marital Status",
|
||||||
@@ -291,7 +536,9 @@ def main():
|
|||||||
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)
|
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
|
# Academic Background
|
||||||
st.markdown("### Academic Background")
|
st.markdown('<div class="section-divider"></div>', unsafe_allow_html=True)
|
||||||
|
st.markdown("### 🎓 Academic Background")
|
||||||
|
st.markdown('<div class="info-box">Information about the student\'s application and academic history</div>', unsafe_allow_html=True)
|
||||||
|
|
||||||
application_mode = st.selectbox(
|
application_mode = st.selectbox(
|
||||||
"Application Mode",
|
"Application Mode",
|
||||||
@@ -324,7 +571,9 @@ def main():
|
|||||||
admission_grade = st.number_input("Admission Grade", min_value=95, max_value=190, value=defaults[preset]["adm_grade"], step=1)
|
admission_grade = st.number_input("Admission Grade", min_value=95, max_value=190, value=defaults[preset]["adm_grade"], step=1)
|
||||||
|
|
||||||
# Family Background
|
# Family Background
|
||||||
st.markdown("### Family Background")
|
st.markdown('<div class="section-divider"></div>', unsafe_allow_html=True)
|
||||||
|
st.markdown("### 👨👩👧👦 Family Background")
|
||||||
|
st.markdown('<div class="info-box">Parent education and occupation information</div>', unsafe_allow_html=True)
|
||||||
|
|
||||||
mothers_qualification = st.selectbox(
|
mothers_qualification = st.selectbox(
|
||||||
"Mother's Qualification",
|
"Mother's Qualification",
|
||||||
@@ -351,7 +600,9 @@ def main():
|
|||||||
)
|
)
|
||||||
|
|
||||||
# Financial and Social Factors
|
# Financial and Social Factors
|
||||||
st.markdown("### Financial & Social")
|
st.markdown('<div class="section-divider"></div>', unsafe_allow_html=True)
|
||||||
|
st.markdown("### 💰 Financial & Social")
|
||||||
|
st.markdown('<div class="info-box">Student financial status and special circumstances</div>', unsafe_allow_html=True)
|
||||||
|
|
||||||
col1, col2 = st.columns(2)
|
col1, col2 = st.columns(2)
|
||||||
with col1:
|
with col1:
|
||||||
@@ -364,8 +615,9 @@ def main():
|
|||||||
international = st.radio("International Student", [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
|
# Academic Performance - 1st Semester
|
||||||
st.markdown("### 1st Semester Performance")
|
st.markdown('<div class="section-divider"></div>', unsafe_allow_html=True)
|
||||||
st.caption("Enter the student's academic performance for their first semester")
|
st.markdown("### 📚 1st Semester Performance")
|
||||||
|
st.markdown('<div class="warning-box">⚠️ <strong>Important:</strong> Academic performance is a key predictor of student success</div>', unsafe_allow_html=True)
|
||||||
|
|
||||||
# Set default academic values based on preset
|
# Set default academic values based on preset
|
||||||
first_sem_defaults = {
|
first_sem_defaults = {
|
||||||
@@ -386,8 +638,9 @@ def main():
|
|||||||
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")
|
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
|
# Academic Performance - 2nd Semester
|
||||||
st.markdown("### 2nd Semester Performance")
|
st.markdown('<div class="section-divider"></div>', unsafe_allow_html=True)
|
||||||
st.caption("Enter the student's academic performance for their second semester")
|
st.markdown("### 📖 2nd Semester Performance")
|
||||||
|
st.markdown('<div class="info-box">Second semester data helps improve prediction accuracy</div>', unsafe_allow_html=True)
|
||||||
|
|
||||||
# Set default academic values for 2nd semester based on preset
|
# Set default academic values for 2nd semester based on preset
|
||||||
second_sem_defaults = {
|
second_sem_defaults = {
|
||||||
@@ -408,8 +661,9 @@ def main():
|
|||||||
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")
|
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
|
# Economic Indicators
|
||||||
st.markdown("### Economic Indicators")
|
st.markdown('<div class="section-divider"></div>', unsafe_allow_html=True)
|
||||||
st.caption("These are typically set by the institution based on current economic conditions")
|
st.markdown("### 📊 Economic Indicators")
|
||||||
|
st.markdown('<div class="info-box">📈 Economic context data - typically set by institution based on current conditions</div>', unsafe_allow_html=True)
|
||||||
|
|
||||||
col1, col2, col3 = st.columns(3)
|
col1, col2, col3 = st.columns(3)
|
||||||
with col1:
|
with col1:
|
||||||
@@ -465,30 +719,93 @@ def main():
|
|||||||
with col1:
|
with col1:
|
||||||
st.markdown('<h2 class="sub-header">Prediction Results</h2>', unsafe_allow_html=True)
|
st.markdown('<h2 class="sub-header">Prediction Results</h2>', unsafe_allow_html=True)
|
||||||
|
|
||||||
# Make prediction
|
# Prediction button with enhanced styling
|
||||||
if st.button("Predict Student Outcome", type="primary"):
|
predict_button = st.button("🔬 Analyze Student Data", type="primary", use_container_width=True)
|
||||||
with st.spinner("Analyzing student data..."):
|
|
||||||
# Preprocess input
|
if predict_button:
|
||||||
processed_input = preprocess_input(user_input, preprocessing_info)
|
# Enhanced loading animation
|
||||||
|
progress_bar = st.progress(0)
|
||||||
|
status_text = st.empty()
|
||||||
|
|
||||||
|
# Simulate processing steps
|
||||||
|
steps = [
|
||||||
|
("Loading student data...", 20),
|
||||||
|
("Preprocessing features...", 40),
|
||||||
|
("Running machine learning model...", 70),
|
||||||
|
("Calculating prediction confidence...", 90),
|
||||||
|
("Finalizing results...", 100)
|
||||||
|
]
|
||||||
|
|
||||||
|
for step, progress in steps:
|
||||||
|
status_text.text(step)
|
||||||
|
progress_bar.progress(progress)
|
||||||
|
time.sleep(0.3)
|
||||||
|
|
||||||
|
# Clear progress indicators
|
||||||
|
progress_bar.empty()
|
||||||
|
status_text.empty()
|
||||||
|
|
||||||
|
# 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]
|
||||||
|
|
||||||
|
# Store in session state for export
|
||||||
|
st.session_state['last_prediction'] = {
|
||||||
|
'outcome': predicted_outcome,
|
||||||
|
'probabilities': prediction_proba,
|
||||||
|
'timestamp': time.time()
|
||||||
|
}
|
||||||
|
|
||||||
|
# Enhanced prediction display with confidence indicators
|
||||||
|
confidence_level = max(prediction_proba)
|
||||||
|
confidence_text = "High" if confidence_level > 0.7 else "Medium" if confidence_level > 0.5 else "Low"
|
||||||
|
|
||||||
|
# Display prediction with enhanced styling and confidence
|
||||||
|
if predicted_outcome == "Graduate":
|
||||||
|
st.markdown(f'''<div class="prediction-box graduate-box fade-in">
|
||||||
|
🎓 GRADUATE
|
||||||
|
<br><small style="font-size: 1rem; opacity: 0.9;">Confidence: {confidence_level:.1%} ({confidence_text})</small>
|
||||||
|
</div>''', unsafe_allow_html=True)
|
||||||
|
st.balloons()
|
||||||
|
elif predicted_outcome == "Dropout":
|
||||||
|
st.markdown(f'''<div class="prediction-box dropout-box fade-in">
|
||||||
|
⚠️ AT RISK OF DROPOUT
|
||||||
|
<br><small style="font-size: 1rem; opacity: 0.9;">Confidence: {confidence_level:.1%} ({confidence_text})</small>
|
||||||
|
</div>''', unsafe_allow_html=True)
|
||||||
|
else:
|
||||||
|
st.markdown(f'''<div class="prediction-box enrolled-box fade-in">
|
||||||
|
📚 LIKELY TO STAY ENROLLED
|
||||||
|
<br><small style="font-size: 1rem; opacity: 0.9;">Confidence: {confidence_level:.1%} ({confidence_text})</small>
|
||||||
|
</div>''', unsafe_allow_html=True)
|
||||||
|
|
||||||
# Make prediction
|
# Add personalized recommendations based on prediction
|
||||||
prediction = model.predict(processed_input)[0]
|
st.markdown("### 💡 Personalized Recommendations")
|
||||||
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":
|
if predicted_outcome == "Graduate":
|
||||||
st.markdown(f'<div class="prediction-box graduate-box">Predicted Outcome: GRADUATE</div>', unsafe_allow_html=True)
|
st.markdown('<div class="success-box">🎯 <strong>Success Indicators:</strong> This student shows strong potential for graduation. Continue supporting their academic journey.</div>', unsafe_allow_html=True)
|
||||||
elif predicted_outcome == "Dropout":
|
elif predicted_outcome == "Dropout":
|
||||||
st.markdown(f'<div class="prediction-box dropout-box">Predicted Outcome: DROPOUT</div>', unsafe_allow_html=True)
|
recommendations = []
|
||||||
|
if user_input['Curricular units 1st sem (approved)'] == 0:
|
||||||
|
recommendations.append("📚 **Academic Support:** Provide tutoring for struggling courses")
|
||||||
|
if user_input['Scholarship holder'] == 0:
|
||||||
|
recommendations.append("💰 **Financial Aid:** Consider scholarship opportunities")
|
||||||
|
if user_input['Age at enrollment'] > 25:
|
||||||
|
recommendations.append("🕐 **Flexible Scheduling:** Offer evening or part-time options")
|
||||||
|
|
||||||
|
if recommendations:
|
||||||
|
st.markdown('<div class="warning-box">🎯 <strong>Intervention Strategies:</strong><br>' + '<br>'.join(recommendations) + '</div>', unsafe_allow_html=True)
|
||||||
else:
|
else:
|
||||||
st.markdown(f'<div class="prediction-box enrolled-box">Predicted Outcome: ENROLLED</div>', unsafe_allow_html=True)
|
st.markdown('<div class="info-box">📈 <strong>Monitoring Needed:</strong> This student may benefit from regular check-ins and academic support.</div>', unsafe_allow_html=True)
|
||||||
|
|
||||||
# Display probabilities
|
# Display probabilities
|
||||||
st.markdown("### Prediction Confidence")
|
st.markdown("### 📊 Prediction Confidence")
|
||||||
|
|
||||||
prob_df = pd.DataFrame({
|
prob_df = pd.DataFrame({
|
||||||
'Outcome': ['Dropout', 'Enrolled', 'Graduate'],
|
'Outcome': ['Dropout', 'Enrolled', 'Graduate'],
|
||||||
@@ -539,16 +856,40 @@ def main():
|
|||||||
""", unsafe_allow_html=True)
|
""", unsafe_allow_html=True)
|
||||||
|
|
||||||
with col2:
|
with col2:
|
||||||
st.markdown('<h2 class="sub-header">About the Model</h2>', unsafe_allow_html=True)
|
st.markdown('<h2 class="sub-header">📊 Model Insights</h2>', unsafe_allow_html=True)
|
||||||
|
|
||||||
st.info("""
|
# Dynamic model information based on current inputs
|
||||||
**Model Details:**
|
st.markdown('<div class="info-box">', unsafe_allow_html=True)
|
||||||
|
st.markdown("""
|
||||||
|
**🤖 Model Details:**
|
||||||
- **Algorithm:** Random Forest Classifier
|
- **Algorithm:** Random Forest Classifier
|
||||||
- **Features:** 36 student characteristics
|
- **Features:** 36 student characteristics
|
||||||
- **Training Accuracy:** 100%
|
- **Training Accuracy:** 100%
|
||||||
- **Test Accuracy:** 93.84%
|
- **Test Accuracy:** 93.84%
|
||||||
- **Classes:** Graduate, Dropout, Enrolled
|
- **Classes:** Graduate, Dropout, Enrolled
|
||||||
""")
|
""")
|
||||||
|
st.markdown('</div>', unsafe_allow_html=True)
|
||||||
|
|
||||||
|
# Add input validation feedback
|
||||||
|
st.markdown("### 🔍 Input Validation")
|
||||||
|
validation_container = st.container()
|
||||||
|
|
||||||
|
# Basic validation checks
|
||||||
|
validation_issues = []
|
||||||
|
|
||||||
|
# Check for unrealistic combinations
|
||||||
|
if user_input['Age at enrollment'] < 17:
|
||||||
|
validation_issues.append("⚠️ Age seems too low for enrollment")
|
||||||
|
if user_input['Curricular units 1st sem (approved)'] > user_input['Curricular units 1st sem (enrolled)']:
|
||||||
|
validation_issues.append("⚠️ Approved units exceed enrolled units (1st sem)")
|
||||||
|
if user_input['Curricular units 2nd sem (approved)'] > user_input['Curricular units 2nd sem (enrolled)']:
|
||||||
|
validation_issues.append("⚠️ Approved units exceed enrolled units (2nd sem)")
|
||||||
|
|
||||||
|
if validation_issues:
|
||||||
|
for issue in validation_issues:
|
||||||
|
st.warning(issue)
|
||||||
|
else:
|
||||||
|
st.success("✅ All inputs look valid!")
|
||||||
|
|
||||||
st.markdown("### Key Factors")
|
st.markdown("### Key Factors")
|
||||||
st.markdown("""
|
st.markdown("""
|
||||||
@@ -593,10 +934,47 @@ def main():
|
|||||||
)
|
)
|
||||||
|
|
||||||
st.plotly_chart(fig_perf, use_container_width=True)
|
st.plotly_chart(fig_perf, use_container_width=True)
|
||||||
|
|
||||||
|
# Add export functionality
|
||||||
|
st.markdown("### 📥 Export Results")
|
||||||
|
|
||||||
|
if st.session_state.get('last_prediction'):
|
||||||
|
export_data = {
|
||||||
|
'timestamp': time.strftime('%Y-%m-%d %H:%M:%S'),
|
||||||
|
'prediction': st.session_state['last_prediction']['outcome'],
|
||||||
|
'confidence': f"{max(st.session_state['last_prediction']['probabilities']):.1%}",
|
||||||
|
'student_data': user_input
|
||||||
|
}
|
||||||
|
|
||||||
|
# Convert to JSON for download
|
||||||
|
import json
|
||||||
|
json_str = json.dumps(export_data, indent=2, ensure_ascii=False)
|
||||||
|
|
||||||
|
st.download_button(
|
||||||
|
label="📁 Download Prediction Report",
|
||||||
|
data=json_str,
|
||||||
|
file_name=f"student_prediction_{time.strftime('%Y%m%d_%H%M%S')}.json",
|
||||||
|
mime="application/json"
|
||||||
|
)
|
||||||
|
|
||||||
|
# Enhanced Footer with additional info
|
||||||
|
st.markdown('<div class="section-divider"></div>', unsafe_allow_html=True)
|
||||||
|
col1, col2, col3 = st.columns(3)
|
||||||
|
|
||||||
|
with col1:
|
||||||
|
st.markdown("**📊 Data Source**")
|
||||||
|
st.caption("UCI ML Repository - Student Academic Success")
|
||||||
|
|
||||||
|
with col2:
|
||||||
|
st.markdown("**🔬 Model Info**")
|
||||||
|
st.caption("Random Forest with 93.84% accuracy")
|
||||||
|
|
||||||
|
with col3:
|
||||||
|
st.markdown("**🛠️ Built With**")
|
||||||
|
st.caption("Streamlit • scikit-learn • Plotly")
|
||||||
|
|
||||||
# Footer
|
|
||||||
st.markdown("---")
|
st.markdown("---")
|
||||||
st.markdown("*Developed using Streamlit and scikit-learn*")
|
st.markdown('<p style="text-align: center; color: #6c757d; font-size: 0.9rem;">🎓 Empowering educational institutions with data-driven insights for student success</p>', unsafe_allow_html=True)
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
main()
|
main()
|
||||||
Reference in New Issue
Block a user