Oyedon/UI_Enrolment_Trend_Resource_Optimization_App
0
1import streamlit as st2import pandas as pd3import numpy as np4import matplotlib.pyplot as plt5import plotly.graph_objects as go6import plotly.express as px7import joblib8import pickle9import requests10from io import BytesIO11 12# --------------------------------------------------13# PAGE CONFIG14# --------------------------------------------------15st.set_page_config(16 page_title="UI Analytics Platform",17 page_icon="π",18 layout="wide"19)20 21# Custom CSS22st.markdown("""23<style>24 .main-header {25 font-size: 2.5rem;26 font-weight: bold;27 color: #1a5490;28 text-align: center;29 margin-bottom: 1rem;30 }31 .sub-header {32 font-size: 1.2rem;33 color: #666;34 text-align: center;35 margin-bottom: 2rem;36 }37 .metric-card {38 background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);39 padding: 1.5rem;40 border-radius: 10px;41 color: white;42 text-align: center;43 box-shadow: 0 4px 6px rgba(0,0,0,0.1);44 }45 .metric-card-secondary {46 background: linear-gradient(135deg, #f093fb 0%, #f5576c 100%);47 padding: 1.5rem;48 border-radius: 10px;49 color: white;50 text-align: center;51 box-shadow: 0 4px 6px rgba(0,0,0,0.1);52 }53 .feature-card {54 background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);55 padding: 2rem;56 border-radius: 10px;57 color: white;58 margin: 1rem 0;59 height: 100%;60 }61 .info-box {62 background-color: #e8f5e9;63 padding: 1rem;64 border-left: 4px solid #4caf50;65 border-radius: 5px;66 margin: 1rem 0;67 }68 .warning-box {69 background-color: #fff3cd;70 padding: 1rem;71 border-left: 4px solid #ffc107;72 border-radius: 5px;73 margin: 1rem 0;74 }75 .insight-card {76 background: white;77 padding: 1rem;78 border-radius: 8px;79 border: 1px solid #e0e0e0;80 margin: 0.5rem 0;81 }82 .hierarchy-card {83 background: linear-gradient(135deg, #667eea20 0%, #764ba220 100%);84 padding: 1rem;85 border-radius: 8px;86 border: 2px solid #667eea;87 margin: 1rem 0;88 }89</style>90""", unsafe_allow_html=True)91 92# --------------------------------------------------93# SESSION STATE INITIALIZATION94# --------------------------------------------------95if 'page' not in st.session_state:96 st.session_state.page = 'landing'97if 'eda_data' not in st.session_state:98 st.session_state.eda_data = None99 100# --------------------------------------------------101# HIERARCHICAL STRUCTURE DATA102# --------------------------------------------------103UNIVERSITY_STRUCTURE = {104 'AGRICULTURE': ['Agricultural Economics', 'Agricultural Extension & Rural Development', 'Agronomy', 'Animal Science', 'Crop Protection & Environmental Biology', 'Soil Resources Management'],105 'ARTS': ['Arabic & Islamic Studies', 'Classics', 'Communication & Language Arts', 'English', 'European Studies', 'History', 'Linguistics & African Languages', 'Music', 'Philosophy', 'Theatre Arts'],106 'BASIC MEDICAL SCIENCES': ['Anatomy', 'Biochemistry', 'Physiology'],107 'CLINICAL SCIENCES': ['Anaesthesia', 'Chemical Pathology', 'Community Medicine', 'Haematology', 'Medical Microbiology & Parasitology', 'Medicine', 'Morbid Anatomy', 'Obstetrics & Gynaecology', 'Ophthalmology', 'Otorhinolaryngology', 'Paediatrics', 'Psychiatry', 'Radiation Medicine', 'Surgery'],108 'DENTISTRY': ['Child Dental Health', 'Oral & Maxillofacial Surgery', 'Oral Pathology & Biology', 'Periodontology & Community Dentistry', 'Preventive Dentistry', 'Restorative Dentistry'],109 'EDUCATION': ['Adult Education', 'Arts & Social Sciences Education', 'Educational Management', 'Guidance & Counselling', 'Library, Archival & Information Studies', 'Science & Technology Education', 'Teacher Education'],110 'ENVIRONMENTAL DESIGN & MANAGEMENT': ['Architecture', 'Estate Management', 'Quantity Surveying', 'Urban & Regional Planning'],111 'LAW': ['Commercial & Industrial Law', 'International Law', 'Private & Property Law', 'Public Law'],112 'PHARMACY': ['Clinical Pharmacy & Pharmacy Administration', 'Pharmaceutical Chemistry', 'Pharmaceutics', 'Pharmacognosy', 'Pharmacology & Therapeutics'],113 'PUBLIC HEALTH': ['Epidemiology & Medical Statistics', 'Environmental Health Sciences', 'Health Policy & Management', 'Health Promotion & Education', 'Human Nutrition'],114 'RENEWABLE NATURAL RESOURCES': ['Aquaculture & Fisheries Management', 'Forest Resources Management', 'Social & Environmental Forestry', 'Wildlife & Ecotourism Management'],115 'SCIENCE': ['Botany', 'Chemistry', 'Computer Science', 'Geography', 'Geology', 'Mathematics', 'Microbiology', 'Physics', 'Statistics', 'Zoology'],116 'SOCIAL SCIENCES': ['Anthropology', 'Economics', 'Geography', 'Political Science', 'Psychology', 'Sociology'],117 'TECHNOLOGY': ['Agricultural & Environmental Engineering', 'Civil Engineering', 'Electrical & Electronics Engineering', 'Food Technology', 'Industrial & Production Engineering', 'Mechanical Engineering', 'Petroleum Engineering', 'Wood Products Engineering'],118 'VETERINARY MEDICINE': ['Veterinary Anatomy', 'Veterinary Medicine', 'Veterinary Microbiology & Parasitology', 'Veterinary Pathology', 'Veterinary Pharmacology & Toxicology', 'Veterinary Physiology & Biochemistry', 'Veterinary Public Health & Preventive Medicine', 'Veterinary Surgery & Reproduction']119}120 121# --------------------------------------------------122# LOAD MODELS123# --------------------------------------------------124@st.cache_resource125def load_models():126 urls = {127 "enroll_features": "https://huggingface.co/spaces/Oyedon/UI_Enrolment_Trend_Resource_Optimization_App/resolve/main/src/ui_enrollment_features.pkl",128 "enroll_model": "https://huggingface.co/spaces/Oyedon/UI_Enrolment_Trend_Resource_Optimization_App/resolve/main/src/ui_enrollment_prediction_model.pkl",129 "resource_model": "https://huggingface.co/spaces/Oyedon/UI_Enrolment_Trend_Resource_Optimization_App/resolve/main/src/ui_resource_allocation_model.pkl",130 "resource_features": "https://huggingface.co/spaces/Oyedon/UI_Enrolment_Trend_Resource_Optimization_App/resolve/main/src/ui_resource_features.pkl",131 "metadata": "https://huggingface.co/spaces/Oyedon/UI_Enrolment_Trend_Resource_Optimization_App/resolve/main/src/ui_system_metadata.pkl",132 }133 134 def load_joblib(url):135 response = requests.get(url, timeout=30)136 response.raise_for_status()137 return joblib.load(BytesIO(response.content))138 139 def load_pickle(url):140 response = requests.get(url, timeout=30)141 response.raise_for_status()142 return pickle.load(BytesIO(response.content))143 144 enroll_model = load_joblib(urls["enroll_model"])145 resource_model = load_joblib(urls["resource_model"])146 enroll_features = load_pickle(urls["enroll_features"])147 resource_features = load_pickle(urls["resource_features"])148 metadata = load_pickle(urls["metadata"])149 150 return enroll_model, enroll_features, resource_model, resource_features, metadata151 152# --------------------------------------------------153# NAVIGATION SIDEBAR154# --------------------------------------------------155def render_sidebar():156 with st.sidebar:157 st.image("https://via.placeholder.com/150x50/1a5490/ffffff?text=UI+Platform", use_container_width=True)158 st.markdown("### π§ Navigation")159 160 if st.button("π Home", use_container_width=True, type="primary" if st.session_state.page == 'landing' else "secondary"):161 st.session_state.page = 'landing'162 st.rerun()163 164 if st.button("π EDA Dashboard", use_container_width=True, type="primary" if st.session_state.page == 'eda' else "secondary"):165 st.session_state.page = 'eda'166 st.rerun()167 168 if st.button("π― Prediction Tool", use_container_width=True, type="primary" if st.session_state.page == 'prediction' else "secondary"):169 st.session_state.page = 'prediction'170 st.rerun()171 172 st.markdown("---")173 st.markdown("### βΉοΈ About")174 st.info("**University of Ibadan**\n\nEnrollment Prediction & Resource Allocation Platform")175 176 st.markdown("---")177 st.markdown("### π Quick Stats")178 st.metric("Faculties", len(UNIVERSITY_STRUCTURE))179 total_depts = sum(len(depts) for depts in UNIVERSITY_STRUCTURE.values())180 st.metric("Departments", total_depts)181 182# --------------------------------------------------183# LANDING PAGE184# --------------------------------------------------185def landing_page():186 st.markdown('<div class="main-header">π University of Ibadan</div>', unsafe_allow_html=True)187 st.markdown('<div class="sub-header">Enrollment Trend Prediction and Resource Allocation Analytics Platform</div>', unsafe_allow_html=True)188 189 st.warning("β οΈ **Disclaimer:** This system provides indicative predictions for planning and decision support. Final outcomes depend on policy decisions, admissions quotas, and external factors.")190 191 st.markdown("## π Platform Features")192 col1, col2, col3 = st.columns(3)193 194 with col1:195 st.markdown("""196 <div class="feature-card">197 <h3>π EDA Dashboard</h3>198 <p>Explore enrollment data with interactive visualizations, statistical analysis, and trend identification across faculties and departments.</p>199 <br>200 <p><strong>Features:</strong></p>201 <ul>202 <li>Data overview & statistics</li>203 <li>Distribution analysis</li>204 <li>Correlation matrices</li>205 <li>Trend visualization</li>206 </ul>207 </div>208 """, unsafe_allow_html=True)209 210 with col2:211 st.markdown("""212 <div class="feature-card">213 <h3>π― Enrollment Prediction</h3>214 <p>AI-powered enrollment growth forecasting using machine learning models trained on historical data and economic indicators.</p>215 <br>216 <p><strong>Predictions:</strong></p>217 <ul>218 <li>5-year enrollment projections</li>219 <li>Growth rate analysis</li>220 <li>Uncertainty ranges</li>221 <li>Faculty-level forecasts</li>222 </ul>223 </div>224 """, unsafe_allow_html=True)225 226 with col3:227 st.markdown("""228 <div class="feature-card">229 <h3>π° Resource Planning</h3>230 <p>Optimize resource allocation with graduation rate predictions and scenario simulations for evidence-based planning.</p>231 <br>232 <p><strong>Analysis:</strong></p>233 <ul>234 <li>Graduation rate forecasts</li>235 <li>Resource impact assessment</li>236 <li>Scenario simulation</li>237 <li>Budget optimization</li>238 </ul>239 </div>240 """, unsafe_allow_html=True)241 242 st.markdown("---")243 st.header("π Getting Started")244 245 col1, col2 = st.columns([2, 1])246 247 with col1:248 st.markdown("""249 ### How to Use This Platform250 251 **1. Exploratory Data Analysis (EDA)**252 - Upload enrollment datasets in CSV format253 - Explore historical trends and patterns254 - Analyze distributions and correlations255 - Identify key factors affecting enrollment256 257 **2. Enrollment Prediction**258 - Select faculty from hierarchical structure259 - Input current parameters and economic indicators260 - Get AI-powered enrollment growth predictions261 - View 5-year forecasts with uncertainty ranges262 263 **3. Resource Planning**264 - Analyze resource allocation efficiency265 - Predict graduation rates based on resources266 - Simulate different resource scenarios267 - Optimize budget and staffing decisions268 """)269 270 with col2:271 st.markdown("""272 <div class="info-box">273 <h4>π Data Requirements</h4>274 <p><strong>Format:</strong> CSV files</p>275 <p><strong>Key Fields:</strong></p>276 <ul style='margin-left: 1rem;'>277 <li>Year</li>278 <li>Enrollment numbers</li>279 <li>Budget data</li>280 <li>Staff counts</li>281 </ul>282 </div>283 """, unsafe_allow_html=True)284 285 st.markdown("---")286 st.markdown("## π― Quick Actions")287 288 col1, col2, col3 = st.columns(3)289 290 with col1:291 if st.button("π Explore Data β", use_container_width=True, type="primary"):292 st.session_state.page = 'eda'293 st.rerun()294 295 with col2:296 if st.button("π― Make Predictions β", use_container_width=True, type="primary"):297 st.session_state.page = 'prediction'298 st.rerun()299 300 with col3:301 sample_data = "year,enrollment,budget,staff\n2024,4000,50000000,1200\n2023,3800,48000000,1150"302 st.download_button(303 label="π₯ Sample Data",304 data=sample_data,305 file_name="sample_data.csv",306 mime="text/csv",307 use_container_width=True308 )309 310 st.markdown("---")311 with st.expander("π Research Context & Methodology"):312 st.markdown("""313 ### Research Foundation314 315 This platform is developed as part of a **Master of Information Science (M.Info.Sci)** research project316 at the University of Ibadan, investigating machine learning applications for enrollment prediction317 and resource optimization in Nigerian higher education.318 """)319 320 st.markdown("---")321 st.markdown("""322 <div style='text-align: center; color: #666; font-size: 0.9rem; padding: 2rem 0;'>323 <p><strong>Β© University of Ibadan 2026</strong></p>324 <p>All Rights Reserved Β© 2026</p>325 </div>326 """, unsafe_allow_html=True)327 328# --------------------------------------------------329# EDA DASHBOARD330# --------------------------------------------------331def eda_dashboard():332 st.markdown('<div class="main-header">π Exploratory Data Analysis</div>', unsafe_allow_html=True)333 st.markdown('<div class="sub-header">Analyze enrollment trends and patterns</div>', unsafe_allow_html=True)334 335 # Initialize session state for upload method if not exists336 if 'upload_method' not in st.session_state:337 st.session_state.upload_method = "Upload CSV File"338 339 # Multiple upload options340 upload_method = st.radio(341 "Choose data input method:",342 ["Upload CSV File", "Use Sample Data", "Paste CSV Data"],343 horizontal=True,344 key='upload_method_radio'345 )346 347 df = None348 349 if upload_method == "Upload CSV File":350 # Clear instructions351 st.markdown("**π€ Upload your CSV file below:**")352 353 uploaded_file = st.file_uploader(354 "Choose a CSV file", 355 type=['csv'],356 accept_multiple_files=False,357 help="Upload a CSV file containing enrollment data"358 )359 360 if uploaded_file is not None:361 try:362 # Show file details363 st.write(f"**File name:** {uploaded_file.name}")364 file_size_mb = uploaded_file.size / (1024 * 1024)365 st.write(f"**File size:** {file_size_mb:.2f} MB")366 367 if file_size_mb > 200:368 st.error("β File too large. Maximum size is 200MB")369 st.stop()370 371 # Progress indicator372 with st.spinner('Loading file...'):373 # Read the file into bytes first374 file_bytes = uploaded_file.read()375 uploaded_file.seek(0) # Reset pointer376 377 # Try multiple encoding options378 encodings_to_try = ['utf-8', 'latin-1', 'iso-8859-1', 'cp1252']379 380 for encoding in encodings_to_try:381 try:382 df = pd.read_csv(uploaded_file, encoding=encoding, on_bad_lines='skip')383 st.session_state.eda_data = df384 st.success(f"β
File loaded successfully using {encoding} encoding!")385 break386 except Exception as enc_error:387 uploaded_file.seek(0) # Reset pointer for next attempt388 if encoding == encodings_to_try[-1]: # Last encoding failed389 raise Exception(f"Could not read file with any encoding. Last error: {str(enc_error)}")390 continue391 392 except Exception as e:393 st.error(f"β Error loading file: {str(e)}")394 st.markdown("### π‘ Troubleshooting Tips:")395 st.markdown("""396 1. **Check file format**: Make sure it's a valid CSV file397 2. **Re-save your file**: 398 - Open in Excel399 - Go to File β Save As400 - Choose "CSV UTF-8 (Comma delimited) (*.csv)"401 - Save and try uploading again402 3. **Try alternative methods**: Use "Use Sample Data" or "Paste CSV Data" options above403 4. **File size**: Ensure file is under 200MB404 5. **Remove special characters**: Check if your data has unusual characters405 """)406 407 # Show option to try paste method408 st.warning("β οΈ **Alternative:** Try the 'Paste CSV Data' method instead - it often works when upload fails!")409 410 elif upload_method == "Use Sample Data":411 st.info("π Loading sample enrollment data...")412 413 # Create comprehensive sample data414 sample_data = {415 'year': [2019, 2020, 2021, 2022, 2023, 2024],416 'total_enrollment': [3200, 3500, 3650, 3800, 3900, 4000],417 'annual_budget_dept(β¦)': [42000000, 45000000, 46000000, 48000000, 49000000, 50000000],418 'fac_staff_count_male': [600, 650, 670, 680, 690, 700],419 'fac_staff_count_female': [400, 450, 470, 480, 490, 500],420 'hostel_allocation_probability': [35, 37, 38, 40, 40, 40],421 'strike_duration_months': [2, 0, 1, 0, 1, 1],422 'gdp_growth_percentage': [2.2, -1.8, 3.4, 3.2, 2.5, 2.8],423 'unemployment_rate_percentage': [23.1, 27.1, 22.5, 21.0, 20.5, 20.0]424 }425 df = pd.DataFrame(sample_data)426 st.session_state.eda_data = df427 st.success("β
Sample data loaded successfully!")428 429 elif upload_method == "Paste CSV Data":430 st.markdown("### π Paste Your CSV Data")431 st.info("""432 **Instructions:**433 1. Open your CSV file in Excel, Notepad, or any text editor434 2. Select all the content (Ctrl+A or Cmd+A)435 3. Copy it (Ctrl+C or Cmd+C)436 4. Paste it in the box below437 5. Click 'Load Data'438 """)439 440 csv_text = st.text_area(441 "Paste your CSV data here (include the header row):",442 height=250,443 placeholder="year,total_enrollment,budget,staff\n2024,4000,50000000,1200\n2023,3800,48000000,1150\n2022,3600,45000000,1100",444 key='csv_text_input'445 )446 447 if st.button("π Load Data", type="primary", use_container_width=True):448 if csv_text.strip():449 try:450 from io import StringIO451 df = pd.read_csv(StringIO(csv_text))452 st.session_state.eda_data = df453 st.success(f"β
Data loaded successfully! {df.shape[0]} rows Γ {df.shape[1]} columns")454 except Exception as e:455 st.error(f"β Error parsing CSV: {str(e)}")456 st.info("""457 **Common issues:**458 - Make sure the first line contains column headers459 - Ensure values are separated by commas460 - Check that there are no extra spaces or special characters461 - Each row should have the same number of columns462 """)463 else:464 st.warning("β οΈ Please paste your CSV data in the text area above")465 466 # Display data if available467 if st.session_state.eda_data is not None:468 df = st.session_state.eda_data469 470 st.markdown("---")471 st.success(f"β
**Dataset loaded:** {df.shape[0]} rows Γ {df.shape[1]} columns")472 473 # Add a clear data button474 if st.button("ποΈ Clear Data", help="Remove loaded data and start over"):475 st.session_state.eda_data = None476 st.rerun()477 478 tab1, tab2, tab3, tab4, tab5 = st.tabs(["π Overview", "π Distributions", "π Correlations", "π Trends", "π Summary"])479 480 with tab1:481 st.subheader("Dataset Overview")482 col1, col2, col3, col4 = st.columns(4)483 with col1:484 st.metric("Records", f"{df.shape[0]:,}")485 with col2:486 st.metric("Features", df.shape[1])487 with col3:488 st.metric("Numeric", df.select_dtypes(include=[np.number]).shape[1])489 with col4:490 st.metric("Missing", df.isnull().sum().sum())491 492 st.markdown("### π Data Preview (First 20 rows)")493 st.dataframe(df.head(20), use_container_width=True)494 495 st.markdown("### π Column Information")496 col_info = pd.DataFrame({497 'Column': df.columns,498 'Type': df.dtypes.values,499 'Non-Null Count': df.count().values,500 'Null Count': df.isnull().sum().values,501 'Unique Values': [df[col].nunique() for col in df.columns]502 })503 st.dataframe(col_info, use_container_width=True)504 505 with tab2:506 st.subheader("Distribution Analysis")507 numeric_cols = df.select_dtypes(include=[np.number]).columns.tolist()508 509 if numeric_cols:510 selected_col = st.selectbox("Select variable for distribution analysis", numeric_cols)511 512 col1, col2 = st.columns(2)513 with col1:514 fig = px.histogram(515 df, 516 x=selected_col, 517 nbins=30, 518 title=f'Distribution of {selected_col}',519 color_discrete_sequence=['#667eea']520 )521 st.plotly_chart(fig, use_container_width=True)522 523 with col2:524 fig = px.box(525 df, 526 y=selected_col, 527 title=f'Box Plot of {selected_col}',528 color_discrete_sequence=['#764ba2']529 )530 st.plotly_chart(fig, use_container_width=True)531 532 # Statistics533 st.markdown(f"### π Statistics for {selected_col}")534 stat_col1, stat_col2, stat_col3, stat_col4 = st.columns(4)535 536 with stat_col1:537 st.metric("Mean", f"{df[selected_col].mean():.2f}")538 with stat_col2:539 st.metric("Median", f"{df[selected_col].median():.2f}")540 with stat_col3:541 st.metric("Std Dev", f"{df[selected_col].std():.2f}")542 with stat_col4:543 st.metric("Range", f"{df[selected_col].max() - df[selected_col].min():.2f}")544 else:545 st.info("No numeric columns found in the dataset")546 547 with tab3:548 st.subheader("Correlation Analysis")549 numeric_df = df.select_dtypes(include=[np.number])550 551 if len(numeric_df.columns) > 1:552 corr_matrix = numeric_df.corr()553 554 fig = px.imshow(555 corr_matrix, 556 text_auto='.2f',557 aspect='auto',558 color_continuous_scale='RdBu_r',559 title='Correlation Heatmap',560 labels=dict(color="Correlation")561 )562 fig.update_layout(height=600)563 st.plotly_chart(fig, use_container_width=True)564 565 # Top correlations566 st.markdown("### π Strongest Correlations")567 corr_pairs = []568 for i in range(len(corr_matrix.columns)):569 for j in range(i+1, len(corr_matrix.columns)):570 corr_pairs.append({571 'Feature 1': corr_matrix.columns[i],572 'Feature 2': corr_matrix.columns[j],573 'Correlation': corr_matrix.iloc[i, j]574 })575 576 corr_df = pd.DataFrame(corr_pairs).sort_values('Correlation', key=abs, ascending=False).head(10)577 st.dataframe(corr_df, use_container_width=True, hide_index=True)578 else:579 st.info("Need at least 2 numeric columns for correlation analysis")580 581 with tab4:582 st.subheader("Trend Analysis")583 numeric_cols = df.select_dtypes(include=[np.number]).columns.tolist()584 585 if numeric_cols:586 year_cols = [col for col in df.columns if 'year' in col.lower()]587 588 if year_cols:589 year_col = st.selectbox("Select year column", year_cols)590 value_col = st.selectbox("Select value to trend", numeric_cols)591 592 fig = px.line(593 df.sort_values(year_col), 594 x=year_col, 595 y=value_col,596 title=f'{value_col} Trend Over Time',597 markers=True,598 color_discrete_sequence=['#667eea']599 )600 fig.update_layout(height=450)601 st.plotly_chart(fig, use_container_width=True)602 603 # Year-over-year growth604 if len(df) > 1:605 df_sorted = df.sort_values(year_col)606 growth = df_sorted[value_col].pct_change() * 100607 608 fig = go.Figure()609 fig.add_trace(go.Bar(610 x=df_sorted[year_col],611 y=growth,612 name='YoY Growth %',613 marker_color=['green' if x > 0 else 'red' for x in growth]614 ))615 fig.update_layout(616 title=f'Year-over-Year Growth Rate: {value_col}',617 xaxis_title='Year',618 yaxis_title='Growth Rate (%)',619 height=400620 )621 st.plotly_chart(fig, use_container_width=True)622 else:623 st.warning("No 'year' column found. Please ensure your data has a year column for trend analysis.")624 else:625 st.info("No numeric columns available for trend analysis")626 627 with tab5:628 st.subheader("Statistical Summary")629 630 st.markdown("### π Descriptive Statistics (Numeric Columns)")631 st.dataframe(df.describe(), use_container_width=True)632 633 if df.select_dtypes(include=['object']).shape[1] > 0:634 st.markdown("### π Categorical Variables Summary")635 cat_summary = df.select_dtypes(include=['object']).describe()636 st.dataframe(cat_summary, use_container_width=True)637 638 else:639 # Show helpful message when no data is loaded640 st.info("π **No data loaded.** Choose a method above to get started:")641 st.markdown("""642 - **Upload CSV File**: Browse and upload your file643 - **Use Sample Data**: Quick start with pre-loaded data644 - **Paste CSV Data**: Copy-paste your data directly (recommended if upload fails)645 """)646 647# --------------------------------------------------648# PREDICTION TOOL (YOUR COMPLETE EXISTING CODE)649# --------------------------------------------------650def prediction_tool():651 st.markdown('<div class="main-header">π University of Ibadan</div>', unsafe_allow_html=True)652 st.markdown('<div class="sub-header">Enrollment Trend Prediction and Resource Allocation Planning Tool</div>', unsafe_allow_html=True)653 654 st.warning("β οΈ **Disclaimer:** This system provides indicative predictions for planning and decision support.")655 656 try:657 enroll_model, enroll_features, resource_model, resource_features, metadata = load_models()658 except Exception as e:659 st.error(f"β Error loading models: {e}")660 st.stop()661 662 # Sidebar inputs663 st.sidebar.header("ποΈ Organizational Structure")664 st.sidebar.markdown('<div class="hierarchy-card"><strong>π Selection Path:</strong><br>University of Ibadan β Faculty</div>', unsafe_allow_html=True)665 666 faculty_options = sorted(list(UNIVERSITY_STRUCTURE.keys()))667 faculty = st.sidebar.selectbox("π Select Faculty", faculty_options, index=faculty_options.index('SCIENCE') if 'SCIENCE' in faculty_options else 0)668 669 st.sidebar.markdown(f"""670 <div style='background: linear-gradient(135deg, #1a237e 0%, #0d47a1 100%); padding: 0.8rem; border-radius: 5px; margin-top: 0.5rem;'>671 <strong style='color: #ffffff;'>Current Selection:</strong><br>672 <small style='color: #e3f2fd;'>ποΈ University of Ibadan<br>π {faculty}</small>673 </div>674 """, unsafe_allow_html=True)675 676 st.sidebar.markdown("---")677 st.sidebar.header("π Input Parameters")678 679 year = st.sidebar.slider("Target Year", 2025, 2035, 2026)680 total_enrollment = st.sidebar.number_input("Current Total Enrollment", min_value=50, max_value=50000, value=4000, step=50)681 annual_budget = st.sidebar.number_input("Annual Department Budget (β¦)", min_value=1_000_000, max_value=1_000_000_000, value=50_000_000, step=5_000_000, format="%d")682 683 col_staff1, col_staff2 = st.sidebar.columns(2)684 with col_staff1:685 male_staff = st.number_input("Male Staff", min_value=0, max_value=1000, value=700, step=10)686 with col_staff2:687 female_staff = st.number_input("Female Staff", min_value=0, max_value=1000, value=500, step=10)688 689 hostel_prob = st.sidebar.slider("Hostel Allocation Probability (%)", 0, 100, 40)690 strike_months = st.sidebar.slider("Strike Duration (months)", 0, 12, 1)691 692 st.sidebar.markdown("### π Economic Indicators")693 gdp_growth = st.sidebar.slider("GDP Growth Rate (%)", -5.0, 10.0, 2.5, 0.1)694 unemployment = st.sidebar.slider("Unemployment Rate (%)", 0.0, 40.0, 20.0, 0.5)695 696 # Calculate metrics697 total_staff = male_staff + female_staff698 budget_per_student = annual_budget / (total_enrollment if total_enrollment > 0 else 1)699 student_staff_ratio = total_enrollment / (total_staff if total_staff > 0 else 1)700 log_budget = np.log1p(annual_budget)701 702 # Context display703 st.markdown("### π Analysis Context")704 context_col1, context_col2 = st.columns(2)705 706 with context_col1:707 st.markdown(f"""708 <div style='background: #667eea20; padding: 1.5rem; border-radius: 8px; border-left: 4px solid #667eea;'>709 <strong>ποΈ University</strong><br>710 <span style='font-size: 1.3rem;'>University of Ibadan</span>711 </div>712 """, unsafe_allow_html=True)713 714 with context_col2:715 st.markdown(f"""716 <div style='background: #764ba220; padding: 1.5rem; border-radius: 8px; border-left: 4px solid #764ba2;'>717 <strong>π Faculty</strong><br>718 <span style='font-size: 1.3rem;'>{faculty}</span>719 </div>720 """, unsafe_allow_html=True)721 722 # ENROLLMENT PREDICTION723 st.markdown("---")724 st.header("π Enrollment Trend Prediction")725 726 enroll_input = pd.DataFrame([{727 'year': year, 'total_enrollment': total_enrollment, 'annual_budget_dept(β¦)': annual_budget,728 'fac_staff_count_male': male_staff, 'fac_staff_count_female': female_staff,729 'hostel_allocation_probability': hostel_prob, 'strike_duration_months': strike_months,730 'gdp_growth_percentage': gdp_growth, 'unemployment_rate_percentage': unemployment,731 'total_staff': total_staff, 'log_budget': log_budget732 }])733 734 for col in enroll_features:735 if col not in enroll_input.columns:736 enroll_input[col] = 0737 enroll_input = enroll_input[enroll_features]738 739 enroll_growth_pred = enroll_model.predict(enroll_input)[0]740 741 def calculate_uncertainty(prediction_value, base_uncertainty=0.15):742 uncertainty = abs(prediction_value) * base_uncertainty + 5.0743 return round(uncertainty, 1)744 745 enroll_uncertainty = calculate_uncertainty(enroll_growth_pred)746 747 col1, col2, col3 = st.columns([1, 2, 1])748 with col2:749 st.markdown(f"""750 <div class="metric-card">751 <div style='font-size: 0.9rem; opacity: 0.9;'>Predicted Enrollment Growth Rate</div>752 <div style='font-size: 3.5rem; font-weight: bold; margin: 1rem 0;'>{enroll_growth_pred:.1f}%</div>753 <div style='font-size: 1.2rem;'>754 {'β¬οΈ Strong Growth Expected' if enroll_growth_pred > 10 else 'π Moderate Growth' if enroll_growth_pred > 5 else 'β‘οΈ Stable Enrollment' if enroll_growth_pred > 0 else 'β¬οΈ Decline Expected'}755 </div>756 <div style='font-size: 0.95rem; opacity: 0.85; margin-top: 1rem; padding-top: 1rem; border-top: 1px solid rgba(255,255,255,0.3);'>757 <strong>Uncertainty Range:</strong> Β±{enroll_uncertainty} pp<br>758 <small style='font-size: 0.8rem;'>Range: {enroll_growth_pred - enroll_uncertainty:.1f}% to {enroll_growth_pred + enroll_uncertainty:.1f}%</small>759 </div>760 </div>761 """, unsafe_allow_html=True)762 763 # Key factors764 st.markdown("### π Key Predictive Factors")765 fact_col1, fact_col2, fact_col3 = st.columns(3)766 767 with fact_col1:768 econ_health = gdp_growth - (unemployment / 4)769 econ_status = "Strong" if econ_health > 2 else "Moderate" if econ_health > 0 else "Weak"770 st.markdown(f"""771 <div style='background: linear-gradient(135deg, #1b5e20 0%, #2e7d32 100%); padding: 1rem; border-left: 4px solid #66bb6a; border-radius: 5px;'>772 <strong style='color: #ffffff;'>π Economic Conditions</strong><br>773 <span style='font-size: 1.5rem; font-weight: bold; color: #a5d6a7;'>{econ_status}</span><br>774 <small style='color: #c8e6c9;'>GDP: {gdp_growth:.1f}% | Unemployment: {unemployment:.1f}%</small>775 </div>776 """, unsafe_allow_html=True)777 778 with fact_col2:779 fac_demand = "High" if faculty in ['SCIENCE', 'CLINICAL SCIENCES', 'TECHNOLOGY'] else "Moderate" if faculty in ['LAW', 'ARTS', 'SOCIAL SCIENCES'] else "Average"780 st.markdown(f"""781 <div style='background: linear-gradient(135deg, #1b5e20 0%, #2e7d32 100%); padding: 1rem; border-left: 4px solid #66bb6a; border-radius: 5px;'>782 <strong style='color: #ffffff;'>π Faculty Demand</strong><br>783 <span style='font-size: 1.5rem; font-weight: bold; color: #a5d6a7;'>{fac_demand}</span><br>784 <small style='color: #c8e6c9;'>{faculty}</small>785 </div>786 """, unsafe_allow_html=True)787 788 with fact_col3:789 disruption = "None" if strike_months == 0 else "Minor" if strike_months <= 3 else "Significant"790 disruption_impact = strike_months * -1.5791 st.markdown(f"""792 <div style='background: linear-gradient(135deg, #1b5e20 0%, #2e7d32 100%); padding: 1rem; border-left: 4px solid #66bb6a; border-radius: 5px;'>793 <strong style='color: #ffffff;'>β οΈ Disruption Level</strong><br>794 <span style='font-size: 1.5rem; font-weight: bold; color: #a5d6a7;'>{disruption}</span><br>795 <small style='color: #c8e6c9;'>{strike_months} month(s) | Impact: {disruption_impact:+.1f} pp</small>796 </div>797 """, unsafe_allow_html=True)798 799 # MULTI-YEAR PROJECTION800 st.markdown("---")801 st.header("π Multi-Year Enrollment Projection (5-Year Forecast)")802 803 projection_years = list(range(year, year + 6))804 projected_enrollment = [total_enrollment]805 projected_growth_rates = []806 current_enroll = total_enrollment807 808 for future_year in projection_years[1:]:809 future_input = pd.DataFrame([{810 'year': future_year, 'total_enrollment': current_enroll, 'annual_budget_dept(β¦)': annual_budget,811 'fac_staff_count_male': male_staff, 'fac_staff_count_female': female_staff,812 'hostel_allocation_probability': hostel_prob, 'strike_duration_months': strike_months,813 'gdp_growth_percentage': gdp_growth, 'unemployment_rate_percentage': unemployment,814 'total_staff': total_staff, 'log_budget': log_budget815 }])816 817 for col in enroll_features:818 if col not in future_input.columns:819 future_input[col] = 0820 future_input = future_input[enroll_features]821 822 growth_pred = enroll_model.predict(future_input)[0]823 projected_growth_rates.append(growth_pred)824 new_enroll = current_enroll * (1 + growth_pred / 100)825 projected_enrollment.append(int(new_enroll))826 current_enroll = new_enroll827 828 # Projection visualization829 fig_projection = go.Figure()830 fig_projection.add_trace(go.Scatter(831 x=projection_years, y=projected_enrollment, mode='lines+markers', name='Projected Enrollment',832 line=dict(color='#667eea', width=3), marker=dict(size=10, color='#667eea'),833 text=[f'{int(e):,}' for e in projected_enrollment], textposition='top center'834 ))835 836 upper_bound = [e * (1 + enroll_uncertainty/100) for e in projected_enrollment]837 lower_bound = [e * (1 - enroll_uncertainty/100) for e in projected_enrollment]838 839 fig_projection.add_trace(go.Scatter(840 x=projection_years + projection_years[::-1], y=upper_bound + lower_bound[::-1],841 fill='toself', fillcolor='rgba(102, 126, 234, 0.2)', line=dict(color='rgba(255,255,255,0)'),842 name='Uncertainty Range', hoverinfo='skip'843 ))844 845 fig_projection.update_layout(846 title=f"5-Year Enrollment Projection for {faculty}",847 xaxis_title="Year", yaxis_title="Total Enrollment", height=450,848 template="plotly_white", hovermode='x unified', showlegend=True849 )850 st.plotly_chart(fig_projection, use_container_width=True)851 852 # Summary table853 st.markdown("### π Year-by-Year Projection Summary")854 summary_data = []855 for i, proj_year in enumerate(projection_years):856 if i == 0:857 summary_data.append({'Year': proj_year, 'Projected Enrollment': f"{int(projected_enrollment[i]):,}",858 'Growth Rate': "Current", 'Change from Previous': "β", 'Cumulative Growth': "0%"})859 else:860 growth = projected_growth_rates[i-1]861 change = projected_enrollment[i] - projected_enrollment[i-1]862 cumulative = ((projected_enrollment[i] - projected_enrollment[0]) / projected_enrollment[0]) * 100863 summary_data.append({'Year': proj_year, 'Projected Enrollment': f"{int(projected_enrollment[i]):,}",864 'Growth Rate': f"{growth:.1f}%", 'Change from Previous': f"{change:+,.0f}",865 'Cumulative Growth': f"{cumulative:+.1f}%"})866 867 st.dataframe(pd.DataFrame(summary_data), use_container_width=True, hide_index=True)868 869 # Key Statistics870 stat_col1, stat_col2, stat_col3, stat_col4 = st.columns(4)871 872 with stat_col1:873 total_growth = ((projected_enrollment[-1] - projected_enrollment[0]) / projected_enrollment[0]) * 100874 st.metric("5-Year Total Growth", f"{total_growth:+.1f}%", delta=f"{int(projected_enrollment[-1] - projected_enrollment[0]):+,} students")875 876 with stat_col2:877 avg_annual_growth = sum(projected_growth_rates) / len(projected_growth_rates)878 st.metric("Average Annual Growth", f"{avg_annual_growth:.1f}%", delta="Per year")879 880 with stat_col3:881 final_enrollment = int(projected_enrollment[-1])882 st.metric(f"Projected {projection_years[-1]} Enrollment", f"{final_enrollment:,}", delta=f"From {int(total_enrollment):,}")883 884 with stat_col4:885 max_growth = max(projected_growth_rates)886 max_growth_year = projection_years[projected_growth_rates.index(max_growth) + 1]887 st.metric("Peak Growth Year", max_growth_year, delta=f"{max_growth:.1f}%")888 889 # Planning Implications890 st.markdown("### π‘ Planning Implications")891 impl_col1, impl_col2 = st.columns(2)892 893 with impl_col1:894 st.markdown('<div class="insight-card"><strong>π Capacity Planning:</strong></div>', unsafe_allow_html=True)895 if total_growth > 20:896 st.error("π¨ **High Growth Alert:** Significant infrastructure expansion needed")897 elif total_growth > 10:898 st.warning("π **Moderate Growth:** Gradual capacity expansion recommended")899 else:900 st.success("β‘οΈ **Stable Growth:** Maintain current capacity")901 902 with impl_col2:903 st.markdown('<div class="insight-card"><strong>π― Resource Requirements:</strong></div>', unsafe_allow_html=True)904 final_student_staff = projected_enrollment[-1] / total_staff905 additional_staff_needed = max(0, int((projected_enrollment[-1] / 20) - total_staff))906 st.info(f"π Projected ratio by {projection_years[-1]}: **{final_student_staff:.1f}:1**")907 if additional_staff_needed > 0:908 st.warning(f"π₯ Recommended staff increase: **{additional_staff_needed:,}** lecturers")909 910 # RESOURCE ALLOCATION911 st.markdown("---")912 st.header("π° Resource Allocation Impact")913 914 resource_input = pd.DataFrame([{915 'year': year, 'total_enrollment': total_enrollment, 'annual_budget_dept(β¦)': annual_budget,916 'fac_staff_count_male': male_staff, 'fac_staff_count_female': female_staff,917 'hostel_allocation_probability': hostel_prob, 'strike_duration_months': strike_months,918 'gdp_growth_percentage': gdp_growth, 'unemployment_rate_percentage': unemployment,919 'total_staff': total_staff, 'budget_per_student': budget_per_student,920 'student_staff_ratio': student_staff_ratio, 'log_budget': log_budget921 }])922 923 for col in resource_features:924 if col not in resource_input.columns:925 resource_input[col] = 0926 resource_input = resource_input[resource_features]927 928 grad_rate_pred = resource_model.predict(resource_input)[0]929 930 # Size adjustment for small faculties931 def adjust_for_faculty_size(predicted_rate, enrollment, staff_ratio, budget_per_student):932 if enrollment < 1500 and staff_ratio < 20:933 ratio_bonus = (20 - staff_ratio) * 0.5934 predicted_rate += ratio_bonus935 if enrollment < 1500 and budget_per_student > 60000:936 funding_bonus = min((budget_per_student - 60000) / 20000 * 2, 5)937 predicted_rate += funding_bonus938 if enrollment < 1000:939 predicted_rate += 3.0940 elif enrollment < 1500:941 predicted_rate += 1.5942 return min(predicted_rate, 95.0)943 944 grad_rate_pred = adjust_for_faculty_size(grad_rate_pred, total_enrollment, student_staff_ratio, budget_per_student)945 946 def calculate_grad_uncertainty(grad_rate, base_uncertainty=0.10):947 distance_from_mean = abs(grad_rate - 65) / 65948 uncertainty = grad_rate * base_uncertainty + (distance_from_mean * 3.0)949 return round(uncertainty, 1)950 951 grad_uncertainty = calculate_grad_uncertainty(grad_rate_pred)952 953 # Display graduation rate954 col1, col2, col3 = st.columns([1, 2, 1])955 with col2:956 st.markdown(f"""957 <div class="metric-card-secondary">958 <div style='font-size: 0.9rem; opacity: 0.9;'>Expected Graduation Rate</div>959 <div style='font-size: 3.5rem; font-weight: bold; margin: 1rem 0;'>{grad_rate_pred:.1f}%</div>960 <div style='font-size: 0.9rem; opacity: 0.85;'>National Average: 65% | NUC Target: 85%</div>961 <div style='font-size: 0.95rem; opacity: 0.85; margin-top: 1rem; padding-top: 1rem; border-top: 1px solid rgba(255,255,255,0.3);'>962 <strong>Uncertainty Range:</strong> Β±{grad_uncertainty} pp<br>963 <small style='font-size: 0.8rem;'>Range: {max(0, grad_rate_pred - grad_uncertainty):.1f}% to {min(100, grad_rate_pred + grad_uncertainty):.1f}%</small>964 </div>965 </div>966 """, unsafe_allow_html=True)967 968 # Resource metrics969 st.markdown("### π Resource Metrics")970 met_col1, met_col2, met_col3 = st.columns(3)971 972 with met_col1:973 ratio_color = "#28a745" if student_staff_ratio < 15 else "#ffc107" if student_staff_ratio < 25 else "#dc3545"974 ratio_status = "Excellent" if student_staff_ratio < 15 else "Good" if student_staff_ratio < 25 else "Strained"975 st.markdown(f"""976 <div style='background: {ratio_color}20; padding: 1.5rem; border-radius: 10px; border-left: 4px solid {ratio_color};'>977 <div style='color: #666; font-size: 0.85rem; font-weight: bold;'>STUDENT-STAFF RATIO</div>978 <div style='font-size: 2.5rem; font-weight: bold; color: {ratio_color}; margin: 0.5rem 0;'>{student_staff_ratio:.2f}:1</div>979 <div style='font-size: 0.85rem;'>{ratio_status}</div>980 </div>981 """, unsafe_allow_html=True)982 983 with met_col2:984 budget_status = "Well-funded" if budget_per_student > 100000 else "Adequate" if budget_per_student > 50000 else "Limited"985 st.markdown(f"""986 <div style='background: #2196f320; padding: 1.5rem; border-radius: 10px; border-left: 4px solid #2196f3;'>987 <div style='color: #666; font-size: 0.85rem; font-weight: bold;'>BUDGET PER STUDENT</div>988 <div style='font-size: 2rem; font-weight: bold; color: #2196f3; margin: 0.5rem 0;'>β¦{budget_per_student:,.0f}</div>989 <div style='font-size: 0.85rem;'>{budget_status}</div>990 </div>991 """, unsafe_allow_html=True)992 993 with met_col3:994 st.markdown(f"""995 <div style='background: #9c27b020; padding: 1.5rem; border-radius: 10px; border-left: 4px solid #9c27b0;'>996 <div style='color: #666; font-size: 0.85rem; font-weight: bold;'>TOTAL ACADEMIC STAFF</div>997 <div style='font-size: 2.5rem; font-weight: bold; color: #9c27b0; margin: 0.5rem 0;'>{total_staff}</div>998 <div style='font-size: 0.85rem;'>{male_staff}M / {female_staff}F</div>999 </div>1000 """, unsafe_allow_html=True)1001 1002 # PLANNING INSIGHTS1003 st.markdown("---")1004 st.header("π― Planning Insights")1005 1006 insight_col1, insight_col2 = st.columns(2)1007 1008 with insight_col1:1009 st.markdown("""1010 <div style='background: linear-gradient(135deg, #1a237e 0%, #283593 100%); padding: 1.5rem; border-radius: 8px;'>1011 <h4 style='color: #ffffff; margin-bottom: 1rem;'>π Key Observations</h4>1012 <ul style='margin: 0; padding-left: 1.5rem; line-height: 1.8; color: #e3f2fd;'>1013 <li>Lower studentβstaff ratios improve graduation outcomes</li>1014 <li>Budget efficiency impacts results more than total budget</li>1015 <li>Economic conditions strongly influence enrollment growth</li>1016 <li>Strike disruptions have measurable negative effects</li>1017 </ul>1018 </div>1019 """, unsafe_allow_html=True)1020 1021 with insight_col2:1022 st.markdown('<div class="insight-card"><h4 style="color: #1a5490;">π‘ Recommendations</h4></div>', unsafe_allow_html=True)1023 1024 recommendations = []1025 if student_staff_ratio > 25:1026 recommendations.append("π¨ **Critical:** Hire more staff to improve ratio")1027 if budget_per_student < 50000:1028 recommendations.append("β οΈ **Action:** Increase department budget allocation")1029 if strike_months > 6:1030 recommendations.append("π¨ **Urgent:** Develop strike mitigation strategies")1031 if enroll_growth_pred > 15:1032 recommendations.append("π‘ **Plan:** Expand infrastructure for growth")1033 if not recommendations:1034 recommendations.append("β
**Status:** Resource allocation is well-balanced")1035 1036 for rec in recommendations:1037 if "Critical" in rec or "Urgent" in rec:1038 st.error(rec)1039 elif "Action" in rec:1040 st.warning(rec)1041 elif "Plan" in rec:1042 st.info(rec)1043 else:1044 st.success(rec)1045 1046 # SCENARIO SIMULATION1047 st.markdown("---")1048 st.header("π Scenario Simulation: Resource Impact")1049 1050 sim_col1, sim_col2 = st.columns(2)1051 with sim_col1:1052 additional_lecturers = st.slider("Additional Lecturers to Hire", 0, 500, 100, step=10)1053 with sim_col2:1054 budget_increase_pct = st.slider("Budget Increase (%)", -20, 100, 25, step=5)1055 1056 scenario_total_staff = total_staff + additional_lecturers1057 budget_adjusted = annual_budget * (1 + budget_increase_pct / 100)1058 scenario_student_staff_ratio = total_enrollment / scenario_total_staff if scenario_total_staff > 0 else 01059 scenario_budget_per_student = budget_adjusted / total_enrollment if total_enrollment > 0 else 01060 1061 grad_rate_baseline = grad_rate_pred1062 grad_rate_effect = (3.1 / max(scenario_student_staff_ratio, 1)) * 41063 scenario_grad_rate = min(grad_rate_baseline + grad_rate_effect, 100)1064 1065 result_col1, result_col2, result_col3 = st.columns(3)1066 1067 with result_col1:1068 change = scenario_student_staff_ratio - student_staff_ratio1069 st.metric("Projected Student-Staff Ratio", f"{scenario_student_staff_ratio:.2f}:1", delta=f"{change:.2f}", delta_color="inverse")1070 1071 with result_col2:1072 change = scenario_budget_per_student - budget_per_student1073 st.metric("Budget per Student", f"β¦{scenario_budget_per_student:,.0f}", delta=f"β¦{change:,.0f}")1074 1075 with result_col3:1076 change = scenario_grad_rate - grad_rate_pred1077 st.metric("Projected Graduation Rate", f"{scenario_grad_rate:.1f}%", delta=f"{change:.1f}pp")1078 1079 # Impact visualization1080 st.markdown("### π Impact Visualization")1081 1082 fig = go.Figure()1083 metrics = ['Student-Staff Ratio', 'Budget per Student (β¦)', 'Graduation Rate (%)']1084 current_values = [student_staff_ratio, budget_per_student, grad_rate_pred]1085 projected_values = [scenario_student_staff_ratio, scenario_budget_per_student, scenario_grad_rate]1086 1087 fig.add_trace(go.Bar(name='Current', x=metrics, y=current_values, marker_color='#667eea', text=[f'{v:.1f}' for v in current_values], textposition='auto'))1088 fig.add_trace(go.Bar(name='Projected', x=metrics, y=projected_values, marker_color='#f5576c', text=[f'{v:.1f}' for v in projected_values], textposition='auto'))1089 1090 fig.update_layout(1091 title="Current vs Projected Resource Allocation",1092 xaxis_title="Metrics", yaxis_title="Value", barmode='group',1093 height=400, template="plotly_white", showlegend=True1094 )1095 st.plotly_chart(fig, use_container_width=True)1096 1097 # RESEARCH CONTEXT1098 st.markdown("---")1099 with st.expander("π Research Context & Methodology"):1100 st.markdown("""1101 ### Research Foundation1102 1103 This tool is developed as part of a **Master of Information Science (M.Info.Sci)** research project1104 at the University of Ibadan, investigating machine learning applications for enrollment prediction1105 and resource optimization in Nigerian higher education.1106 1107 ### Model Performance1108 1109 **Enrollment Prediction Model:**1110 - Trained on historical data from 2014-20241111 - Incorporates economic indicators, resource metrics, and institutional factors1112 - Provides indicative growth projections for planning purposes1113 1114 **Resource Allocation Model:**1115 - Analyzes relationship between resources and graduation outcomes1116 - Considers staff ratios, budget efficiency, and disruption factors1117 - Supports evidence-based resource planning decisions1118 1119 ### Usage Guidelines1120 1121 - Predictions are indicative and should inform, not replace, expert judgment1122 - Consider institutional policies, admission quotas, and external factors1123 - Use scenario simulation to explore different resource allocation strategies1124 - Review predictions alongside historical trends and domain expertise1125 1126 ### Limitations1127 1128 - Cannot predict sudden policy changes or regulatory reforms1129 - Historical patterns may not reflect future structural changes1130 - External shocks (pandemics, major political events) fall outside model scope1131 - Best used for comparative analysis and scenario planning1132 """)1133 1134 # Footer1135 st.markdown("---")1136 st.markdown("""1137 <div style='text-align: center; color: #666; font-size: 0.9rem; padding: 2rem 0;'>1138 <p><strong>Β© University of Ibadan 2026</strong></p>1139 <p>Developed as part of M.Info.Sci research on Machine Learning for Enrollment Prediction and Resource Optimization</p>1140 <p style='margin-top: 1rem; font-size: 0.8rem; color: #999;'>All Rights Reserved Β© 2026</p>1141 </div>1142 """, unsafe_allow_html=True)1143 1144# --------------------------------------------------1145# MAIN APP LOGIC1146# --------------------------------------------------1147render_sidebar()1148 1149if st.session_state.page == 'landing':1150 landing_page()1151elif st.session_state.page == 'eda':1152 eda_dashboard()1153elif st.session_state.page == 'prediction':1154 prediction_tool()