From 4a690d5f72c3feeb6c20e9fc40af23ad2ac04796 Mon Sep 17 00:00:00 2001 From: andwati Date: Sun, 28 Sep 2025 00:00:02 +0300 Subject: [PATCH] Refactor Streamlit app for improved UI and user experience; updated titles, added quick presets, and enhanced input forms. --- streamlit_app.py | 167 +++++++++++++++++++++++++++++++---------------- 1 file changed, 111 insertions(+), 56 deletions(-) diff --git a/streamlit_app.py b/streamlit_app.py index 5e0af2a..2478d4b 100644 --- a/streamlit_app.py +++ b/streamlit_app.py @@ -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('

๐ŸŽ“ Student Success Predictor

', unsafe_allow_html=True) + st.markdown('

Student Success Predictor

', 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('

๐Ÿ“ Student Information

', unsafe_allow_html=True) + st.sidebar.markdown('

Student Information

', 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('

๐Ÿ”ฎ Prediction Results

', unsafe_allow_html=True) + st.markdown('

Prediction Results

', 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'
๐ŸŽ“ Predicted Outcome: GRADUATE
', unsafe_allow_html=True) + st.markdown(f'
Predicted Outcome: GRADUATE
', unsafe_allow_html=True) elif predicted_outcome == "Dropout": - st.markdown(f'
โš ๏ธ Predicted Outcome: DROPOUT
', unsafe_allow_html=True) + st.markdown(f'
Predicted Outcome: DROPOUT
', unsafe_allow_html=True) else: - st.markdown(f'
๐Ÿ“š Predicted Outcome: ENROLLED
', unsafe_allow_html=True) + st.markdown(f'
Predicted Outcome: ENROLLED
', 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"""
-

๐ŸŽ“ Graduate

+

Graduate

{prediction_proba[2]:.1%}

""", unsafe_allow_html=True) @@ -470,7 +525,7 @@ def main(): with col_b: st.markdown(f"""
-

โš ๏ธ Dropout

+

Dropout

{prediction_proba[0]:.1%}

""", unsafe_allow_html=True) @@ -478,16 +533,16 @@ def main(): with col_c: st.markdown(f"""
-

๐Ÿ“š Enrolled

+

Enrolled

{prediction_proba[1]:.1%}

""", unsafe_allow_html=True) with col2: - st.markdown('

โ„น๏ธ About the Model

', unsafe_allow_html=True) + st.markdown('

About the Model

', 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() \ No newline at end of file