Refactor Streamlit app for improved UI and user experience; updated titles, added quick presets, and enhanced input forms.

This commit is contained in:
2025-09-28 00:00:02 +03:00
parent ed2ff5b359
commit 4a690d5f72
+111 -56
View File
@@ -13,8 +13,8 @@ from plotly.subplots import make_subplots
# Configure Streamlit page
st.set_page_config(
page_title="🎓 Student Success Predictor",
page_icon="🎓",
page_title="Student Success Predictor",
page_icon="📊",
layout="wide",
initial_sidebar_state="expanded"
)
@@ -251,42 +251,62 @@ def main():
feature_maps = create_feature_mappings()
# 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.**")
# 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)
# 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")
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]
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")
age_at_enrollment = st.slider("Age at Enrollment", 17, 70, 20)
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])
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")
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}")
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.slider("Application Order", 0, 9, 1)
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}")
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")
@@ -297,11 +317,14 @@ def main():
format_func=lambda x: feature_maps['qualification'].get(x, f"Qualification {x}")
)
previous_qualification_grade = st.slider("Previous Qualification Grade", 95, 190, 120)
admission_grade = st.slider("Admission Grade", 95, 190, 120)
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")
st.markdown("### Family Background")
mothers_qualification = st.selectbox(
"Mother's Qualification",
@@ -328,41 +351,73 @@ def main():
)
# Financial and Social Factors
st.markdown("### 💰 Financial & Social")
st.markdown("### Financial & Social")
displaced = st.selectbox("Displaced", [0, 1], format_func=lambda x: "No" if x == 0 else "Yes")
educational_special_needs = st.selectbox("Educational Special Needs", [0, 1], format_func=lambda x: "No" if x == 0 else "Yes")
debtor = st.selectbox("Debtor", [0, 1], format_func=lambda x: "No" if x == 0 else "Yes")
tuition_fees_up_to_date = st.selectbox("Tuition Fees Up to Date", [0, 1], format_func=lambda x: "No" if x == 0 else "Yes")
scholarship_holder = st.selectbox("Scholarship Holder", [0, 1], format_func=lambda x: "No" if x == 0 else "Yes")
international = st.selectbox("International Student", [0, 1], format_func=lambda x: "No" if x == 0 else "Yes")
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.markdown("### 1st Semester Performance")
st.caption("Enter the student's academic performance for their first semester")
curricular_units_1st_sem_credited = st.slider("Curricular Units 1st Sem (Credited)", 0, 26, 0)
curricular_units_1st_sem_enrolled = st.slider("Curricular Units 1st Sem (Enrolled)", 0, 26, 6)
curricular_units_1st_sem_evaluations = st.slider("Curricular Units 1st Sem (Evaluations)", 0, 45, 6)
curricular_units_1st_sem_approved = st.slider("Curricular Units 1st Sem (Approved)", 0, 26, 6)
curricular_units_1st_sem_grade = st.slider("Curricular Units 1st Sem (Grade)", 0, 20, 13)
curricular_units_1st_sem_without_evaluations = st.slider("Curricular Units 1st Sem (Without Evaluations)", 0, 14, 0)
# 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.markdown("### 2nd Semester Performance")
st.caption("Enter the student's academic performance for their second semester")
curricular_units_2nd_sem_credited = st.slider("Curricular Units 2nd Sem (Credited)", 0, 20, 0)
curricular_units_2nd_sem_enrolled = st.slider("Curricular Units 2nd Sem (Enrolled)", 0, 23, 6)
curricular_units_2nd_sem_evaluations = st.slider("Curricular Units 2nd Sem (Evaluations)", 0, 33, 6)
curricular_units_2nd_sem_approved = st.slider("Curricular Units 2nd Sem (Approved)", 0, 20, 6)
curricular_units_2nd_sem_grade = st.slider("Curricular Units 2nd Sem (Grade)", 0, 20, 13)
curricular_units_2nd_sem_without_evaluations = st.slider("Curricular Units 2nd Sem (Without Evaluations)", 0, 12, 0)
# 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.markdown("### Economic Indicators")
st.caption("These are typically set by the institution based on current economic conditions")
unemployment_rate = st.slider("Unemployment Rate (%)", 7.0, 16.2, 10.8)
inflation_rate = st.slider("Inflation Rate (%)", -0.8, 3.7, 1.4)
gdp = st.slider("GDP", -4.06, 3.51, 1.74)
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 = {
@@ -408,10 +463,10 @@ def main():
col1, col2 = st.columns([2, 1])
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
if st.button("🚀 Predict Student Outcome", type="primary"):
if st.button("Predict Student Outcome", type="primary"):
with st.spinner("Analyzing student data..."):
# Preprocess input
processed_input = preprocess_input(user_input, preprocessing_info)
@@ -426,14 +481,14 @@ def main():
# 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)
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)
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)
st.markdown(f'<div class="prediction-box enrolled-box">Predicted Outcome: ENROLLED</div>', unsafe_allow_html=True)
# Display probabilities
st.markdown("### 📊 Prediction Confidence")
st.markdown("### Prediction Confidence")
prob_df = pd.DataFrame({
'Outcome': ['Dropout', 'Enrolled', 'Graduate'],
@@ -462,7 +517,7 @@ def main():
with col_a:
st.markdown(f"""
<div class="metric-card">
<h4>🎓 Graduate</h4>
<h4>Graduate</h4>
<h2>{prediction_proba[2]:.1%}</h2>
</div>
""", unsafe_allow_html=True)
@@ -470,7 +525,7 @@ def main():
with col_b:
st.markdown(f"""
<div class="metric-card">
<h4>⚠️ Dropout</h4>
<h4>Dropout</h4>
<h2>{prediction_proba[0]:.1%}</h2>
</div>
""", unsafe_allow_html=True)
@@ -478,16 +533,16 @@ def main():
with col_c:
st.markdown(f"""
<div class="metric-card">
<h4>📚 Enrolled</h4>
<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.markdown('<h2 class="sub-header">About the Model</h2>', unsafe_allow_html=True)
st.info("""
**🤖 Model Details:**
**Model Details:**
- **Algorithm:** Random Forest Classifier
- **Features:** 36 student characteristics
- **Training Accuracy:** 100%
@@ -495,7 +550,7 @@ def main():
- **Classes:** Graduate, Dropout, Enrolled
""")
st.markdown("### 📈 Key Factors")
st.markdown("### Key Factors")
st.markdown("""
The model considers:
- **Academic Performance** (grades, units)
@@ -504,7 +559,7 @@ def main():
- **Social Factors** (scholarships, special needs)
""")
st.markdown("### 🎯 Use Cases")
st.markdown("### Use Cases")
st.markdown("""
- **Early Warning System** for at-risk students
- **Resource Allocation** planning
@@ -513,7 +568,7 @@ def main():
""")
# Model performance visualization
st.markdown("### 📊 Model Performance")
st.markdown("### Model Performance")
# Create a simple performance chart
metrics = ['Accuracy', 'Precision', 'Recall', 'F1-Score']
@@ -541,7 +596,7 @@ def main():
# Footer
st.markdown("---")
st.markdown("*Developed with ❤️ using Streamlit and scikit-learn*")
st.markdown("*Developed using Streamlit and scikit-learn*")
if __name__ == "__main__":
main()