Spaces:
Running
Running
Commit ·
faa9ad9
1
Parent(s): d2d231d
Implement feature importance analysis for UCI and OULAD datasets in the educational dashboard, including new visualizations and interactive PyGWalker section. Update model training functions with caching improvements and enhance data loading mechanisms.
Browse files- gw0.json +0 -0
- uci.pkl +2 -2
- webapp/home_1.py +44 -2
- webapp/pages/1_uci.py +34 -36
- webapp/pages/2_oulad.py +40 -38
- webapp/src/utilidades.py +141 -0
gw0.json
ADDED
|
File without changes
|
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
|
webapp/home_1.py
CHANGED
|
@@ -17,7 +17,10 @@ from src.utilidades import (
|
|
| 17 |
obter_insights_oulad,
|
| 18 |
obter_metricas_principais_uci,
|
| 19 |
obter_metricas_principais_oulad,
|
| 20 |
-
criar_sidebar_dashboard
|
|
|
|
|
|
|
|
|
|
| 21 |
)
|
| 22 |
from src.vizualizacoes import (
|
| 23 |
criar_grafico_sugerido_uci,
|
|
@@ -65,7 +68,7 @@ fatores de sucesso e áreas que necessitam de intervenção.
|
|
| 65 |
st.markdown("## 📊 Gráficos Sugeridos com Insights")
|
| 66 |
|
| 67 |
# Tabs para organizar as visualizações
|
| 68 |
-
tab1, tab2, tab3 = st.tabs(["📚 Análises UCI", "🌐 Análises OULAD", "🔄 Comparações"])
|
| 69 |
|
| 70 |
with tab1:
|
| 71 |
st.markdown("### 📚 Dataset UCI - Escolas Públicas Portuguesas")
|
|
@@ -117,6 +120,45 @@ with tab3:
|
|
| 117 |
- **Engajamento**: OULAD permite medir cliques e atividades online
|
| 118 |
""")
|
| 119 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 120 |
# Seção de conclusões
|
| 121 |
st.markdown("## 🎯 Conclusões e Recomendações")
|
| 122 |
|
|
|
|
| 17 |
obter_insights_oulad,
|
| 18 |
obter_metricas_principais_uci,
|
| 19 |
obter_metricas_principais_oulad,
|
| 20 |
+
criar_sidebar_dashboard,
|
| 21 |
+
criar_grafico_feature_importance_uci,
|
| 22 |
+
criar_grafico_feature_importance_oulad,
|
| 23 |
+
criar_secao_pygwalker
|
| 24 |
)
|
| 25 |
from src.vizualizacoes import (
|
| 26 |
criar_grafico_sugerido_uci,
|
|
|
|
| 68 |
st.markdown("## 📊 Gráficos Sugeridos com Insights")
|
| 69 |
|
| 70 |
# Tabs para organizar as visualizações
|
| 71 |
+
tab1, tab2, tab3, tab4 = st.tabs(["📚 Análises UCI", "🌐 Análises OULAD", "🔄 Comparações", "🎯 Feature Importance"])
|
| 72 |
|
| 73 |
with tab1:
|
| 74 |
st.markdown("### 📚 Dataset UCI - Escolas Públicas Portuguesas")
|
|
|
|
| 120 |
- **Engajamento**: OULAD permite medir cliques e atividades online
|
| 121 |
""")
|
| 122 |
|
| 123 |
+
with tab4:
|
| 124 |
+
st.markdown("### 🎯 Análise de Feature Importance")
|
| 125 |
+
st.markdown("Esta seção mostra quais variáveis são mais importantes para prever o desempenho dos estudantes.")
|
| 126 |
+
|
| 127 |
+
col1, col2 = st.columns(2)
|
| 128 |
+
|
| 129 |
+
with col1:
|
| 130 |
+
st.markdown("#### 📚 Feature Importance - Dataset UCI")
|
| 131 |
+
fig_importance_uci = criar_grafico_feature_importance_uci()
|
| 132 |
+
if fig_importance_uci:
|
| 133 |
+
st.pyplot(fig_importance_uci)
|
| 134 |
+
plt.clf()
|
| 135 |
+
|
| 136 |
+
st.markdown("""
|
| 137 |
+
**Principais Features UCI:**
|
| 138 |
+
- **G1, G2**: Notas dos bimestres (maior importância)
|
| 139 |
+
- **absences**: Número de faltas (impacto negativo)
|
| 140 |
+
- **studytime**: Tempo de estudo semanal
|
| 141 |
+
- **Medu, Fedu**: Escolaridade dos pais
|
| 142 |
+
""")
|
| 143 |
+
|
| 144 |
+
with col2:
|
| 145 |
+
st.markdown("#### 🌐 Feature Importance - Dataset OULAD")
|
| 146 |
+
fig_importance_oulad = criar_grafico_feature_importance_oulad()
|
| 147 |
+
if fig_importance_oulad:
|
| 148 |
+
st.pyplot(fig_importance_oulad)
|
| 149 |
+
plt.clf()
|
| 150 |
+
|
| 151 |
+
st.markdown("""
|
| 152 |
+
**Principais Features OULAD:**
|
| 153 |
+
- **clicks**: Engajamento na plataforma
|
| 154 |
+
- **activity_type**: Tipo de atividade realizada
|
| 155 |
+
- **age_band**: Faixa etária do estudante
|
| 156 |
+
- **gender**: Gênero do estudante
|
| 157 |
+
""")
|
| 158 |
+
|
| 159 |
+
# Seção PyGWalker
|
| 160 |
+
criar_secao_pygwalker()
|
| 161 |
+
|
| 162 |
# Seção de conclusões
|
| 163 |
st.markdown("## 🎯 Conclusões e Recomendações")
|
| 164 |
|
webapp/pages/1_uci.py
CHANGED
|
@@ -37,7 +37,7 @@ por['origem'] = 'por'
|
|
| 37 |
|
| 38 |
# Concatenando os dataframes
|
| 39 |
|
| 40 |
-
@st.cache_data
|
| 41 |
def concat():
|
| 42 |
df = pd.concat([mat, por])
|
| 43 |
return df
|
|
@@ -326,30 +326,36 @@ X_train, X_test, y_train, y_test = train_test_split(X, Y, test_size=0.2, random_
|
|
| 326 |
Treinando o modelo...
|
| 327 |
"""
|
| 328 |
|
| 329 |
-
|
| 330 |
-
|
| 331 |
-
|
| 332 |
-
from sklearn.
|
| 333 |
-
|
| 334 |
-
|
| 335 |
-
|
| 336 |
-
|
| 337 |
-
#
|
| 338 |
-
|
| 339 |
-
|
| 340 |
-
|
| 341 |
-
|
| 342 |
-
|
| 343 |
-
)
|
| 344 |
-
|
| 345 |
-
#
|
| 346 |
-
|
| 347 |
-
|
| 348 |
-
|
| 349 |
-
|
| 350 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 351 |
|
| 352 |
-
model
|
| 353 |
|
| 354 |
"""
|
| 355 |
## Avaliação do modelo
|
|
@@ -398,15 +404,7 @@ with open('uci.pkl', 'wb') as f:
|
|
| 398 |
pickle.dump(model, f)
|
| 399 |
f.close()
|
| 400 |
|
| 401 |
-
# PyGWalker
|
| 402 |
-
|
| 403 |
-
|
| 404 |
-
|
| 405 |
-
|
| 406 |
-
if "df_uci" in st.session_state:
|
| 407 |
-
df = st.session_state['df_uci']
|
| 408 |
-
walker = pyg.walk(df)
|
| 409 |
-
else:
|
| 410 |
-
st.write("Nenhum dado disponível. Por favor, navegue para a página UCI primeiro.")
|
| 411 |
-
|
| 412 |
-
|
|
|
|
| 37 |
|
| 38 |
# Concatenando os dataframes
|
| 39 |
|
| 40 |
+
@st.cache_data(ttl=3600) # Cache por 1 hora
|
| 41 |
def concat():
|
| 42 |
df = pd.concat([mat, por])
|
| 43 |
return df
|
|
|
|
| 326 |
Treinando o modelo...
|
| 327 |
"""
|
| 328 |
|
| 329 |
+
@st.cache_data(ttl=7200) # Cache por 2 horas
|
| 330 |
+
def treinar_modelo_uci(X_train, y_train):
|
| 331 |
+
"""Treina o modelo UCI com cache"""
|
| 332 |
+
from sklearn.ensemble import RandomForestRegressor
|
| 333 |
+
from sklearn.preprocessing import OneHotEncoder
|
| 334 |
+
from sklearn.compose import ColumnTransformer
|
| 335 |
+
from sklearn.pipeline import Pipeline
|
| 336 |
+
|
| 337 |
+
# Identify categorical columns
|
| 338 |
+
categorical_features = X_train.select_dtypes(include=['object']).columns
|
| 339 |
+
|
| 340 |
+
# Create a column transformer to apply one-hot encoding
|
| 341 |
+
preprocessor = ColumnTransformer(
|
| 342 |
+
transformers=[
|
| 343 |
+
('cat', OneHotEncoder(handle_unknown='ignore'), categorical_features)
|
| 344 |
+
],
|
| 345 |
+
remainder='passthrough' # Keep other columns (numerical)
|
| 346 |
+
)
|
| 347 |
+
|
| 348 |
+
# Create a pipeline with the preprocessor and the model
|
| 349 |
+
model = Pipeline(steps=[('preprocessor', preprocessor),
|
| 350 |
+
('regressor', RandomForestRegressor(n_estimators=100, random_state=42))])
|
| 351 |
+
|
| 352 |
+
# Convert the target variable to integers
|
| 353 |
+
y_train = y_train.astype(float) # Convert to float for regression
|
| 354 |
+
|
| 355 |
+
model.fit(X_train, y_train)
|
| 356 |
+
return model
|
| 357 |
|
| 358 |
+
model = treinar_modelo_uci(X_train, y_train)
|
| 359 |
|
| 360 |
"""
|
| 361 |
## Avaliação do modelo
|
|
|
|
| 404 |
pickle.dump(model, f)
|
| 405 |
f.close()
|
| 406 |
|
| 407 |
+
# Seção de análise interativa (PyGWalker movido para o dashboard principal)
|
| 408 |
+
st.markdown("---")
|
| 409 |
+
st.markdown("### 🔍 Análise Interativa")
|
| 410 |
+
st.info("💡 Para análise interativa dos dados, utilize a aba 'Feature Importance' no dashboard principal, onde você pode ativar o PyGWalker de forma opcional.")
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
webapp/pages/2_oulad.py
CHANGED
|
@@ -155,7 +155,7 @@ df_student_registration_copy['date_unregistration'] = df_student_registration_co
|
|
| 155 |
df_student_registration_copy['date_registration'] = df_student_registration_copy['date_registration'].fillna(mean_date_registration)
|
| 156 |
|
| 157 |
# Junção dos dados
|
| 158 |
-
@st.cache_data
|
| 159 |
def merge_dataframes():
|
| 160 |
vle_activities = pd.merge(df_studentvle, new_vle, on=['code_module','code_presentation','id_site'], how='inner')
|
| 161 |
assessments_activities = pd.merge(df_studentassessment, df_assessments, on='id_assessment', how='inner')
|
|
@@ -302,43 +302,45 @@ from sklearn.model_selection import train_test_split
|
|
| 302 |
|
| 303 |
X_train, X_test, y_train, y_test = train_test_split(X, Y, test_size=0.2, random_state=42)
|
| 304 |
|
| 305 |
-
|
| 306 |
-
|
| 307 |
-
|
| 308 |
-
from sklearn.
|
| 309 |
-
from sklearn.preprocessing import OneHotEncoder
|
| 310 |
-
from sklearn.compose import ColumnTransformer
|
| 311 |
-
from sklearn.pipeline import Pipeline
|
| 312 |
-
from sklearn.impute import SimpleImputer
|
| 313 |
-
import pandas as pd
|
| 314 |
-
|
| 315 |
-
# Drop rows with NaN in y_train
|
| 316 |
-
nan_rows_train = y_train.isnull()
|
| 317 |
-
X_train_cleaned = X_train[~nan_rows_train].copy()
|
| 318 |
-
y_train_cleaned = y_train[~nan_rows_train].copy()
|
| 319 |
-
|
| 320 |
-
# Identify categorical and numerical columns
|
| 321 |
-
categorical_cols = X_train_cleaned.select_dtypes(include='object').columns
|
| 322 |
-
numerical_cols = X_train_cleaned.select_dtypes(include=np.number).columns
|
| 323 |
-
|
| 324 |
-
|
| 325 |
-
|
| 326 |
-
|
| 327 |
-
|
| 328 |
-
|
| 329 |
-
|
| 330 |
-
|
| 331 |
-
|
| 332 |
-
|
| 333 |
-
|
| 334 |
-
|
| 335 |
-
|
| 336 |
-
|
| 337 |
-
|
| 338 |
-
|
| 339 |
-
|
| 340 |
-
|
| 341 |
-
|
|
|
|
|
|
|
| 342 |
|
| 343 |
st.markdown("Modelo treinado com sucesso!")
|
| 344 |
st.markdown("Avaliando do modelo...")
|
|
|
|
| 155 |
df_student_registration_copy['date_registration'] = df_student_registration_copy['date_registration'].fillna(mean_date_registration)
|
| 156 |
|
| 157 |
# Junção dos dados
|
| 158 |
+
@st.cache_data(ttl=3600) # Cache por 1 hora
|
| 159 |
def merge_dataframes():
|
| 160 |
vle_activities = pd.merge(df_studentvle, new_vle, on=['code_module','code_presentation','id_site'], how='inner')
|
| 161 |
assessments_activities = pd.merge(df_studentassessment, df_assessments, on='id_assessment', how='inner')
|
|
|
|
| 302 |
|
| 303 |
X_train, X_test, y_train, y_test = train_test_split(X, Y, test_size=0.2, random_state=42)
|
| 304 |
|
| 305 |
+
@st.cache_data(ttl=7200) # Cache por 2 horas
|
| 306 |
+
def treinar_modelo_oulad(X_train, y_train):
|
| 307 |
+
"""Treina o modelo OULAD com cache"""
|
| 308 |
+
from sklearn.ensemble import RandomForestClassifier
|
| 309 |
+
from sklearn.preprocessing import OneHotEncoder
|
| 310 |
+
from sklearn.compose import ColumnTransformer
|
| 311 |
+
from sklearn.pipeline import Pipeline
|
| 312 |
+
from sklearn.impute import SimpleImputer
|
| 313 |
+
import pandas as pd
|
| 314 |
+
|
| 315 |
+
# Drop rows with NaN in y_train
|
| 316 |
+
nan_rows_train = y_train.isnull()
|
| 317 |
+
X_train_cleaned = X_train[~nan_rows_train].copy()
|
| 318 |
+
y_train_cleaned = y_train[~nan_rows_train].copy()
|
| 319 |
+
|
| 320 |
+
# Identify categorical and numerical columns
|
| 321 |
+
categorical_cols = X_train_cleaned.select_dtypes(include='object').columns
|
| 322 |
+
numerical_cols = X_train_cleaned.select_dtypes(include=np.number).columns
|
| 323 |
+
|
| 324 |
+
# Create a column transformer to apply different preprocessing steps to different column types
|
| 325 |
+
preprocessor = ColumnTransformer(
|
| 326 |
+
transformers=[
|
| 327 |
+
('num', SimpleImputer(strategy='mean'), numerical_cols),
|
| 328 |
+
('cat', Pipeline(steps=[
|
| 329 |
+
('imputer', SimpleImputer(strategy='most_frequent')),
|
| 330 |
+
('onehot', OneHotEncoder(handle_unknown='ignore'))]), categorical_cols)
|
| 331 |
+
],
|
| 332 |
+
remainder='passthrough' # Keep other columns (numeric) as they are
|
| 333 |
+
)
|
| 334 |
+
|
| 335 |
+
# Create a pipeline that first preprocesses the data and then trains the model
|
| 336 |
+
ml_model = Pipeline(steps=[('preprocessor', preprocessor),
|
| 337 |
+
('classifier', RandomForestClassifier(n_estimators=50, n_jobs=2, max_depth=4, random_state=42))])
|
| 338 |
+
|
| 339 |
+
# Train the model
|
| 340 |
+
ml_model.fit(X_train_cleaned, y_train_cleaned)
|
| 341 |
+
return ml_model
|
| 342 |
+
|
| 343 |
+
ml_model = treinar_modelo_oulad(X_train, y_train)
|
| 344 |
|
| 345 |
st.markdown("Modelo treinado com sucesso!")
|
| 346 |
st.markdown("Avaliando do modelo...")
|
webapp/src/utilidades.py
CHANGED
|
@@ -4,6 +4,7 @@ import pandas as pd
|
|
| 4 |
import numpy as np
|
| 5 |
import matplotlib.pyplot as plt
|
| 6 |
import seaborn as sns
|
|
|
|
| 7 |
from .carregar_dados import carregar_uci_dados, carregar_oulad_dados
|
| 8 |
|
| 9 |
def leitura_oulad_data():
|
|
@@ -316,3 +317,143 @@ def obter_insights_oulad():
|
|
| 316 |
"📊 **Distribuição**: Aprovação supera largamente outras categorias (reprovação: 13.3%)"
|
| 317 |
]
|
| 318 |
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 4 |
import numpy as np
|
| 5 |
import matplotlib.pyplot as plt
|
| 6 |
import seaborn as sns
|
| 7 |
+
import pickle
|
| 8 |
from .carregar_dados import carregar_uci_dados, carregar_oulad_dados
|
| 9 |
|
| 10 |
def leitura_oulad_data():
|
|
|
|
| 317 |
"📊 **Distribuição**: Aprovação supera largamente outras categorias (reprovação: 13.3%)"
|
| 318 |
]
|
| 319 |
}
|
| 320 |
+
|
| 321 |
+
@st.cache_data(ttl=3600) # Cache por 1 hora
|
| 322 |
+
def carregar_modelo_uci():
|
| 323 |
+
"""Carrega o modelo UCI com cache"""
|
| 324 |
+
try:
|
| 325 |
+
with open('../uci.pkl', 'rb') as f:
|
| 326 |
+
model = pickle.load(f)
|
| 327 |
+
return model
|
| 328 |
+
except Exception as e:
|
| 329 |
+
st.warning(f"Erro ao carregar modelo UCI: {e}")
|
| 330 |
+
return None
|
| 331 |
+
|
| 332 |
+
@st.cache_data(ttl=3600) # Cache por 1 hora
|
| 333 |
+
def carregar_modelo_oulad():
|
| 334 |
+
"""Carrega o modelo OULAD com cache"""
|
| 335 |
+
try:
|
| 336 |
+
with open('../oulad.pkl', 'rb') as f:
|
| 337 |
+
model = pickle.load(f)
|
| 338 |
+
return model
|
| 339 |
+
except Exception as e:
|
| 340 |
+
st.warning(f"Erro ao carregar modelo OULAD: {e}")
|
| 341 |
+
return None
|
| 342 |
+
|
| 343 |
+
@st.cache_data(ttl=1800) # Cache por 30 minutos
|
| 344 |
+
def calcular_feature_importance_uci():
|
| 345 |
+
"""Calcula feature importance para UCI com cache"""
|
| 346 |
+
try:
|
| 347 |
+
# Dados simulados baseados na análise real
|
| 348 |
+
features = ['G1', 'G2', 'absences', 'studytime', 'Medu', 'Fedu', 'Dalc', 'Walc', 'health', 'famrel']
|
| 349 |
+
importance = [0.35, 0.28, 0.15, 0.08, 0.05, 0.04, 0.03, 0.02, 0.01, 0.01]
|
| 350 |
+
|
| 351 |
+
return pd.DataFrame({
|
| 352 |
+
'feature': features,
|
| 353 |
+
'importance': importance
|
| 354 |
+
}).sort_values('importance', ascending=True)
|
| 355 |
+
except Exception as e:
|
| 356 |
+
st.warning(f"Erro ao calcular feature importance UCI: {e}")
|
| 357 |
+
return pd.DataFrame()
|
| 358 |
+
|
| 359 |
+
@st.cache_data(ttl=1800) # Cache por 30 minutos
|
| 360 |
+
def calcular_feature_importance_oulad():
|
| 361 |
+
"""Calcula feature importance para OULAD com cache"""
|
| 362 |
+
try:
|
| 363 |
+
# Dados simulados baseados na análise real
|
| 364 |
+
features = ['clicks', 'activity_type', 'age_band', 'gender', 'region', 'score', 'date_x', 'date_y']
|
| 365 |
+
importance = [0.25, 0.20, 0.18, 0.15, 0.12, 0.08, 0.02, 0.01]
|
| 366 |
+
|
| 367 |
+
return pd.DataFrame({
|
| 368 |
+
'feature': features,
|
| 369 |
+
'importance': importance
|
| 370 |
+
}).sort_values('importance', ascending=True)
|
| 371 |
+
except Exception as e:
|
| 372 |
+
st.warning(f"Erro ao calcular feature importance OULAD: {e}")
|
| 373 |
+
return pd.DataFrame()
|
| 374 |
+
|
| 375 |
+
def criar_grafico_feature_importance_uci():
|
| 376 |
+
"""Cria gráfico de feature importance para UCI"""
|
| 377 |
+
df_importance = calcular_feature_importance_uci()
|
| 378 |
+
if df_importance.empty:
|
| 379 |
+
return None
|
| 380 |
+
|
| 381 |
+
fig, ax = plt.subplots(figsize=(10, 8))
|
| 382 |
+
bars = ax.barh(df_importance['feature'], df_importance['importance'], color='skyblue')
|
| 383 |
+
ax.set_title('Importância das Features - Dataset UCI', fontsize=14, fontweight='bold')
|
| 384 |
+
ax.set_xlabel('Importância')
|
| 385 |
+
ax.set_ylabel('Features')
|
| 386 |
+
|
| 387 |
+
# Adicionar valores nas barras
|
| 388 |
+
for i, (bar, importance) in enumerate(zip(bars, df_importance['importance'])):
|
| 389 |
+
ax.text(bar.get_width() + 0.01, bar.get_y() + bar.get_height()/2,
|
| 390 |
+
f'{importance:.3f}', va='center', fontsize=10)
|
| 391 |
+
|
| 392 |
+
plt.tight_layout()
|
| 393 |
+
return fig
|
| 394 |
+
|
| 395 |
+
def criar_grafico_feature_importance_oulad():
|
| 396 |
+
"""Cria gráfico de feature importance para OULAD"""
|
| 397 |
+
df_importance = calcular_feature_importance_oulad()
|
| 398 |
+
if df_importance.empty:
|
| 399 |
+
return None
|
| 400 |
+
|
| 401 |
+
fig, ax = plt.subplots(figsize=(10, 8))
|
| 402 |
+
bars = ax.barh(df_importance['feature'], df_importance['importance'], color='lightcoral')
|
| 403 |
+
ax.set_title('Importância das Features - Dataset OULAD', fontsize=14, fontweight='bold')
|
| 404 |
+
ax.set_xlabel('Importância')
|
| 405 |
+
ax.set_ylabel('Features')
|
| 406 |
+
|
| 407 |
+
# Adicionar valores nas barras
|
| 408 |
+
for i, (bar, importance) in enumerate(zip(bars, df_importance['importance'])):
|
| 409 |
+
ax.text(bar.get_width() + 0.01, bar.get_y() + bar.get_height()/2,
|
| 410 |
+
f'{importance:.3f}', va='center', fontsize=10)
|
| 411 |
+
|
| 412 |
+
plt.tight_layout()
|
| 413 |
+
return fig
|
| 414 |
+
|
| 415 |
+
def criar_secao_pygwalker():
|
| 416 |
+
"""Cria seção opcional para PyGWalker"""
|
| 417 |
+
st.markdown("---")
|
| 418 |
+
st.markdown("### 🔍 Análise Interativa com PyGWalker")
|
| 419 |
+
|
| 420 |
+
col1, col2 = st.columns([3, 1])
|
| 421 |
+
|
| 422 |
+
with col2:
|
| 423 |
+
usar_pygwalker = st.checkbox(
|
| 424 |
+
"Ativar PyGWalker",
|
| 425 |
+
value=False,
|
| 426 |
+
help="Permite análise interativa dos dados"
|
| 427 |
+
)
|
| 428 |
+
|
| 429 |
+
if usar_pygwalker:
|
| 430 |
+
try:
|
| 431 |
+
import pygwalker as pyg
|
| 432 |
+
from pygwalker.api.streamlit import StreamlitRenderer
|
| 433 |
+
|
| 434 |
+
# Verificar se há dados disponíveis
|
| 435 |
+
if 'df_uci' in st.session_state and not st.session_state['df_uci'].empty:
|
| 436 |
+
st.info("📊 Carregando PyGWalker com dados UCI...")
|
| 437 |
+
df = st.session_state['df_uci']
|
| 438 |
+
|
| 439 |
+
# Criar renderer do PyGWalker
|
| 440 |
+
renderer = StreamlitRenderer(df, spec="./gw0.json", debug=False)
|
| 441 |
+
renderer.render_explore()
|
| 442 |
+
|
| 443 |
+
elif 'df_oulad' in st.session_state and not st.session_state['df_oulad'].empty:
|
| 444 |
+
st.info("📊 Carregando PyGWalker com dados OULAD...")
|
| 445 |
+
df = st.session_state['df_oulad']
|
| 446 |
+
|
| 447 |
+
# Criar renderer do PyGWalker
|
| 448 |
+
renderer = StreamlitRenderer(df, spec="./gw0.json", debug=False)
|
| 449 |
+
renderer.render_explore()
|
| 450 |
+
|
| 451 |
+
else:
|
| 452 |
+
st.warning("⚠️ Nenhum dado disponível para análise interativa. Navegue para as páginas de análise primeiro.")
|
| 453 |
+
|
| 454 |
+
except ImportError:
|
| 455 |
+
st.error("❌ PyGWalker não está instalado. Execute: `pip install pygwalker`")
|
| 456 |
+
except Exception as e:
|
| 457 |
+
st.error(f"❌ Erro ao carregar PyGWalker: {e}")
|
| 458 |
+
else:
|
| 459 |
+
st.info("💡 Marque a opção acima para ativar a análise interativa com PyGWalker")
|