Team Ai
Apppublic

Oyedon/UI_Enrolment_Trend_Resource_Optimization_App

sourceHugging Facemitupdated 9mo agoView on Hugging Face
0likes
streamlit_app.py1154 linesDownload Raw Back to src
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()