Spaces:
Running
Running
Commit ·
a1cb950
1
Parent(s): 70c4eaa
Atualizar versão para 0.1.1 e melhorias no rodapé
Browse files- Atualizada versão do sistema de 0.1.0 para 0.1.1
- Adicionados badges de status no rodapé de todas as páginas
- Padronizado rodapé com informações do sistema em todas as sidebars
- Reorganizadas páginas (UCI, OULAD, Painel Analítico, Análise Exploratória)
- Removidos arquivos antigos e adicionadas novas páginas
- pyproject.toml +1 -1
- setup.py +1 -1
- webapp/home.py +1 -1
- webapp/oulad.pkl +2 -2
- webapp/pages/{1_dashboard.py → 1_Painel Analítico.py} +5 -5
- webapp/pages/{1_uci.py → 1_UCI.py} +4 -9
- webapp/pages/{2_oulad.py → 2_OULAD.py} +11 -0
- webapp/pages/3_Análise Exploratória_Autosserviço.py +37 -0
- webapp/pages/3_analisador.py +0 -919
- webapp/pages/4_pygwalker.py +0 -10
- webapp/pages/5_pandas_profiling.py +0 -185
- webapp/src/openai_interpreter.py +21 -33
- webapp/src/utilidades.py +6 -6
- webapp/uci.pkl +2 -2
pyproject.toml
CHANGED
|
@@ -1,6 +1,6 @@
|
|
| 1 |
[project]
|
| 2 |
name = "sida"
|
| 3 |
-
version = "0.1.
|
| 4 |
description = "Sistema de Identificação de Dificuldades de Aprendizagem (SIDA)"
|
| 5 |
readme = "README.md"
|
| 6 |
requires-python = ">=3.9"
|
|
|
|
| 1 |
[project]
|
| 2 |
name = "sida"
|
| 3 |
+
version = "0.1.1"
|
| 4 |
description = "Sistema de Identificação de Dificuldades de Aprendizagem (SIDA)"
|
| 5 |
readme = "README.md"
|
| 6 |
requires-python = ">=3.9"
|
setup.py
CHANGED
|
@@ -2,7 +2,7 @@ from setuptools import setup, find_packages
|
|
| 2 |
|
| 3 |
setup(
|
| 4 |
name='SIDA',
|
| 5 |
-
version='0.1.
|
| 6 |
packages=find_packages(),
|
| 7 |
install_requires=[
|
| 8 |
'pandas',
|
|
|
|
| 2 |
|
| 3 |
setup(
|
| 4 |
name='SIDA',
|
| 5 |
+
version='0.1.1',
|
| 6 |
packages=find_packages(),
|
| 7 |
install_requires=[
|
| 8 |
'pandas',
|
webapp/home.py
CHANGED
|
@@ -151,5 +151,5 @@ Programa de Pós-Graduação em Tecnologias Educacionais (PPGTE)
|
|
| 151 |
Instituto UFC Virtual (IUVI)
|
| 152 |
Universidade Federal do Ceará (UFC)
|
| 153 |
|
| 154 |
-
Versão
|
| 155 |
""")
|
|
|
|
| 151 |
Instituto UFC Virtual (IUVI)
|
| 152 |
Universidade Federal do Ceará (UFC)
|
| 153 |
|
| 154 |
+
Versão 0.1.1 - 2025
|
| 155 |
""")
|
webapp/oulad.pkl
CHANGED
|
@@ -1,3 +1,3 @@
|
|
| 1 |
version https://git-lfs.github.com/spec/v1
|
| 2 |
-
oid sha256:
|
| 3 |
-
size
|
|
|
|
| 1 |
version https://git-lfs.github.com/spec/v1
|
| 2 |
+
oid sha256:75c62b6e90e29e9bc916a9b3cad92be50d432d4c1124600bd37352c43b2a9207
|
| 3 |
+
size 143750
|
webapp/pages/{1_dashboard.py → 1_Painel Analítico.py}
RENAMED
|
@@ -1,5 +1,5 @@
|
|
| 1 |
"""
|
| 2 |
-
|
| 3 |
Página separada para análise consolidada dos datasets
|
| 4 |
"""
|
| 5 |
|
|
@@ -15,7 +15,7 @@ from src.openai_interpreter import criar_sidebar_padrao
|
|
| 15 |
|
| 16 |
# Configuração da página
|
| 17 |
st.set_page_config(
|
| 18 |
-
page_title="
|
| 19 |
page_icon="📊",
|
| 20 |
layout="wide"
|
| 21 |
)
|
|
@@ -24,7 +24,7 @@ st.set_page_config(
|
|
| 24 |
criar_sidebar_padrao()
|
| 25 |
|
| 26 |
# Título principal
|
| 27 |
-
st.title("📊
|
| 28 |
st.markdown("Visão geral dos datasets UCI e OULAD")
|
| 29 |
|
| 30 |
# Métricas principais
|
|
@@ -131,7 +131,7 @@ st.markdown("""
|
|
| 131 |
- `utilidades.py`: Lógica de negócio
|
| 132 |
- `openai_interpreter.py`: Interpretação IA
|
| 133 |
- `home.py`: Landing page
|
| 134 |
-
- `
|
| 135 |
|
| 136 |
2. **Extensibilidade**
|
| 137 |
- Fácil adição de novos datasets
|
|
@@ -150,5 +150,5 @@ Programa de Pós-Graduação em Tecnologias Educacionais (PPGTE)
|
|
| 150 |
Instituto UFC Virtual (IUVI)
|
| 151 |
Universidade Federal do Ceará (UFC)
|
| 152 |
|
| 153 |
-
Versão
|
| 154 |
""")
|
|
|
|
| 1 |
"""
|
| 2 |
+
Painel Analítico - Visão geral dos datasets UCI e OULAD
|
| 3 |
Página separada para análise consolidada dos datasets
|
| 4 |
"""
|
| 5 |
|
|
|
|
| 15 |
|
| 16 |
# Configuração da página
|
| 17 |
st.set_page_config(
|
| 18 |
+
page_title="Painel Analítico",
|
| 19 |
page_icon="📊",
|
| 20 |
layout="wide"
|
| 21 |
)
|
|
|
|
| 24 |
criar_sidebar_padrao()
|
| 25 |
|
| 26 |
# Título principal
|
| 27 |
+
st.title("📊 Painel Analítico")
|
| 28 |
st.markdown("Visão geral dos datasets UCI e OULAD")
|
| 29 |
|
| 30 |
# Métricas principais
|
|
|
|
| 131 |
- `utilidades.py`: Lógica de negócio
|
| 132 |
- `openai_interpreter.py`: Interpretação IA
|
| 133 |
- `home.py`: Landing page
|
| 134 |
+
- `1_Painel Analítico.py`: Painel Analítico consolidado
|
| 135 |
|
| 136 |
2. **Extensibilidade**
|
| 137 |
- Fácil adição de novos datasets
|
|
|
|
| 150 |
Instituto UFC Virtual (IUVI)
|
| 151 |
Universidade Federal do Ceará (UFC)
|
| 152 |
|
| 153 |
+
Versão 0.1.1 - 2025
|
| 154 |
""")
|
webapp/pages/{1_uci.py → 1_UCI.py}
RENAMED
|
@@ -6,6 +6,7 @@ import matplotlib.pyplot as plt
|
|
| 6 |
import seaborn as sns
|
| 7 |
import numpy as np
|
| 8 |
import pickle
|
|
|
|
| 9 |
|
| 10 |
st.set_page_config(
|
| 11 |
page_title="Análise Exploratória dos Dados - UCI",
|
|
@@ -63,12 +64,6 @@ with st.sidebar:
|
|
| 63 |
["Box Plot", "Histograma", "Violin Plot"]
|
| 64 |
)
|
| 65 |
st.markdown("---")
|
| 66 |
-
st.markdown("### Experimente")
|
| 67 |
-
analyzer = st.selectbox(
|
| 68 |
-
"Analisador",
|
| 69 |
-
["UCI EDA", "OULAD EDA"]
|
| 70 |
-
)
|
| 71 |
-
st.markdown("---")
|
| 72 |
st.markdown("## Informações")
|
| 73 |
# Calcular estudantes únicos baseado em características demográficas
|
| 74 |
colunas_id = ['school', 'sex', 'age', 'address', 'famsize', 'Pstatus', 'Medu', 'Fedu', 'Mjob', 'Fjob', 'reason', 'guardian']
|
|
@@ -83,8 +78,8 @@ with st.sidebar:
|
|
| 83 |
st.write(f"**Número de Valores Duplicados:** {df.duplicated().sum()}")
|
| 84 |
st.markdown("---")
|
| 85 |
|
| 86 |
-
#
|
| 87 |
-
|
| 88 |
|
| 89 |
# Create visualization section
|
| 90 |
st.markdown("### Visualização das distribuições dos dados numéricos ")
|
|
@@ -489,4 +484,4 @@ with open('uci.pkl', 'wb') as f:
|
|
| 489 |
# Seção de análise interativa (PyGWalker movido para o dashboard principal)
|
| 490 |
st.markdown("---")
|
| 491 |
st.markdown("### 🔍 Análise Interativa")
|
| 492 |
-
st.info("💡 Para análise interativa dos dados, utilize a aba 'Feature Importance' no
|
|
|
|
| 6 |
import seaborn as sns
|
| 7 |
import numpy as np
|
| 8 |
import pickle
|
| 9 |
+
from src.openai_interpreter import criar_rodape_sidebar
|
| 10 |
|
| 11 |
st.set_page_config(
|
| 12 |
page_title="Análise Exploratória dos Dados - UCI",
|
|
|
|
| 64 |
["Box Plot", "Histograma", "Violin Plot"]
|
| 65 |
)
|
| 66 |
st.markdown("---")
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 67 |
st.markdown("## Informações")
|
| 68 |
# Calcular estudantes únicos baseado em características demográficas
|
| 69 |
colunas_id = ['school', 'sex', 'age', 'address', 'famsize', 'Pstatus', 'Medu', 'Fedu', 'Mjob', 'Fjob', 'reason', 'guardian']
|
|
|
|
| 78 |
st.write(f"**Número de Valores Duplicados:** {df.duplicated().sum()}")
|
| 79 |
st.markdown("---")
|
| 80 |
|
| 81 |
+
# Rodapé com badges de status (igual ao da home)
|
| 82 |
+
criar_rodape_sidebar()
|
| 83 |
|
| 84 |
# Create visualization section
|
| 85 |
st.markdown("### Visualização das distribuições dos dados numéricos ")
|
|
|
|
| 484 |
# Seção de análise interativa (PyGWalker movido para o dashboard principal)
|
| 485 |
st.markdown("---")
|
| 486 |
st.markdown("### 🔍 Análise Interativa")
|
| 487 |
+
st.info("💡 Para análise interativa dos dados, utilize a aba 'Feature Importance' no painel analítico principal, onde você pode ativar o PyGWalker de forma opcional.")
|
webapp/pages/{2_oulad.py → 2_OULAD.py}
RENAMED
|
@@ -7,6 +7,7 @@ import matplotlib.pyplot as plt
|
|
| 7 |
import missingno as msno
|
| 8 |
import numpy as np
|
| 9 |
import pickle
|
|
|
|
| 10 |
|
| 11 |
|
| 12 |
st.set_page_config(
|
|
@@ -183,6 +184,16 @@ for col in merged_df.select_dtypes(include='object').columns:
|
|
| 183 |
# st.write("Merged DataFrame after handling missing values:")
|
| 184 |
# st.dataframe(merged_df.isnull().sum())
|
| 185 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 186 |
st.write('# Análise Exploratória de Dados (EDA) - OULAD')
|
| 187 |
|
| 188 |
'''
|
|
|
|
| 7 |
import missingno as msno
|
| 8 |
import numpy as np
|
| 9 |
import pickle
|
| 10 |
+
from src.openai_interpreter import criar_rodape_sidebar
|
| 11 |
|
| 12 |
|
| 13 |
st.set_page_config(
|
|
|
|
| 184 |
# st.write("Merged DataFrame after handling missing values:")
|
| 185 |
# st.dataframe(merged_df.isnull().sum())
|
| 186 |
|
| 187 |
+
# Sidebar com rodapé
|
| 188 |
+
with st.sidebar:
|
| 189 |
+
st.markdown("### 📊 Informações")
|
| 190 |
+
st.info("""
|
| 191 |
+
Esta página apresenta uma análise exploratória dos dados do OULAD (Open University Learning Analytics Dataset).
|
| 192 |
+
""")
|
| 193 |
+
st.markdown("---")
|
| 194 |
+
# Rodapé com badges de status (igual ao da home)
|
| 195 |
+
criar_rodape_sidebar()
|
| 196 |
+
|
| 197 |
st.write('# Análise Exploratória de Dados (EDA) - OULAD')
|
| 198 |
|
| 199 |
'''
|
webapp/pages/3_Análise Exploratória_Autosserviço.py
ADDED
|
@@ -0,0 +1,37 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import pandas as pd
|
| 2 |
+
import pygwalker as pyg
|
| 3 |
+
from pygwalker.api.streamlit import StreamlitRenderer
|
| 4 |
+
import streamlit as st
|
| 5 |
+
from src.openai_interpreter import criar_rodape_sidebar
|
| 6 |
+
|
| 7 |
+
st.set_page_config(
|
| 8 |
+
page_title="Análise Exploratória - Autosserviço",
|
| 9 |
+
page_icon="📊",
|
| 10 |
+
layout="wide",
|
| 11 |
+
initial_sidebar_state="expanded",
|
| 12 |
+
)
|
| 13 |
+
|
| 14 |
+
# Sidebar com rodapé
|
| 15 |
+
with st.sidebar:
|
| 16 |
+
st.markdown("### 📊 Sobre")
|
| 17 |
+
st.info("""
|
| 18 |
+
Esta página oferece uma ferramenta de análise interativa usando PygWalker, permitindo que você explore os dados de forma autônoma.
|
| 19 |
+
""")
|
| 20 |
+
st.markdown("---")
|
| 21 |
+
# Rodapé com badges de status (igual ao da home)
|
| 22 |
+
criar_rodape_sidebar()
|
| 23 |
+
|
| 24 |
+
st.title("📊 Análise Exploratória - Autosserviço (PygWalker)")
|
| 25 |
+
st.divider()
|
| 26 |
+
|
| 27 |
+
st.markdown("""
|
| 28 |
+
Esta página oferece uma ferramenta de análise interativa usando PygWalker, permitindo que você explore os dados de forma autônoma.
|
| 29 |
+
""")
|
| 30 |
+
|
| 31 |
+
if "df_uci" in st.session_state:
|
| 32 |
+
df = st.session_state['df_uci']
|
| 33 |
+
st.info("💡 Usando dados UCI. Para usar dados OULAD, navegue primeiro para a página OULAD.")
|
| 34 |
+
walker = pyg.walk(df)
|
| 35 |
+
else:
|
| 36 |
+
st.warning("⚠️ Nenhum dado disponível. Por favor, navegue para a página UCI ou OULAD primeiro para carregar os dados.")
|
| 37 |
+
|
webapp/pages/3_analisador.py
DELETED
|
@@ -1,919 +0,0 @@
|
|
| 1 |
-
import streamlit as st
|
| 2 |
-
import pandas as pd
|
| 3 |
-
import io
|
| 4 |
-
import plotly.express as px # FIX: Correct Plotly Express import
|
| 5 |
-
import tabula # For PDF reading
|
| 6 |
-
import tempfile # To save PDF temporarily for Tabula
|
| 7 |
-
import os
|
| 8 |
-
import numpy as np # For numeric checks
|
| 9 |
-
import logging # For better error logging (optional but good practice)
|
| 10 |
-
import matplotlib.pyplot as plt # For EDA visualizations
|
| 11 |
-
import seaborn as sns # For EDA visualizations
|
| 12 |
-
|
| 13 |
-
# Import custom functions
|
| 14 |
-
import sys
|
| 15 |
-
sys.path.append(os.path.join(os.path.dirname(__file__), '..', 'src'))
|
| 16 |
-
from utilidades import (
|
| 17 |
-
gerar_template_features,
|
| 18 |
-
gerar_template_unificado,
|
| 19 |
-
converter_template_para_excel,
|
| 20 |
-
validar_template_usuario,
|
| 21 |
-
realizar_eda_automatica
|
| 22 |
-
)
|
| 23 |
-
from vizualizacoes import criar_graficos_eda_usuario, criar_grafico_metricas_modelo
|
| 24 |
-
|
| 25 |
-
# --- Basic Logging Setup ---
|
| 26 |
-
# Helps in debugging, especially if deployed
|
| 27 |
-
logging.basicConfig(level=logging.INFO, format='%(asctime)s - %(levelname)s - %(message)s')
|
| 28 |
-
|
| 29 |
-
# --- Page Configuration (MUST be the first Streamlit command) ---
|
| 30 |
-
# Initialize theme based on session state *before* first run
|
| 31 |
-
if 'dark_mode' not in st.session_state:
|
| 32 |
-
st.session_state.dark_mode = False # Default to light mode
|
| 33 |
-
|
| 34 |
-
# NOTE: Streamlit's native theme setting (3 dots menu > Settings) is often preferred.
|
| 35 |
-
# This programmatic toggle is an alternative but less standard.
|
| 36 |
-
# We set the initial config, but CSS will handle the dynamic switching visually.
|
| 37 |
-
st.set_page_config(
|
| 38 |
-
page_title='Ferramenta de Análise de Dados Educacionais',
|
| 39 |
-
layout="wide",
|
| 40 |
-
initial_sidebar_state="expanded"
|
| 41 |
-
# theme="dark" if st.session_state.dark_mode else "light" # This doesn't work dynamically after first run
|
| 42 |
-
)
|
| 43 |
-
|
| 44 |
-
# --- Custom CSS Injection ---
|
| 45 |
-
# FIX: Corrected CSS injection with markdown and modern styles
|
| 46 |
-
# Enhancement: Added styles for dark mode toggle and improved button/sidebar look
|
| 47 |
-
def apply_custom_css():
|
| 48 |
-
"""Applies custom CSS for styling and dark mode."""
|
| 49 |
-
light_mode_css = """
|
| 50 |
-
<style>
|
| 51 |
-
/* --- Light Mode Variables --- */
|
| 52 |
-
:root {
|
| 53 |
-
--primary-bg: #FFFFFF;
|
| 54 |
-
--secondary-bg: #f8f9fa;
|
| 55 |
-
--text-color: #212529;
|
| 56 |
-
--primary-color: #007bff; /* Blue */
|
| 57 |
-
--primary-color-hover: #0056b3;
|
| 58 |
-
--secondary-color: #6c757d; /* Gray */
|
| 59 |
-
--border-color: #dee2e6;
|
| 60 |
-
--button-bg: var(--primary-color);
|
| 61 |
-
--button-text: white;
|
| 62 |
-
--button-hover-bg: var(--primary-color-hover);
|
| 63 |
-
--button-hover-text: white;
|
| 64 |
-
--button-border: var(--primary-color);
|
| 65 |
-
--sidebar-bg: var(--secondary-bg);
|
| 66 |
-
}
|
| 67 |
-
|
| 68 |
-
/* --- General Styling --- */
|
| 69 |
-
body {
|
| 70 |
-
font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
|
| 71 |
-
color: var(--text-color);
|
| 72 |
-
background-color: var(--primary-bg);
|
| 73 |
-
}
|
| 74 |
-
.main .block-container {
|
| 75 |
-
padding-top: 2rem; padding-bottom: 2rem; padding-left: 3rem; padding-right: 3rem;
|
| 76 |
-
}
|
| 77 |
-
h1, h2, h3 { color: var(--primary-color); }
|
| 78 |
-
|
| 79 |
-
/* --- Sidebar Styling (Modern) --- */
|
| 80 |
-
[data-testid="stSidebar"] {
|
| 81 |
-
background-color: var(--sidebar-bg);
|
| 82 |
-
border-right: 1px solid var(--border-color);
|
| 83 |
-
}
|
| 84 |
-
[data-testid="stSidebar"] h1, [data-testid="stSidebar"] h2, [data-testid="stSidebar"] h3 {
|
| 85 |
-
color: var(--primary-color); /* Match main area heading color */
|
| 86 |
-
}
|
| 87 |
-
[data-testid="stSidebar"] .stButton>button { /* Specificity for sidebar buttons */
|
| 88 |
-
border-color: var(--secondary-color);
|
| 89 |
-
color: var(--secondary-color);
|
| 90 |
-
background-color: transparent;
|
| 91 |
-
}
|
| 92 |
-
[data-testid="stSidebar"] .stButton>button:hover {
|
| 93 |
-
background-color: var(--secondary-color);
|
| 94 |
-
color: white;
|
| 95 |
-
}
|
| 96 |
-
|
| 97 |
-
|
| 98 |
-
/* --- Button Styling (Modern) --- */
|
| 99 |
-
.stButton>button {
|
| 100 |
-
border: 2px solid var(--button-border);
|
| 101 |
-
border-radius: 20px; /* Rounded */
|
| 102 |
-
padding: 0.5rem 1.25rem;
|
| 103 |
-
color: var(--button-text);
|
| 104 |
-
background-color: var(--button-bg);
|
| 105 |
-
transition: all 0.3s ease-in-out;
|
| 106 |
-
font-weight: 500;
|
| 107 |
-
}
|
| 108 |
-
.stButton>button:hover {
|
| 109 |
-
background-color: var(--button-hover-bg);
|
| 110 |
-
color: var(--button-hover-text);
|
| 111 |
-
border-color: var(--button-hover-bg);
|
| 112 |
-
transform: translateY(-2px); /* Slight lift */
|
| 113 |
-
box-shadow: 0 4px 8px rgba(0, 0, 0, 0.1);
|
| 114 |
-
}
|
| 115 |
-
.stButton>button:active {
|
| 116 |
-
transform: translateY(0);
|
| 117 |
-
box-shadow: none;
|
| 118 |
-
}
|
| 119 |
-
|
| 120 |
-
/* --- DataFrame Styling --- */
|
| 121 |
-
.stDataFrame { border: 1px solid var(--border-color); border-radius: 5px; }
|
| 122 |
-
|
| 123 |
-
/* --- Expander Styling --- */
|
| 124 |
-
.stExpander { border: 1px solid var(--border-color); border-radius: 5px; margin-bottom: 1rem; }
|
| 125 |
-
.stExpander header { background-color: var(--secondary-bg); border-radius: 5px 5px 0 0; }
|
| 126 |
-
|
| 127 |
-
/* --- Download Button --- */
|
| 128 |
-
/* Might need more specific selector if default styling overrides */
|
| 129 |
-
div[data-testid="stDownloadButton"] > button {
|
| 130 |
-
background-color: #28a745; /* Green */
|
| 131 |
-
border-color: #28a745;
|
| 132 |
-
}
|
| 133 |
-
div[data-testid="stDownloadButton"] > button:hover {
|
| 134 |
-
background-color: #218838;
|
| 135 |
-
border-color: #1e7e34;
|
| 136 |
-
}
|
| 137 |
-
|
| 138 |
-
</style>
|
| 139 |
-
"""
|
| 140 |
-
|
| 141 |
-
dark_mode_css = """
|
| 142 |
-
<style>
|
| 143 |
-
/* --- Dark Mode Variables --- */
|
| 144 |
-
body.dark-mode {
|
| 145 |
-
--primary-bg: #0e1117; /* Streamlit dark background */
|
| 146 |
-
--secondary-bg: #1c1e24; /* Slightly lighter dark */
|
| 147 |
-
--text-color: #fafafa; /* Light text */
|
| 148 |
-
--primary-color: #00aeff; /* Brighter Blue for dark */
|
| 149 |
-
--primary-color-hover: #008ecc;
|
| 150 |
-
--secondary-color: #8e949c; /* Lighter Gray */
|
| 151 |
-
--border-color: #31333F;
|
| 152 |
-
--button-bg: var(--primary-color);
|
| 153 |
-
--button-text: var(--primary-bg); /* Dark text on bright button */
|
| 154 |
-
--button-hover-bg: var(--primary-color-hover);
|
| 155 |
-
--button-hover-text: var(--primary-bg);
|
| 156 |
-
--button-border: var(--primary-color);
|
| 157 |
-
--sidebar-bg: #15181e; /* Darker sidebar */
|
| 158 |
-
}
|
| 159 |
-
|
| 160 |
-
/* --- Dark Mode Overrides --- */
|
| 161 |
-
body.dark-mode { background-color: var(--primary-bg); color: var(--text-color); }
|
| 162 |
-
body.dark-mode h1, body.dark-mode h2, body.dark-mode h3 { color: var(--primary-color); }
|
| 163 |
-
body.dark-mode [data-testid="stSidebar"] { background-color: var(--sidebar-bg); border-right-color: var(--border-color); }
|
| 164 |
-
body.dark-mode [data-testid="stSidebar"] h1, body.dark-mode [data-testid="stSidebar"] h2, body.dark-mode [data-testid="stSidebar"] h3 {
|
| 165 |
-
color: var(--primary-color);
|
| 166 |
-
}
|
| 167 |
-
body.dark-mode .stButton>button {
|
| 168 |
-
border-color: var(--button-border);
|
| 169 |
-
color: var(--button-text);
|
| 170 |
-
background-color: var(--button-bg);
|
| 171 |
-
}
|
| 172 |
-
body.dark-mode .stButton>button:hover {
|
| 173 |
-
background-color: var(--button-hover-bg);
|
| 174 |
-
color: var(--button-hover-text);
|
| 175 |
-
border-color: var(--button-hover-bg);
|
| 176 |
-
}
|
| 177 |
-
body.dark-mode .stDataFrame { border-color: var(--border-color); }
|
| 178 |
-
body.dark-mode .stExpander { border-color: var(--border-color); }
|
| 179 |
-
body.dark-mode .stExpander header { background-color: var(--secondary-bg); }
|
| 180 |
-
|
| 181 |
-
body.dark-mode div[data-testid="stDownloadButton"] > button {
|
| 182 |
-
background-color: #34c759; /* Brighter Green */
|
| 183 |
-
border-color: #34c759;
|
| 184 |
-
color: var(--primary-bg);
|
| 185 |
-
}
|
| 186 |
-
body.dark-mode div[data-testid="stDownloadButton"] > button:hover {
|
| 187 |
-
background-color: #2fad4f;
|
| 188 |
-
border-color: #2fad4f;
|
| 189 |
-
}
|
| 190 |
-
|
| 191 |
-
body.dark-mode [data-testid="stSidebar"] .stButton>button { /* Sidebar buttons in dark mode */
|
| 192 |
-
border-color: var(--secondary-color);
|
| 193 |
-
color: var(--secondary-color);
|
| 194 |
-
background-color: transparent;
|
| 195 |
-
}
|
| 196 |
-
body.dark-mode [data-testid="stSidebar"] .stButton>button:hover {
|
| 197 |
-
background-color: var(--secondary-color);
|
| 198 |
-
color: var(--primary-bg);
|
| 199 |
-
}
|
| 200 |
-
|
| 201 |
-
</style>
|
| 202 |
-
"""
|
| 203 |
-
|
| 204 |
-
# JavaScript to add/remove 'dark-mode' class from body
|
| 205 |
-
# FIX: Correct use of unsafe_allow_html for CSS and JS
|
| 206 |
-
js_code = f"""
|
| 207 |
-
<script>
|
| 208 |
-
function addDarkTheme() {{
|
| 209 |
-
document.body.classList.add('dark-mode');
|
| 210 |
-
}}
|
| 211 |
-
function removeDarkTheme() {{
|
| 212 |
-
document.body.classList.remove('dark-mode');
|
| 213 |
-
}}
|
| 214 |
-
{'addDarkTheme();' if st.session_state.dark_mode else 'removeDarkTheme();'}
|
| 215 |
-
</script>
|
| 216 |
-
"""
|
| 217 |
-
|
| 218 |
-
st.markdown(light_mode_css, unsafe_allow_html=True)
|
| 219 |
-
st.markdown(dark_mode_css, unsafe_allow_html=True)
|
| 220 |
-
st.markdown(js_code, unsafe_allow_html=True)
|
| 221 |
-
|
| 222 |
-
apply_custom_css() # Apply the styling
|
| 223 |
-
|
| 224 |
-
# --- Helper Functions ---
|
| 225 |
-
|
| 226 |
-
# FIX: Added show_spinner for user feedback during caching
|
| 227 |
-
# FIX: Enhanced error handling within load_data
|
| 228 |
-
@st.cache_data(show_spinner="Reading file...")
|
| 229 |
-
def load_data(uploaded_file):
|
| 230 |
-
"""Loads data from CSV, Excel, or PDF into a DataFrame list."""
|
| 231 |
-
if uploaded_file is None:
|
| 232 |
-
return None # Should not happen if called correctly, but good practice
|
| 233 |
-
|
| 234 |
-
dfs_list = None
|
| 235 |
-
file_extension = os.path.splitext(uploaded_file.name)[1].lower()
|
| 236 |
-
logging.info(f"Attempting to load file: {uploaded_file.name}, extension: {file_extension}")
|
| 237 |
-
|
| 238 |
-
try:
|
| 239 |
-
if file_extension == '.csv':
|
| 240 |
-
df = pd.read_csv(uploaded_file)
|
| 241 |
-
dfs_list = [df]
|
| 242 |
-
elif file_extension in ['.xlsx', '.xls']:
|
| 243 |
-
# Consider sheet selection for robustness later
|
| 244 |
-
df = pd.read_excel(uploaded_file, sheet_name=0)
|
| 245 |
-
dfs_list = [df]
|
| 246 |
-
elif file_extension == '.pdf':
|
| 247 |
-
# FIX: Robust PDF handling with Tabula and error checking
|
| 248 |
-
# ENHANCEMENT: Use with statement for temp file ensures cleanup
|
| 249 |
-
with tempfile.NamedTemporaryFile(delete=False, suffix=".pdf") as tmp_file:
|
| 250 |
-
tmp_file.write(uploaded_file.getvalue())
|
| 251 |
-
tmp_file_path = tmp_file.name
|
| 252 |
-
logging.info(f"Saved PDF to temporary file: {tmp_file_path}")
|
| 253 |
-
|
| 254 |
-
read_successful = False
|
| 255 |
-
try:
|
| 256 |
-
# Try reading with lattice=True (good for tables with lines)
|
| 257 |
-
logging.info("Trying tabula.read_pdf with lattice=True")
|
| 258 |
-
dfs_tabula = tabula.read_pdf(tmp_file_path, pages='all', multiple_tables=True, lattice=True)
|
| 259 |
-
read_successful = True
|
| 260 |
-
except Exception as e_lattice:
|
| 261 |
-
logging.warning(f"Tabula lattice=True failed: {e_lattice}. Trying stream=True.")
|
| 262 |
-
try:
|
| 263 |
-
# Fallback to stream=True (good for whitespace separation)
|
| 264 |
-
logging.info("Trying tabula.read_pdf with stream=True")
|
| 265 |
-
dfs_tabula = tabula.read_pdf(tmp_file_path, pages='all', multiple_tables=True, stream=True)
|
| 266 |
-
read_successful = True
|
| 267 |
-
except Exception as e_stream:
|
| 268 |
-
logging.error(f"Tabula stream=True also failed: {e_stream}")
|
| 269 |
-
# Check for common Java error
|
| 270 |
-
if "java" in str(e_stream).lower() or isinstance(e_stream, FileNotFoundError):
|
| 271 |
-
st.error("Error processing PDF: Java not found or not configured correctly. Please ensure Java Development Kit (JDK) is installed and in your system's PATH.")
|
| 272 |
-
else:
|
| 273 |
-
st.error(f"Failed to read PDF tables using Tabula: {e_stream}")
|
| 274 |
-
dfs_tabula = None # Ensure it's None on failure
|
| 275 |
-
finally:
|
| 276 |
-
# FIX: Ensure temporary file is always removed
|
| 277 |
-
if os.path.exists(tmp_file_path):
|
| 278 |
-
os.remove(tmp_file_path)
|
| 279 |
-
logging.info(f"Removed temporary PDF file: {tmp_file_path}")
|
| 280 |
-
|
| 281 |
-
if read_successful and isinstance(dfs_tabula, list):
|
| 282 |
-
# Filter out empty DataFrames and check if any tables were found
|
| 283 |
-
dfs_list = [df for df in dfs_tabula if isinstance(df, pd.DataFrame) and not df.empty]
|
| 284 |
-
if not dfs_list:
|
| 285 |
-
st.warning("Tabula processed the PDF, but no data tables were found.")
|
| 286 |
-
logging.warning(f"No non-empty tables extracted from PDF: {uploaded_file.name}")
|
| 287 |
-
dfs_list = None # Set back to None if only empty tables found
|
| 288 |
-
else:
|
| 289 |
-
logging.info(f"Successfully extracted {len(dfs_list)} table(s) from PDF.")
|
| 290 |
-
|
| 291 |
-
else:
|
| 292 |
-
st.error(f"Unsupported file format: '{file_extension}'. Please upload CSV, Excel, or PDF.")
|
| 293 |
-
logging.error(f"Unsupported file type uploaded: {uploaded_file.name}")
|
| 294 |
-
return None # Explicitly return None for unsupported types
|
| 295 |
-
|
| 296 |
-
return dfs_list
|
| 297 |
-
|
| 298 |
-
# FIX: Catch specific library errors and general exceptions during file reading
|
| 299 |
-
except ImportError as ie:
|
| 300 |
-
if 'tabula' in str(ie).lower():
|
| 301 |
-
st.error("Processing Error: `tabula-py` library not installed or Java not found/configured. Cannot process PDF files.")
|
| 302 |
-
logging.error("ImportError related to tabula-py or Java.")
|
| 303 |
-
else:
|
| 304 |
-
st.error(f"Import Error: {ie}. Please ensure required libraries (pandas, openpyxl, tabula-py) are installed.")
|
| 305 |
-
logging.error(f"ImportError: {ie}")
|
| 306 |
-
return None
|
| 307 |
-
except Exception as e:
|
| 308 |
-
st.error(f"An error occurred while reading '{uploaded_file.name}': {e}")
|
| 309 |
-
st.warning("Please ensure the file is valid, not password-protected or corrupted, and that Java is installed (for PDF).")
|
| 310 |
-
logging.error(f"Failed to load file {uploaded_file.name}: {e}", exc_info=True) # Log traceback
|
| 311 |
-
return None
|
| 312 |
-
|
| 313 |
-
|
| 314 |
-
# FIX: Added show_spinner
|
| 315 |
-
@st.cache_data(show_spinner="Preparing download...")
|
| 316 |
-
def convert_df_to_csv(df):
|
| 317 |
-
"""Converts DataFrame to CSV bytes for download."""
|
| 318 |
-
buffer = io.StringIO()
|
| 319 |
-
df.to_csv(buffer, index=False)
|
| 320 |
-
return buffer.getvalue().encode('utf-8')
|
| 321 |
-
|
| 322 |
-
# --- Session State Initialization ---
|
| 323 |
-
# (Ensures variables persist across reruns)
|
| 324 |
-
if 'dataframes_list' not in st.session_state:
|
| 325 |
-
st.session_state.dataframes_list = None
|
| 326 |
-
if 'selected_df_index' not in st.session_state:
|
| 327 |
-
st.session_state.selected_df_index = 0
|
| 328 |
-
if 'original_df' not in st.session_state:
|
| 329 |
-
st.session_state.original_df = None
|
| 330 |
-
if 'current_df' not in st.session_state:
|
| 331 |
-
st.session_state.current_df = None
|
| 332 |
-
if 'uploaded_file_name' not in st.session_state:
|
| 333 |
-
st.session_state.uploaded_file_name = None
|
| 334 |
-
if 'data_loaded' not in st.session_state:
|
| 335 |
-
st.session_state.data_loaded = False
|
| 336 |
-
if 'template_dataset_type' not in st.session_state:
|
| 337 |
-
st.session_state.template_dataset_type = 'uci'
|
| 338 |
-
if 'template_downloaded' not in st.session_state:
|
| 339 |
-
st.session_state.template_downloaded = False
|
| 340 |
-
if 'user_data_uploaded' not in st.session_state:
|
| 341 |
-
st.session_state.user_data_uploaded = None
|
| 342 |
-
if 'eda_results' not in st.session_state:
|
| 343 |
-
st.session_state.eda_results = None
|
| 344 |
-
# 'dark_mode' initialized near st.set_page_config
|
| 345 |
-
|
| 346 |
-
# --- Sidebar ---
|
| 347 |
-
with st.sidebar: # ENHANCEMENT: Use 'with' context manager for sidebar clarity
|
| 348 |
-
st.header("⚙️ Upload & Options")
|
| 349 |
-
|
| 350 |
-
# ENHANCEMENT: Dark mode toggle removed
|
| 351 |
-
def apply_custom_css():
|
| 352 |
-
pass # No custom css applied
|
| 353 |
-
|
| 354 |
-
|
| 355 |
-
uploaded_file = st.file_uploader(
|
| 356 |
-
"Choose a CSV, Excel, or PDF file",
|
| 357 |
-
type=['csv', 'xlsx', 'xls', 'pdf'],
|
| 358 |
-
accept_multiple_files=False,
|
| 359 |
-
help="Upload your dataset (CSV, Excel, PDF)."
|
| 360 |
-
)
|
| 361 |
-
|
| 362 |
-
# --- File Processing Logic ---
|
| 363 |
-
if uploaded_file is not None:
|
| 364 |
-
# Process only if it's a new file or data hasn't been loaded successfully before
|
| 365 |
-
if not st.session_state.data_loaded or uploaded_file.name != st.session_state.get('uploaded_file_name'):
|
| 366 |
-
st.session_state.dataframes_list = load_data(uploaded_file) # Call robust load_data
|
| 367 |
-
st.session_state.uploaded_file_name = uploaded_file.name
|
| 368 |
-
st.session_state.selected_df_index = 0 # Reset index for new file
|
| 369 |
-
|
| 370 |
-
if st.session_state.dataframes_list: # Check if load_data returned a valid list
|
| 371 |
-
st.session_state.data_loaded = True
|
| 372 |
-
# Handle multiple tables from PDF
|
| 373 |
-
if len(st.session_state.dataframes_list) > 1:
|
| 374 |
-
st.success(f"Found {len(st.session_state.dataframes_list)} tables.")
|
| 375 |
-
table_options = {i: f"Table {i+1} (Shape: {df.shape})" for i, df in enumerate(st.session_state.dataframes_list)}
|
| 376 |
-
st.session_state.selected_df_index = st.selectbox(
|
| 377 |
-
"Select table to analyze:",
|
| 378 |
-
options=list(table_options.keys()),
|
| 379 |
-
format_func=lambda x: table_options[x],
|
| 380 |
-
key='pdf_table_select'
|
| 381 |
-
)
|
| 382 |
-
else:
|
| 383 |
-
st.success(f"File '{uploaded_file.name}' loaded.")
|
| 384 |
-
|
| 385 |
-
# Set initial dataframes in session state
|
| 386 |
-
st.session_state.original_df = st.session_state.dataframes_list[st.session_state.selected_df_index].copy()
|
| 387 |
-
st.session_state.current_df = st.session_state.original_df.copy()
|
| 388 |
-
# Force a rerun to update the main panel immediately after successful load
|
| 389 |
-
# st.rerun() # Careful with rerun, can cause loops if not managed well. Often UI updates automatically.
|
| 390 |
-
|
| 391 |
-
else: # load_data returned None or empty list
|
| 392 |
-
st.session_state.data_loaded = False
|
| 393 |
-
st.session_state.original_df = None
|
| 394 |
-
st.session_state.current_df = None
|
| 395 |
-
# Error/Warning message is handled inside load_data()
|
| 396 |
-
|
| 397 |
-
# Handling selection change for multi-table PDFs *after* initial load
|
| 398 |
-
elif st.session_state.data_loaded and st.session_state.dataframes_list and len(st.session_state.dataframes_list) > 1:
|
| 399 |
-
table_options = {i: f"Table {i+1} (Shape: {df.shape})" for i, df in enumerate(st.session_state.dataframes_list)}
|
| 400 |
-
new_index = st.selectbox(
|
| 401 |
-
"Select table to analyze:",
|
| 402 |
-
options=list(table_options.keys()),
|
| 403 |
-
index=st.session_state.selected_df_index, # Default to current selection
|
| 404 |
-
format_func=lambda x: table_options[x],
|
| 405 |
-
key='pdf_table_select_rerun'
|
| 406 |
-
)
|
| 407 |
-
if new_index != st.session_state.selected_df_index:
|
| 408 |
-
st.session_state.selected_df_index = new_index
|
| 409 |
-
# Update dfs based on new selection
|
| 410 |
-
st.session_state.original_df = st.session_state.dataframes_list[st.session_state.selected_df_index].copy()
|
| 411 |
-
st.session_state.current_df = st.session_state.original_df.copy()
|
| 412 |
-
st.rerun() # Rerun needed to reflect the change in the main panel
|
| 413 |
-
|
| 414 |
-
# --- Sidebar Analysis Options (Conditional Display) ---
|
| 415 |
-
if st.session_state.data_loaded and st.session_state.current_df is not None:
|
| 416 |
-
st.markdown("---") # Visual separator
|
| 417 |
-
st.header("📊 Analysis Options")
|
| 418 |
-
df = st.session_state.current_df # Use current (possibly cleaned) df
|
| 419 |
-
|
| 420 |
-
analysis_options = ['Data Preview & Info', 'Data Cleaning', 'Feature Importance Template']
|
| 421 |
-
numeric_cols_exist = any(pd.api.types.is_numeric_dtype(dtype) for dtype in df.dtypes)
|
| 422 |
-
|
| 423 |
-
if numeric_cols_exist:
|
| 424 |
-
analysis_options.extend(['Column Statistics', 'Visualizations', 'Correlation Analysis'])
|
| 425 |
-
else:
|
| 426 |
-
st.warning("No numeric columns detected. Some analysis options disabled.", icon="⚠️")
|
| 427 |
-
|
| 428 |
-
# Use session state to remember the choice, preventing reset on reruns
|
| 429 |
-
if 'analysis_choice' not in st.session_state:
|
| 430 |
-
st.session_state.analysis_choice = analysis_options[0] # Default choice
|
| 431 |
-
|
| 432 |
-
st.session_state.analysis_choice = st.radio(
|
| 433 |
-
"Choose Analysis:",
|
| 434 |
-
analysis_options,
|
| 435 |
-
index=analysis_options.index(st.session_state.analysis_choice), # Set index based on state
|
| 436 |
-
key='analysis_radio' # Consistent key
|
| 437 |
-
)
|
| 438 |
-
else:
|
| 439 |
-
# Clear analysis choice if data is unloaded
|
| 440 |
-
if 'analysis_choice' in st.session_state:
|
| 441 |
-
del st.session_state.analysis_choice
|
| 442 |
-
|
| 443 |
-
|
| 444 |
-
# --- Footer ---
|
| 445 |
-
st.markdown("---")
|
| 446 |
-
st.caption("PPGTE | IUVI | UFC - SIDA 0.2.1")
|
| 447 |
-
|
| 448 |
-
|
| 449 |
-
# --- Main Application Area ---
|
| 450 |
-
st.title("📈 Ferramenta de Análise de Dados")
|
| 451 |
-
|
| 452 |
-
if not st.session_state.data_loaded or st.session_state.current_df is None:
|
| 453 |
-
st.info("👈 Enviar um conjunto de dados (CSV, Excel, PDF) usando a barra lateral para iniciar a análise.")
|
| 454 |
-
st.markdown("""
|
| 455 |
-
**instruções:**
|
| 456 |
-
1. Use o botão na barra lateral para enviar o seu arquivo com o conjunto de dados que deseja analisar.
|
| 457 |
-
2. Se desejar enviar um PDF com múltiplas tabelas, selecione uma para analisar na lista suspensa da barra lateral.
|
| 458 |
-
3. Use as **Opções de Análise** na barra lateral para explorar e limpar seus dados.
|
| 459 |
-
4. Ative o **Modo Escuro** na barra lateral para o tema de visualização que preferir.
|
| 460 |
-
""")
|
| 461 |
-
else:
|
| 462 |
-
df = st.session_state.current_df # Work with the current dataframe
|
| 463 |
-
analysis_type = st.session_state.analysis_choice # Get selected analysis from state
|
| 464 |
-
|
| 465 |
-
st.header(f"🔎 {analysis_type}") # Show title for the selected analysis
|
| 466 |
-
|
| 467 |
-
# --- Data Preview & Info ---
|
| 468 |
-
if analysis_type == 'Data Preview & Info':
|
| 469 |
-
# ENHANCEMENT: Use st.columns for better layout
|
| 470 |
-
col1, col2 = st.columns(2)
|
| 471 |
-
with col1:
|
| 472 |
-
st.subheader("Informação básica do conjunto de dados")
|
| 473 |
-
st.write(f"**Nome do Arquivo:** `{st.session_state.uploaded_file_name}`")
|
| 474 |
-
if st.session_state.dataframes_list and len(st.session_state.dataframes_list) > 1:
|
| 475 |
-
st.write(f"**Tabela Selecionada:** `Table {st.session_state.selected_df_index + 1}`")
|
| 476 |
-
st.write(f"**Linhas:** `{df.shape[0]}`")
|
| 477 |
-
st.write(f"**Colunas:** `{df.shape[1]}`")
|
| 478 |
-
with col2:
|
| 479 |
-
st.subheader("Tipos de Colunas")
|
| 480 |
-
# Improve display of dtypes
|
| 481 |
-
st.dataframe(df.dtypes.astype(str).reset_index().rename(columns={'index': 'Column', 0: 'DataType'}), height=200, use_container_width=True, hide_index=True)
|
| 482 |
-
|
| 483 |
-
st.subheader("Data Preview")
|
| 484 |
-
st.dataframe(df.head()) # Show first 5 rows by default
|
| 485 |
-
|
| 486 |
-
# ENHANCEMENT: Use st.expander for download section
|
| 487 |
-
with st.expander("Baixar Dados"):
|
| 488 |
-
st.markdown("Baixe os dados *conforme exibido atualmente* (incluindo qualquer limpeza).")
|
| 489 |
-
csv_data = convert_df_to_csv(df) # Use cached function
|
| 490 |
-
st.download_button(
|
| 491 |
-
label="⬇️ Baixar Dados como CSV",
|
| 492 |
-
data=csv_data,
|
| 493 |
-
file_name=f"current_data_{st.session_state.uploaded_file_name.split('.')[0]}{'_T'+str(st.session_state.selected_df_index+1) if st.session_state.dataframes_list and len(st.session_state.dataframes_list)>1 else ''}.csv",
|
| 494 |
-
mime='text/csv',
|
| 495 |
-
key='download-current-csv'
|
| 496 |
-
)
|
| 497 |
-
|
| 498 |
-
# --- Data Cleaning ---
|
| 499 |
-
elif analysis_type == 'Data Cleaning':
|
| 500 |
-
st.subheader("Tratar Valores Ausentes")
|
| 501 |
-
|
| 502 |
-
# ENHANCEMENT: Use spinner for calculations if dataset is large
|
| 503 |
-
with st.spinner("Analisando valores ausentes..."):
|
| 504 |
-
missing_values = df.isnull().sum()
|
| 505 |
-
missing_summary = missing_values[missing_values > 0].sort_values(ascending=False)
|
| 506 |
-
|
| 507 |
-
# ENHANCEMENT: Use st.columns for layout
|
| 508 |
-
col1, col2 = st.columns([1, 2]) # Column for summary, column for options
|
| 509 |
-
|
| 510 |
-
with col1:
|
| 511 |
-
st.write("**Resumo de Valores Ausentes:**")
|
| 512 |
-
if not missing_summary.empty:
|
| 513 |
-
st.dataframe(missing_summary.reset_index().rename(columns={'index': 'Coluna', 0: 'Contagem de Ausentes'}), use_container_width=True, hide_index=True)
|
| 514 |
-
st.metric(label="Total de Valores Ausentes", value=f"{missing_summary.sum():,}") # Formatted number
|
| 515 |
-
else:
|
| 516 |
-
st.success("✅ Nenhum valor ausente encontrado!")
|
| 517 |
-
|
| 518 |
-
if st.button("🔄 Resetar para os Dados Originais", key='reset_data', help="Descarta todas as etapas de limpeza aplicadas nesta sessão."):
|
| 519 |
-
# ENHANCEMENT: Use spinner for reset action
|
| 520 |
-
with st.spinner("Resetando dados..."):
|
| 521 |
-
st.session_state.current_df = st.session_state.original_df.copy()
|
| 522 |
-
st.success("Dados restaurados para o estado original.")
|
| 523 |
-
st.rerun() # Rerun to reflect the reset
|
| 524 |
-
|
| 525 |
-
with col2:
|
| 526 |
-
if not missing_summary.empty:
|
| 527 |
-
st.write("**Opções de Limpeza:**")
|
| 528 |
-
# FIX: Ensure unique keys for widgets if they might reappear
|
| 529 |
-
cleaning_strategy = st.radio(
|
| 530 |
-
"Escolha uma estrat��gia:",
|
| 531 |
-
('Remover linhas com valores ausentes',
|
| 532 |
-
'Remover colunas com valores ausentes',
|
| 533 |
-
'Impute missing values (Fill)'),
|
| 534 |
-
key='clean_strategy_radio'
|
| 535 |
-
)
|
| 536 |
-
|
| 537 |
-
imputation_method = None
|
| 538 |
-
constant_value = None
|
| 539 |
-
cols_to_impute = None
|
| 540 |
-
|
| 541 |
-
if cleaning_strategy == 'Impute missing values (Fill)':
|
| 542 |
-
imputation_method = st.selectbox(
|
| 543 |
-
"Método de Imputação:",
|
| 544 |
-
('Média (Apenas Numérico)', 'Mediana (Apenas Numérico)', 'Moda (Mais Frequente)', 'Valor Constante', 'Preencher para Frente (ffill)', 'Preencher para Trás (bfill)'), # Added ffill/bfill
|
| 545 |
-
key='impute_method_select'
|
| 546 |
-
)
|
| 547 |
-
if imputation_method == 'Valor Constante':
|
| 548 |
-
constant_value = st.text_input("Digite o valor constante para preencher:", value="NA", key='impute_constant_input')
|
| 549 |
-
|
| 550 |
-
# Option to select columns for imputation
|
| 551 |
-
all_cols_with_na = missing_summary.index.tolist()
|
| 552 |
-
cols_to_impute = st.multiselect(
|
| 553 |
-
"Selecione as colunas para imputação (padrão: todas com valores ausentes):",
|
| 554 |
-
options=df.columns.tolist(),
|
| 555 |
-
default=all_cols_with_na,
|
| 556 |
-
key='impute_cols_multi'
|
| 557 |
-
)
|
| 558 |
-
|
| 559 |
-
if st.button("✨ Aplicar Limpeza", key='apply_cleaning_button'):
|
| 560 |
-
# ENHANCEMENT: Use spinner for cleaning action
|
| 561 |
-
with st.spinner("Aplicando regras de limpeza..."):
|
| 562 |
-
df_cleaned = df.copy() # Work on a copy
|
| 563 |
-
action_taken = "Nenhuma alteração aplicada." # Default message
|
| 564 |
-
try:
|
| 565 |
-
if cleaning_strategy == 'Drop rows with any missing values':
|
| 566 |
-
rows_before = len(df_cleaned)
|
| 567 |
-
df_cleaned.dropna(axis=0, inplace=True)
|
| 568 |
-
rows_after = len(df_cleaned)
|
| 569 |
-
action_taken = f"Removidas {rows_before - rows_after} linhas com valores ausentes."
|
| 570 |
-
elif cleaning_strategy == 'Drop columns with any missing values':
|
| 571 |
-
cols_before = df_cleaned.shape[1]
|
| 572 |
-
df_cleaned.dropna(axis=1, inplace=True)
|
| 573 |
-
cols_after = df_cleaned.shape[1]
|
| 574 |
-
action_taken = f"Removidas {cols_before - cols_after} colunas com valores ausentes."
|
| 575 |
-
|
| 576 |
-
elif cleaning_strategy == 'Impute missing values (Fill)':
|
| 577 |
-
if not cols_to_impute:
|
| 578 |
-
st.warning("Por favor, selecione pelo menos uma coluna para imputação.")
|
| 579 |
-
action_taken = "Nenhuma coluna selecionada para imputação."
|
| 580 |
-
else:
|
| 581 |
-
imputed_cols_count = 0
|
| 582 |
-
skipped_cols = []
|
| 583 |
-
for col in cols_to_impute:
|
| 584 |
-
if col not in df_cleaned.columns or not df_cleaned[col].isnull().any():
|
| 585 |
-
continue # Skip if column doesn't exist or has no NaNs
|
| 586 |
-
|
| 587 |
-
if imputation_method in ['Mean (Numeric Only)', 'Median (Numeric Only)']:
|
| 588 |
-
if pd.api.types.is_numeric_dtype(df_cleaned[col]):
|
| 589 |
-
fill_value = df_cleaned[col].mean() if 'Mean' in imputation_method else df_cleaned[col].median()
|
| 590 |
-
df_cleaned[col].fillna(fill_value, inplace=True)
|
| 591 |
-
imputed_cols_count += 1
|
| 592 |
-
else:
|
| 593 |
-
skipped_cols.append(col)
|
| 594 |
-
elif imputation_method == 'Mode (Most Frequent)':
|
| 595 |
-
mode_val = df_cleaned[col].mode()
|
| 596 |
-
if not mode_val.empty:
|
| 597 |
-
df_cleaned[col].fillna(mode_val[0], inplace=True)
|
| 598 |
-
imputed_cols_count += 1
|
| 599 |
-
else: skipped_cols.append(col) # Handle cases with no mode
|
| 600 |
-
elif imputation_method == 'Constant Value':
|
| 601 |
-
try: # Attempt type conversion for consistency
|
| 602 |
-
typed_value = pd.Series([constant_value]).astype(df_cleaned[col].dtype).iloc[0]
|
| 603 |
-
except Exception:
|
| 604 |
-
typed_value = constant_value # Use raw value if conversion fails
|
| 605 |
-
df_cleaned[col].fillna(typed_value, inplace=True)
|
| 606 |
-
imputed_cols_count += 1
|
| 607 |
-
elif imputation_method == 'Forward Fill (ffill)':
|
| 608 |
-
df_cleaned[col].ffill(inplace=True)
|
| 609 |
-
imputed_cols_count += 1
|
| 610 |
-
elif imputation_method == 'Backward Fill (bfill)':
|
| 611 |
-
df_cleaned[col].bfill(inplace=True)
|
| 612 |
-
imputed_cols_count += 1
|
| 613 |
-
|
| 614 |
-
action_taken = f"Imputed {imputed_cols_count} column(s) using {imputation_method}."
|
| 615 |
-
if skipped_cols:
|
| 616 |
-
st.warning(f"Skipped non-numeric columns for Mean/Median or columns with no mode: {', '.join(skipped_cols)}", icon="⚠️")
|
| 617 |
-
|
| 618 |
-
# Update the main dataframe in session state
|
| 619 |
-
st.session_state.current_df = df_cleaned
|
| 620 |
-
st.success(f"Cleaning applied: {action_taken}")
|
| 621 |
-
logging.info(f"Data cleaning action: {action_taken}")
|
| 622 |
-
st.rerun() # Rerun to update display (summary, preview)
|
| 623 |
-
|
| 624 |
-
except Exception as clean_e:
|
| 625 |
-
st.error(f"Error during cleaning: {clean_e}")
|
| 626 |
-
logging.error(f"Cleaning failed: {clean_e}", exc_info=True)
|
| 627 |
-
else:
|
| 628 |
-
st.info("Select a cleaning strategy and click 'Apply Cleaning'.")
|
| 629 |
-
|
| 630 |
-
|
| 631 |
-
# --- Column Statistics ---
|
| 632 |
-
elif analysis_type == 'Column Statistics':
|
| 633 |
-
st.subheader("Descriptive Statistics (Numeric Columns)")
|
| 634 |
-
numeric_cols = df.select_dtypes(include=np.number).columns.tolist()
|
| 635 |
-
if not numeric_cols:
|
| 636 |
-
st.warning("No numeric columns found in the current dataset.")
|
| 637 |
-
else:
|
| 638 |
-
# ENHANCEMENT: Use spinner for describe() if large
|
| 639 |
-
with st.spinner("Calculating statistics..."):
|
| 640 |
-
stats_df = df[numeric_cols].describe()
|
| 641 |
-
st.dataframe(stats_df, use_container_width=True)
|
| 642 |
-
|
| 643 |
-
# --- Visualizations ---
|
| 644 |
-
elif analysis_type == 'Visualizations':
|
| 645 |
-
st.subheader("Interactive Plots")
|
| 646 |
-
numeric_cols = df.select_dtypes(include=np.number).columns.tolist()
|
| 647 |
-
all_cols = df.columns.tolist()
|
| 648 |
-
|
| 649 |
-
if not numeric_cols:
|
| 650 |
-
st.warning("No numeric columns available for plotting.")
|
| 651 |
-
else:
|
| 652 |
-
# ENHANCEMENT: Use st.tabs for different plot types
|
| 653 |
-
tab1, tab2 = st.tabs(["📊 Histogram", "📈 Scatter Plot"])
|
| 654 |
-
|
| 655 |
-
with tab1:
|
| 656 |
-
st.markdown("#### Histogram")
|
| 657 |
-
hist_col = st.selectbox("Select column:", numeric_cols, key='hist_col_select_tab')
|
| 658 |
-
# ENHANCEMENT: Add optional color dimension
|
| 659 |
-
hist_color = st.selectbox("Optional: Color by:", [None] + all_cols, key='hist_color_select_tab')
|
| 660 |
-
|
| 661 |
-
if hist_col:
|
| 662 |
-
# ENHANCEMENT: Use spinner for plotting
|
| 663 |
-
with st.spinner("Generating Histogram..."):
|
| 664 |
-
try:
|
| 665 |
-
fig_hist = px.histogram(df, x=hist_col, color=hist_color, title=f'Histogram of {hist_col}' + (f' (Colored by {hist_color})' if hist_color else ''))
|
| 666 |
-
fig_hist.update_layout(bargap=0.1)
|
| 667 |
-
st.plotly_chart(fig_hist, use_container_width=True)
|
| 668 |
-
except Exception as e:
|
| 669 |
-
st.error(f"Could not generate histogram: {e}")
|
| 670 |
-
logging.error(f"Histogram error for {hist_col}: {e}", exc_info=True)
|
| 671 |
-
|
| 672 |
-
with tab2:
|
| 673 |
-
st.markdown("#### Scatter Plot")
|
| 674 |
-
if len(numeric_cols) >= 2:
|
| 675 |
-
# FIX: Ensure unique keys for widgets in tabs
|
| 676 |
-
scatter_x = st.selectbox("Select X-axis:", numeric_cols, index=0, key='scatter_x_select_tab')
|
| 677 |
-
# Try to select a different default Y axis
|
| 678 |
-
default_y_index = 1 if len(numeric_cols) > 1 and numeric_cols[1] != scatter_x else 0
|
| 679 |
-
scatter_y = st.selectbox("Select Y-axis:", numeric_cols, index=default_y_index, key='scatter_y_select_tab')
|
| 680 |
-
scatter_color = st.selectbox("Optional: Color by:", [None] + all_cols, key='scatter_color_select_tab')
|
| 681 |
-
scatter_size = st.selectbox("Optional: Size by (Numeric):", [None] + numeric_cols, key='scatter_size_select_tab') # ENHANCEMENT: Size dimension
|
| 682 |
-
|
| 683 |
-
|
| 684 |
-
if scatter_x and scatter_y:
|
| 685 |
-
# ENHANCEMENT: Use spinner for plotting
|
| 686 |
-
with st.spinner("Generating Scatter Plot..."):
|
| 687 |
-
try:
|
| 688 |
-
fig_scatter = px.scatter(df, x=scatter_x, y=scatter_y,
|
| 689 |
-
color=scatter_color, size=scatter_size,
|
| 690 |
-
title=f'Scatter: {scatter_x} vs {scatter_y}' + (f' (Color: {scatter_color})' if scatter_color else '') + (f' (Size: {scatter_size})' if scatter_size else ''))
|
| 691 |
-
st.plotly_chart(fig_scatter, use_container_width=True)
|
| 692 |
-
except Exception as e:
|
| 693 |
-
st.error(f"Could not generate scatter plot: {e}")
|
| 694 |
-
logging.error(f"Scatter plot error ({scatter_x} vs {scatter_y}): {e}", exc_info=True)
|
| 695 |
-
else:
|
| 696 |
-
st.warning("Requires at least two numeric columns for a scatter plot.")
|
| 697 |
-
|
| 698 |
-
# --- Correlation Analysis ---
|
| 699 |
-
elif analysis_type == 'Correlation Analysis':
|
| 700 |
-
st.subheader("Correlation Analysis (Numeric Columns)")
|
| 701 |
-
numeric_cols = df.select_dtypes(include=np.number).columns.tolist()
|
| 702 |
-
|
| 703 |
-
if len(numeric_cols) < 2:
|
| 704 |
-
st.warning("Requires at least two numeric columns to calculate correlations.")
|
| 705 |
-
else:
|
| 706 |
-
# ENHANCEMENT: Use spinner
|
| 707 |
-
with st.spinner("Calculating correlations..."):
|
| 708 |
-
try:
|
| 709 |
-
# FIX: Ensure only numeric calculations if mixed types are suspected
|
| 710 |
-
correlation_matrix = df[numeric_cols].corr(numeric_only=True)
|
| 711 |
-
|
| 712 |
-
# ENHANCEMENT: Use st.tabs for table vs heatmap
|
| 713 |
-
tab1, tab2 = st.tabs(["🔢 Matrix Table", "🔥 Heatmap"])
|
| 714 |
-
|
| 715 |
-
with tab1:
|
| 716 |
-
st.dataframe(correlation_matrix, use_container_width=True)
|
| 717 |
-
|
| 718 |
-
with tab2:
|
| 719 |
-
# ENHANCEMENT: Use spinner for potentially large heatmap render
|
| 720 |
-
with st.spinner("Generating Heatmap..."):
|
| 721 |
-
try:
|
| 722 |
-
fig_corr = px.imshow(
|
| 723 |
-
correlation_matrix,
|
| 724 |
-
text_auto=True, # Show values on heatmap
|
| 725 |
-
aspect="auto",
|
| 726 |
-
color_continuous_scale='RdBu_r', # Red-Blue diverging scale good for correlation
|
| 727 |
-
title='Correlation Heatmap'
|
| 728 |
-
)
|
| 729 |
-
fig_corr.update_layout(coloraxis_colorbar=dict(title='Correlation'))
|
| 730 |
-
st.plotly_chart(fig_corr, use_container_width=True)
|
| 731 |
-
except Exception as e_heatmap:
|
| 732 |
-
st.error(f"Could not generate correlation heatmap: {e_heatmap}")
|
| 733 |
-
logging.error(f"Correlation heatmap error: {e_heatmap}", exc_info=True)
|
| 734 |
-
|
| 735 |
-
except Exception as corr_e:
|
| 736 |
-
st.error(f"Could not calculate correlations. Ensure numeric columns contain valid numbers. Error: {corr_e}")
|
| 737 |
-
logging.error(f"Correlation calculation error: {corr_e}", exc_info=True)
|
| 738 |
-
|
| 739 |
-
# --- Feature Importance Template ---
|
| 740 |
-
elif analysis_type == 'Feature Importance Template':
|
| 741 |
-
st.subheader("🎯 Template Unificado de Feature Importance")
|
| 742 |
-
|
| 743 |
-
# Seção de informações sobre o template unificado
|
| 744 |
-
col1, col2 = st.columns([2, 1])
|
| 745 |
-
|
| 746 |
-
with col1:
|
| 747 |
-
st.markdown("""
|
| 748 |
-
**Como usar o Template Unificado:**
|
| 749 |
-
1. Baixe o template Excel com as **2 features mais importantes** dos datasets UCI e OULAD
|
| 750 |
-
2. Preencha o template com seus dados (incluindo o nome do aluno)
|
| 751 |
-
3. Mantenha a coluna 'resultado_final' com os resultados esperados
|
| 752 |
-
4. Faça upload do template preenchido para análise automática
|
| 753 |
-
|
| 754 |
-
**Vantagens do Template Unificado:**
|
| 755 |
-
- Combina insights de educação tradicional (UCI) e online (OULAD)
|
| 756 |
-
- Inclui campo para nome do aluno para personalização
|
| 757 |
-
- Análise mais abrangente com features diversificadas
|
| 758 |
-
- Template otimizado com apenas as features mais relevantes
|
| 759 |
-
""")
|
| 760 |
-
|
| 761 |
-
with col2:
|
| 762 |
-
st.info("""
|
| 763 |
-
**Template inclui:**
|
| 764 |
-
- Campo para nome do aluno
|
| 765 |
-
- **Top 2 features do UCI**
|
| 766 |
-
- **Top 2 features do OULAD**
|
| 767 |
-
- Coluna de resultado final
|
| 768 |
-
- **Total: 6 colunas organizadas logicamente**
|
| 769 |
-
""")
|
| 770 |
-
|
| 771 |
-
# Gerar e baixar template unificado
|
| 772 |
-
if st.button("📥 Gerar Template Unificado", key='generate_unified_template'):
|
| 773 |
-
with st.spinner("Gerando template unificado..."):
|
| 774 |
-
df_template = gerar_template_unificado()
|
| 775 |
-
|
| 776 |
-
if not df_template.empty:
|
| 777 |
-
st.session_state.template_downloaded = True
|
| 778 |
-
feature_cols = [col for col in df_template.columns if col not in ['nome_aluno', 'resultado_final']]
|
| 779 |
-
st.success(f"Template unificado gerado com sucesso! Inclui {len(feature_cols)} features: {', '.join(feature_cols)}")
|
| 780 |
-
|
| 781 |
-
# Mostrar preview do template
|
| 782 |
-
st.markdown("**Preview do Template Unificado:**")
|
| 783 |
-
st.dataframe(df_template.head(), use_container_width=True)
|
| 784 |
-
|
| 785 |
-
# Botão de download
|
| 786 |
-
excel_data = converter_template_para_excel(df_template)
|
| 787 |
-
if excel_data:
|
| 788 |
-
st.download_button(
|
| 789 |
-
label="⬇️ Baixar Template Excel Unificado",
|
| 790 |
-
data=excel_data,
|
| 791 |
-
file_name="template_unificado_features.xlsx",
|
| 792 |
-
mime="application/vnd.openxmlformats-officedocument.spreadsheetml.sheet",
|
| 793 |
-
key='download_unified_template'
|
| 794 |
-
)
|
| 795 |
-
else:
|
| 796 |
-
st.error("Erro ao gerar template unificado. Verifique se os dados estão carregados.")
|
| 797 |
-
|
| 798 |
-
# Seção de upload do template preenchido
|
| 799 |
-
if st.session_state.template_downloaded:
|
| 800 |
-
st.markdown("---")
|
| 801 |
-
st.subheader("📤 Upload do Template Preenchido")
|
| 802 |
-
|
| 803 |
-
uploaded_template = st.file_uploader(
|
| 804 |
-
"Faça upload do template preenchido:",
|
| 805 |
-
type=['csv', 'xlsx', 'xls'],
|
| 806 |
-
accept_multiple_files=False,
|
| 807 |
-
help="Upload do template Excel preenchido com seus dados",
|
| 808 |
-
key='template_uploader'
|
| 809 |
-
)
|
| 810 |
-
|
| 811 |
-
if uploaded_template is not None:
|
| 812 |
-
try:
|
| 813 |
-
# Carregar dados do template
|
| 814 |
-
if uploaded_template.name.endswith('.csv'):
|
| 815 |
-
df_usuario = pd.read_csv(uploaded_template)
|
| 816 |
-
else:
|
| 817 |
-
df_usuario = pd.read_excel(uploaded_template)
|
| 818 |
-
|
| 819 |
-
# Validar template (não precisa de template de referência para unificado)
|
| 820 |
-
is_valid, validation_msg = validar_template_usuario(df_usuario)
|
| 821 |
-
|
| 822 |
-
if is_valid:
|
| 823 |
-
st.success(f"✅ {validation_msg}")
|
| 824 |
-
st.session_state.user_data_uploaded = df_usuario
|
| 825 |
-
|
| 826 |
-
# Mostrar preview dos dados
|
| 827 |
-
st.markdown("**Preview dos Dados Carregados:**")
|
| 828 |
-
st.dataframe(df_usuario.head(), use_container_width=True)
|
| 829 |
-
|
| 830 |
-
# Botão para executar EDA
|
| 831 |
-
if st.button("🔍 Executar Análise Exploratória", key='run_eda'):
|
| 832 |
-
with st.spinner("Executando análise exploratória..."):
|
| 833 |
-
resultado_eda = realizar_eda_automatica(df_usuario)
|
| 834 |
-
|
| 835 |
-
if resultado_eda:
|
| 836 |
-
st.session_state.eda_results = resultado_eda
|
| 837 |
-
st.success("Análise concluída com sucesso!")
|
| 838 |
-
st.rerun()
|
| 839 |
-
else:
|
| 840 |
-
st.error("Erro na análise exploratória")
|
| 841 |
-
else:
|
| 842 |
-
st.error(f"❌ {validation_msg}")
|
| 843 |
-
|
| 844 |
-
except Exception as e:
|
| 845 |
-
st.error(f"Erro ao processar arquivo: {e}")
|
| 846 |
-
|
| 847 |
-
# Exibir resultados da EDA
|
| 848 |
-
if st.session_state.eda_results and st.session_state.user_data_uploaded is not None:
|
| 849 |
-
st.markdown("---")
|
| 850 |
-
st.subheader("📊 Resultados da Análise Exploratória")
|
| 851 |
-
|
| 852 |
-
df_usuario = st.session_state.user_data_uploaded
|
| 853 |
-
resultado_eda = st.session_state.eda_results
|
| 854 |
-
|
| 855 |
-
# Informações básicas
|
| 856 |
-
col1, col2, col3 = st.columns(3)
|
| 857 |
-
with col1:
|
| 858 |
-
st.metric("Total de Registros", df_usuario.shape[0])
|
| 859 |
-
with col2:
|
| 860 |
-
st.metric("Total de Features", df_usuario.shape[1] - 1) # -1 para excluir target
|
| 861 |
-
with col3:
|
| 862 |
-
problem_type = "Regressão" if resultado_eda.get('is_regression', True) else "Classificação"
|
| 863 |
-
st.metric("Tipo de Problema", problem_type)
|
| 864 |
-
|
| 865 |
-
# Métricas do modelo
|
| 866 |
-
st.markdown("### 📈 Métricas do Modelo")
|
| 867 |
-
metrics = resultado_eda.get('metrics', {})
|
| 868 |
-
|
| 869 |
-
if metrics.get('type') == 'regression':
|
| 870 |
-
col1, col2, col3 = st.columns(3)
|
| 871 |
-
with col1:
|
| 872 |
-
st.metric("MAE", f"{metrics.get('mae', 0):.3f}")
|
| 873 |
-
with col2:
|
| 874 |
-
st.metric("RMSE", f"{metrics.get('rmse', 0):.3f}")
|
| 875 |
-
with col3:
|
| 876 |
-
st.metric("R²", f"{metrics.get('r2', 0):.3f}")
|
| 877 |
-
else:
|
| 878 |
-
col1, col2 = st.columns(2)
|
| 879 |
-
with col1:
|
| 880 |
-
st.metric("Acurácia", f"{metrics.get('accuracy', 0):.3f}")
|
| 881 |
-
with col2:
|
| 882 |
-
st.metric("Tipo", "Classificação")
|
| 883 |
-
|
| 884 |
-
# Gráfico de métricas
|
| 885 |
-
fig_metrics = criar_grafico_metricas_modelo(resultado_eda)
|
| 886 |
-
if fig_metrics:
|
| 887 |
-
st.pyplot(fig_metrics)
|
| 888 |
-
|
| 889 |
-
# Visualizações EDA
|
| 890 |
-
st.markdown("### 📊 Visualizações")
|
| 891 |
-
|
| 892 |
-
# Criar e exibir gráficos
|
| 893 |
-
figuras = criar_graficos_eda_usuario(df_usuario, resultado_eda)
|
| 894 |
-
|
| 895 |
-
if figuras:
|
| 896 |
-
# Usar tabs para organizar os gráficos
|
| 897 |
-
tab_names = [f"Gráfico {i+1}" for i in range(len(figuras))]
|
| 898 |
-
tabs = st.tabs(tab_names)
|
| 899 |
-
|
| 900 |
-
for i, (tab, figura) in enumerate(zip(tabs, figuras)):
|
| 901 |
-
with tab:
|
| 902 |
-
st.pyplot(figura)
|
| 903 |
-
|
| 904 |
-
# Feature importance
|
| 905 |
-
if not resultado_eda.get('feature_importance', pd.DataFrame()).empty:
|
| 906 |
-
st.markdown("### 🎯 Importância das Features")
|
| 907 |
-
df_importance = resultado_eda['feature_importance']
|
| 908 |
-
st.dataframe(df_importance, use_container_width=True)
|
| 909 |
-
|
| 910 |
-
# Botão para baixar resultados
|
| 911 |
-
st.markdown("### 💾 Download dos Resultados")
|
| 912 |
-
csv_data = convert_df_to_csv(df_usuario)
|
| 913 |
-
st.download_button(
|
| 914 |
-
label="⬇️ Baixar Dados Processados (CSV)",
|
| 915 |
-
data=csv_data,
|
| 916 |
-
file_name="dados_processados_eda.csv",
|
| 917 |
-
mime="text/csv",
|
| 918 |
-
key='download_processed_data'
|
| 919 |
-
)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
webapp/pages/4_pygwalker.py
DELETED
|
@@ -1,10 +0,0 @@
|
|
| 1 |
-
import pandas as pd
|
| 2 |
-
import pygwalker as pyg
|
| 3 |
-
from pygwalker.api.streamlit import StreamlitRenderer
|
| 4 |
-
import streamlit as st
|
| 5 |
-
|
| 6 |
-
if "df_uci" in st.session_state:
|
| 7 |
-
df = st.session_state['df_uci']
|
| 8 |
-
walker = pyg.walk(df)
|
| 9 |
-
else:
|
| 10 |
-
st.write("Nenhum dado disponível. Por favor, navegue para a página UCI primeiro.")
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
webapp/pages/5_pandas_profiling.py
DELETED
|
@@ -1,185 +0,0 @@
|
|
| 1 |
-
import pandas as pd
|
| 2 |
-
import pandas_profiling
|
| 3 |
-
import streamlit as st
|
| 4 |
-
from streamlit_pandas_profiling import st_profile_report
|
| 5 |
-
from pathlib import Path
|
| 6 |
-
import os
|
| 7 |
-
import pickle
|
| 8 |
-
import sys
|
| 9 |
-
|
| 10 |
-
# Adicionar o diretório src ao path
|
| 11 |
-
sys.path.append(os.path.join(os.path.dirname(__file__), '..', 'src'))
|
| 12 |
-
|
| 13 |
-
from carregar_dados import carregar_uci_dados, carregar_oulad_dados
|
| 14 |
-
|
| 15 |
-
st.set_page_config(
|
| 16 |
-
page_title="Pandas Profiling - Análise de Dados",
|
| 17 |
-
page_icon="📊",
|
| 18 |
-
layout="wide",
|
| 19 |
-
initial_sidebar_state="expanded",
|
| 20 |
-
)
|
| 21 |
-
|
| 22 |
-
st.title("📊 Pandas Profiling - Análise de Dados")
|
| 23 |
-
st.divider()
|
| 24 |
-
|
| 25 |
-
# Carregar dados se não estiverem no session_state
|
| 26 |
-
if "df_uci" not in st.session_state:
|
| 27 |
-
try:
|
| 28 |
-
with st.spinner("🔄 Carregando dados UCI..."):
|
| 29 |
-
st.session_state.df_uci = carregar_uci_dados()
|
| 30 |
-
except Exception as e:
|
| 31 |
-
st.error(f"Erro ao carregar dados UCI: {e}")
|
| 32 |
-
st.session_state.df_uci = pd.DataFrame()
|
| 33 |
-
|
| 34 |
-
if "df_oulad" not in st.session_state:
|
| 35 |
-
try:
|
| 36 |
-
with st.spinner("🔄 Carregando dados OULAD (pode levar alguns minutos na primeira vez)..."):
|
| 37 |
-
st.session_state.df_oulad = carregar_oulad_dados()
|
| 38 |
-
except Exception as e:
|
| 39 |
-
st.error(f"Erro ao carregar dados OULAD: {e}")
|
| 40 |
-
st.session_state.df_oulad = pd.DataFrame()
|
| 41 |
-
|
| 42 |
-
# Sidebar para seleção do dataset
|
| 43 |
-
with st.sidebar:
|
| 44 |
-
st.markdown("### 📊 Seleção do Dataset")
|
| 45 |
-
|
| 46 |
-
# Caixa de seleção para escolher o dataset
|
| 47 |
-
dataset_options = {
|
| 48 |
-
"📚 UCI Dataset": "df_uci",
|
| 49 |
-
"🌐 OULAD Dataset": "df_oulad"
|
| 50 |
-
}
|
| 51 |
-
|
| 52 |
-
selected_dataset = st.selectbox(
|
| 53 |
-
"Escolha o dataset para análise:",
|
| 54 |
-
options=list(dataset_options.keys()),
|
| 55 |
-
index=0,
|
| 56 |
-
help="Selecione qual dataset você deseja analisar com o Pandas Profiling"
|
| 57 |
-
)
|
| 58 |
-
|
| 59 |
-
st.markdown("---")
|
| 60 |
-
|
| 61 |
-
# Informações sobre o dataset selecionado
|
| 62 |
-
if selected_dataset == "📚 UCI Dataset":
|
| 63 |
-
st.markdown("### 📚 Sobre o Dataset UCI")
|
| 64 |
-
st.markdown("""
|
| 65 |
-
**Dataset UCI:**
|
| 66 |
-
- Escolas públicas portuguesas
|
| 67 |
-
- Dados demográficos e acadêmicos
|
| 68 |
-
- Análise de fatores de sucesso
|
| 69 |
-
- Variáveis: notas, faltas, tempo de estudo, etc.
|
| 70 |
-
""")
|
| 71 |
-
|
| 72 |
-
if not st.session_state.df_uci.empty:
|
| 73 |
-
st.metric("Total de Registros", f"{len(st.session_state.df_uci):,}")
|
| 74 |
-
st.metric("Total de Colunas", f"{len(st.session_state.df_uci.columns)}")
|
| 75 |
-
else:
|
| 76 |
-
st.warning("⚠️ Dados UCI não disponíveis")
|
| 77 |
-
|
| 78 |
-
else: # OULAD Dataset
|
| 79 |
-
st.markdown("### 🌐 Sobre o Dataset OULAD")
|
| 80 |
-
st.markdown("""
|
| 81 |
-
**Dataset OULAD:**
|
| 82 |
-
- Plataforma de aprendizado online
|
| 83 |
-
- Dados de engajamento digital
|
| 84 |
-
- Análise de atividades online
|
| 85 |
-
- Variáveis: cliques, atividades, resultados, etc.
|
| 86 |
-
""")
|
| 87 |
-
|
| 88 |
-
if not st.session_state.df_oulad.empty:
|
| 89 |
-
st.metric("Total de Registros", f"{len(st.session_state.df_oulad):,}")
|
| 90 |
-
st.metric("Total de Colunas", f"{len(st.session_state.df_oulad.columns)}")
|
| 91 |
-
else:
|
| 92 |
-
st.warning("⚠️ Dados OULAD não disponíveis")
|
| 93 |
-
|
| 94 |
-
st.markdown("---")
|
| 95 |
-
st.markdown("### ℹ️ Sobre o Pandas Profiling")
|
| 96 |
-
st.markdown("""
|
| 97 |
-
O Pandas Profiling gera um relatório completo de análise exploratória de dados, incluindo:
|
| 98 |
-
|
| 99 |
-
- **Visão geral** dos dados
|
| 100 |
-
- **Correlações** entre variáveis
|
| 101 |
-
- **Valores ausentes** e duplicados
|
| 102 |
-
- **Distribuições** estatísticas
|
| 103 |
-
- **Avisos** sobre qualidade dos dados
|
| 104 |
-
""")
|
| 105 |
-
|
| 106 |
-
st.markdown("---")
|
| 107 |
-
st.markdown("**Mestrado em Tecnologia Educacional - UFC**")
|
| 108 |
-
|
| 109 |
-
# Obter o dataset selecionado
|
| 110 |
-
df_key = dataset_options[selected_dataset]
|
| 111 |
-
df = st.session_state[df_key]
|
| 112 |
-
|
| 113 |
-
# Verificar se o dataset está disponível
|
| 114 |
-
if df.empty:
|
| 115 |
-
st.error(f"❌ O dataset {selected_dataset} não está disponível. Verifique se os dados foram carregados corretamente.")
|
| 116 |
-
st.stop()
|
| 117 |
-
|
| 118 |
-
# Verificar se o dataset é muito grande e oferecer opção de amostragem
|
| 119 |
-
if len(df) > 10000:
|
| 120 |
-
st.warning(f"⚠️ O dataset {selected_dataset} tem {len(df):,} registros. Para melhor performance, considere usar uma amostra.")
|
| 121 |
-
|
| 122 |
-
col1, col2 = st.columns([1, 1])
|
| 123 |
-
with col1:
|
| 124 |
-
usar_amostra = st.checkbox("Usar amostra de 10.000 registros", value=True)
|
| 125 |
-
with col2:
|
| 126 |
-
if usar_amostra:
|
| 127 |
-
df = df.sample(n=10000, random_state=42)
|
| 128 |
-
st.info(f"📊 Usando amostra de {len(df):,} registros")
|
| 129 |
-
|
| 130 |
-
# Exibir informações básicas do dataset selecionado
|
| 131 |
-
st.markdown(f"### 📋 Informações do Dataset: {selected_dataset}")
|
| 132 |
-
|
| 133 |
-
col1, col2, col3, col4 = st.columns(4)
|
| 134 |
-
with col1:
|
| 135 |
-
st.metric("📊 Total de Registros", f"{len(df):,}")
|
| 136 |
-
with col2:
|
| 137 |
-
st.metric("📋 Total de Colunas", f"{len(df.columns)}")
|
| 138 |
-
with col3:
|
| 139 |
-
st.metric("❌ Valores Ausentes", f"{df.isnull().sum().sum():,}")
|
| 140 |
-
with col4:
|
| 141 |
-
st.metric("🔄 Valores Duplicados", f"{df.duplicated().sum():,}")
|
| 142 |
-
|
| 143 |
-
st.markdown("---")
|
| 144 |
-
|
| 145 |
-
# Gerar e exibir o relatório do Pandas Profiling
|
| 146 |
-
st.markdown("### 🔍 Relatório de Análise Exploratória de Dados")
|
| 147 |
-
|
| 148 |
-
with st.spinner("🔄 Gerando relatório do Pandas Profiling... Isso pode levar alguns minutos para datasets grandes."):
|
| 149 |
-
try:
|
| 150 |
-
# Configurar o relatório do Pandas Profiling
|
| 151 |
-
pr = df.profile_report(
|
| 152 |
-
title=f"Relatório de Análise - {selected_dataset}",
|
| 153 |
-
explorative=True,
|
| 154 |
-
minimal=False,
|
| 155 |
-
progress_bar=True,
|
| 156 |
-
correlations={
|
| 157 |
-
"auto": {"calculate": False},
|
| 158 |
-
"pearson": {"calculate": True},
|
| 159 |
-
"spearman": {"calculate": True},
|
| 160 |
-
"kendall": {"calculate": False}
|
| 161 |
-
}
|
| 162 |
-
)
|
| 163 |
-
|
| 164 |
-
# Exibir o relatório
|
| 165 |
-
st_profile_report(pr)
|
| 166 |
-
|
| 167 |
-
except Exception as e:
|
| 168 |
-
st.error(f"❌ Erro ao gerar relatório do Pandas Profiling: {e}")
|
| 169 |
-
st.markdown("""
|
| 170 |
-
**Possíveis soluções:**
|
| 171 |
-
- Verifique se o dataset não está vazio
|
| 172 |
-
- Tente com um dataset menor
|
| 173 |
-
- Verifique se há problemas de memória
|
| 174 |
-
""")
|
| 175 |
-
|
| 176 |
-
# Informações adicionais
|
| 177 |
-
st.markdown("---")
|
| 178 |
-
st.markdown("### 💡 Dicas de Uso")
|
| 179 |
-
st.markdown("""
|
| 180 |
-
- **Navegue** pelas diferentes seções do relatório usando o menu lateral
|
| 181 |
-
- **Explore** as correlações entre variáveis
|
| 182 |
-
- **Analise** os avisos sobre qualidade dos dados
|
| 183 |
-
- **Use** os filtros interativos para explorar os dados
|
| 184 |
-
- **Exporte** o relatório se necessário usando o botão de download
|
| 185 |
-
""")
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
webapp/src/openai_interpreter.py
CHANGED
|
@@ -140,7 +140,7 @@ def configurar_openai_key():
|
|
| 140 |
Instituto UFC Virtual (IUVI)
|
| 141 |
Universidade Federal do Ceará (UFC)
|
| 142 |
|
| 143 |
-
Versão
|
| 144 |
""")
|
| 145 |
|
| 146 |
def interpretar_grafico(tipo_grafico: str, dados_contexto: Dict[str, Any]) -> str:
|
|
@@ -438,30 +438,18 @@ def criar_sidebar_landpage():
|
|
| 438 |
4. Faça upload para análise
|
| 439 |
""")
|
| 440 |
|
| 441 |
-
# Rodapé
|
| 442 |
-
|
| 443 |
-
st.markdown("### ℹ️ Sobre o Sistema")
|
| 444 |
-
st.caption("""
|
| 445 |
-
**SIDA - Sistema Inteligente de Análise Educacional**
|
| 446 |
-
|
| 447 |
-
Mestrado em Tecnologia Educacional
|
| 448 |
-
Programa de Pós-Graduação em Tecnologias Educacionais (PPGTE)
|
| 449 |
-
Instituto UFC Virtual (IUVI)
|
| 450 |
-
Universidade Federal do Ceará (UFC)
|
| 451 |
-
|
| 452 |
-
Versão 1.0.0 - 2025
|
| 453 |
-
""")
|
| 454 |
|
| 455 |
def criar_sidebar_padrao():
|
| 456 |
-
"""Sidebar padrão para páginas internas (
|
| 457 |
with st.sidebar:
|
| 458 |
st.markdown("### 📊 Navegação")
|
| 459 |
st.markdown("""
|
| 460 |
- 🏠 **Home**: Análise Customizada
|
| 461 |
-
- 📊 **
|
| 462 |
-
- 📈 **
|
| 463 |
-
|
| 464 |
-
- 🔍 **Analisador**: Ferramenta de Análise
|
| 465 |
""")
|
| 466 |
|
| 467 |
st.markdown("---")
|
|
@@ -476,21 +464,10 @@ def criar_sidebar_padrao():
|
|
| 476 |
st.warning("⚠️ Configure na página inicial")
|
| 477 |
|
| 478 |
# Rodapé padrão (mesmo em todas as páginas)
|
| 479 |
-
|
| 480 |
-
st.markdown("### ℹ️ Sobre o Sistema")
|
| 481 |
-
st.caption("""
|
| 482 |
-
**SIDA - Sistema Inteligente de Análise Educacional**
|
| 483 |
-
|
| 484 |
-
Mestrado em Tecnologia Educacional
|
| 485 |
-
Programa de Pós-Graduação em Tecnologias Educacionais (PPGTE)
|
| 486 |
-
Instituto UFC Virtual (IUVI)
|
| 487 |
-
Universidade Federal do Ceará (UFC)
|
| 488 |
-
|
| 489 |
-
Versão 1.0.0 - 2025
|
| 490 |
-
""")
|
| 491 |
|
| 492 |
def criar_rodape_sidebar():
|
| 493 |
-
"""Rodapé padronizado para todas as sidebars"""
|
| 494 |
st.markdown("---")
|
| 495 |
st.markdown("### ℹ️ Sobre o Sistema")
|
| 496 |
st.caption("""
|
|
@@ -501,5 +478,16 @@ def criar_rodape_sidebar():
|
|
| 501 |
Instituto UFC Virtual (IUVI)
|
| 502 |
Universidade Federal do Ceará (UFC)
|
| 503 |
|
| 504 |
-
Versão
|
| 505 |
""")
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 140 |
Instituto UFC Virtual (IUVI)
|
| 141 |
Universidade Federal do Ceará (UFC)
|
| 142 |
|
| 143 |
+
Versão 0.1.1 - 2025
|
| 144 |
""")
|
| 145 |
|
| 146 |
def interpretar_grafico(tipo_grafico: str, dados_contexto: Dict[str, Any]) -> str:
|
|
|
|
| 438 |
4. Faça upload para análise
|
| 439 |
""")
|
| 440 |
|
| 441 |
+
# Rodapé - apenas badges de status
|
| 442 |
+
criar_rodape_sidebar()
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 443 |
|
| 444 |
def criar_sidebar_padrao():
|
| 445 |
+
"""Sidebar padrão para páginas internas (Painel Analítico, Análise Exploratória, etc.)"""
|
| 446 |
with st.sidebar:
|
| 447 |
st.markdown("### 📊 Navegação")
|
| 448 |
st.markdown("""
|
| 449 |
- 🏠 **Home**: Análise Customizada
|
| 450 |
+
- 📊 **Painel Analítico**: Visão Consolidada
|
| 451 |
+
- 📈 **Análise Exploratória**
|
| 452 |
+
- Autosserviço
|
|
|
|
| 453 |
""")
|
| 454 |
|
| 455 |
st.markdown("---")
|
|
|
|
| 464 |
st.warning("⚠️ Configure na página inicial")
|
| 465 |
|
| 466 |
# Rodapé padrão (mesmo em todas as páginas)
|
| 467 |
+
criar_rodape_sidebar()
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 468 |
|
| 469 |
def criar_rodape_sidebar():
|
| 470 |
+
"""Rodapé padronizado para todas as sidebars - informações do sistema e badges"""
|
| 471 |
st.markdown("---")
|
| 472 |
st.markdown("### ℹ️ Sobre o Sistema")
|
| 473 |
st.caption("""
|
|
|
|
| 478 |
Instituto UFC Virtual (IUVI)
|
| 479 |
Universidade Federal do Ceará (UFC)
|
| 480 |
|
| 481 |
+
Versão 0.1.1 - 2025
|
| 482 |
""")
|
| 483 |
+
|
| 484 |
+
# Badges de status do projeto
|
| 485 |
+
st.markdown("""
|
| 486 |
+
<div style="margin-top: 10px; text-align: center;">
|
| 487 |
+
<img src="https://img.shields.io/badge/Python-3.9%2B-blue?logo=python&logoColor=white" alt="Python 3.9+"/>
|
| 488 |
+
<img src="https://img.shields.io/badge/Streamlit-1.28%2B-FF4B4B?logo=streamlit&logoColor=white" alt="Streamlit"/>
|
| 489 |
+
<img src="https://img.shields.io/badge/Docker-Supported-2496ED?logo=docker&logoColor=white" alt="Docker"/>
|
| 490 |
+
<img src="https://img.shields.io/badge/License-GPL--3.0-green" alt="License GPL-3.0"/>
|
| 491 |
+
<img src="https://img.shields.io/badge/Version-0.1.1-orange" alt="Version"/>
|
| 492 |
+
</div>
|
| 493 |
+
""", unsafe_allow_html=True)
|
webapp/src/utilidades.py
CHANGED
|
@@ -28,7 +28,7 @@ def carregar_dados_oulad_cached():
|
|
| 28 |
return carregar_oulad_dados()
|
| 29 |
|
| 30 |
def carregar_dados_dashboard():
|
| 31 |
-
"""Carrega os dados processados para o
|
| 32 |
try:
|
| 33 |
# Carregar dados UCI com cache
|
| 34 |
df_uci = carregar_dados_uci_cached()
|
|
@@ -273,7 +273,7 @@ def calcular_metricas_oulad(df_oulad):
|
|
| 273 |
return metricas
|
| 274 |
|
| 275 |
def gerar_metricas_consolidadas(df_uci, df_oulad):
|
| 276 |
-
"""Gera métricas consolidadas para o
|
| 277 |
metricas_uci = calcular_metricas_uci(df_uci)
|
| 278 |
metricas_oulad = calcular_metricas_oulad(df_oulad)
|
| 279 |
|
|
@@ -292,9 +292,9 @@ def gerar_metricas_consolidadas(df_uci, df_oulad):
|
|
| 292 |
}
|
| 293 |
|
| 294 |
def criar_sidebar_dashboard():
|
| 295 |
-
"""Cria a barra lateral do
|
| 296 |
with st.sidebar:
|
| 297 |
-
st.markdown("### 📊
|
| 298 |
|
| 299 |
# Carregar métricas dinâmicas
|
| 300 |
metricas_uci = obter_metricas_principais_uci()
|
|
@@ -634,7 +634,7 @@ def treinar_modelo_uci_on_demand():
|
|
| 634 |
else:
|
| 635 |
print("🔄 Preparando dados...")
|
| 636 |
|
| 637 |
-
# Preparar dados como na página
|
| 638 |
Y = df_uci['G3']
|
| 639 |
X = df_uci.drop('G3', axis=1)
|
| 640 |
|
|
@@ -743,7 +743,7 @@ def treinar_modelo_oulad_on_demand():
|
|
| 743 |
else:
|
| 744 |
print("🔄 Preparando dados...")
|
| 745 |
|
| 746 |
-
# Preparar dados como na página
|
| 747 |
Y = df_oulad['final_result']
|
| 748 |
X = df_oulad.loc[:, df_oulad.columns != 'final_result']
|
| 749 |
|
|
|
|
| 28 |
return carregar_oulad_dados()
|
| 29 |
|
| 30 |
def carregar_dados_dashboard():
|
| 31 |
+
"""Carrega os dados processados para o painel analítico com cache"""
|
| 32 |
try:
|
| 33 |
# Carregar dados UCI com cache
|
| 34 |
df_uci = carregar_dados_uci_cached()
|
|
|
|
| 273 |
return metricas
|
| 274 |
|
| 275 |
def gerar_metricas_consolidadas(df_uci, df_oulad):
|
| 276 |
+
"""Gera métricas consolidadas para o painel analítico"""
|
| 277 |
metricas_uci = calcular_metricas_uci(df_uci)
|
| 278 |
metricas_oulad = calcular_metricas_oulad(df_oulad)
|
| 279 |
|
|
|
|
| 292 |
}
|
| 293 |
|
| 294 |
def criar_sidebar_dashboard():
|
| 295 |
+
"""Cria a barra lateral do painel analítico"""
|
| 296 |
with st.sidebar:
|
| 297 |
+
st.markdown("### 📊 Painel Analítico")
|
| 298 |
|
| 299 |
# Carregar métricas dinâmicas
|
| 300 |
metricas_uci = obter_metricas_principais_uci()
|
|
|
|
| 634 |
else:
|
| 635 |
print("🔄 Preparando dados...")
|
| 636 |
|
| 637 |
+
# Preparar dados como na página 1_UCI.py
|
| 638 |
Y = df_uci['G3']
|
| 639 |
X = df_uci.drop('G3', axis=1)
|
| 640 |
|
|
|
|
| 743 |
else:
|
| 744 |
print("🔄 Preparando dados...")
|
| 745 |
|
| 746 |
+
# Preparar dados como na página 2_OULAD.py
|
| 747 |
Y = df_oulad['final_result']
|
| 748 |
X = df_oulad.loc[:, df_oulad.columns != 'final_result']
|
| 749 |
|
webapp/uci.pkl
CHANGED
|
@@ -1,3 +1,3 @@
|
|
| 1 |
version https://git-lfs.github.com/spec/v1
|
| 2 |
-
oid sha256:
|
| 3 |
-
size
|
|
|
|
| 1 |
version https://git-lfs.github.com/spec/v1
|
| 2 |
+
oid sha256:e1ad80d80c823f44658a7cec11db12756906e4df85750213e7aceb43920f5edd
|
| 3 |
+
size 3177025
|