Spaces:
Sleeping
Sleeping
| """ | |
| Aplicação Streamlit para visualizar dados, treinar modelos | |
| e avaliar a previsão de reclamações de clientes. | |
| """ | |
| import argparse | |
| import logging | |
| from typing import List | |
| import matplotlib.pyplot as plt | |
| import pandas as pd | |
| import seaborn as sns | |
| import streamlit as st | |
| from utils import ( | |
| carregar_dados, | |
| preparar_base, | |
| treinar_modelo, | |
| avaliar_modelo, | |
| calcular_curva_roc, | |
| obter_importancias, | |
| ) | |
| logging.basicConfig(level=logging.INFO) | |
| logger = logging.getLogger(__name__) | |
| def interpretar_importancias(df_imp: pd.DataFrame, top_n: int = 3) -> str: | |
| """ | |
| Gera um texto simples de interpretação com base nas variáveis mais importantes. | |
| Parâmetros: | |
| df_imp: DataFrame com colunas 'Variavel' e 'Importancia'. | |
| top_n: número de variáveis a destacar. | |
| Retorno: | |
| Texto em português com interpretação básica. | |
| """ | |
| if df_imp.empty: | |
| return "Não foi possível identificar variáveis importantes para este modelo." | |
| top_vars: List[str] = df_imp.head(top_n)["Variavel"].tolist() | |
| texto = ( | |
| "Com base nas importâncias do modelo, as variáveis mais relevantes para " | |
| "prever reclamações foram: " | |
| + ", ".join(top_vars) | |
| + ". Isso indica que clientes com perfis associados a essas variáveis " | |
| "tendem a ter maior probabilidade de registrar insatisfações e devem " | |
| "ser priorizados em ações de atendimento preventivo." | |
| ) | |
| return texto | |
| def executar_dashboard() -> None: | |
| """ | |
| Executa a interface Streamlit do dashboard. | |
| Observação: | |
| Esta função é chamada quando o script é rodado via 'streamlit run'. | |
| """ | |
| st.set_page_config(page_title="Tarefa 4 - Reclamações", layout="wide") | |
| st.title("Dashboard - Previsão de Reclamações de Clientes") | |
| st.sidebar.header("Configurações") | |
| st.sidebar.write("Carregue a base `marketing_campaign.csv` e selecione as opções.") | |
| arquivo = st.sidebar.file_uploader( | |
| "Selecione o arquivo CSV", type=["csv"], help="Use a base marketing_campaign.csv" | |
| ) | |
| aplicar_smote = st.sidebar.checkbox( | |
| "Aplicar SMOTE (balancear classes)", value=True | |
| ) | |
| nome_modelo = st.sidebar.selectbox( | |
| "Escolha o modelo", | |
| ["Regressão Logística", "Random Forest", "LightGBM"], | |
| ) | |
| if arquivo is None: | |
| st.info("Faça o upload do arquivo CSV para iniciar a análise.") | |
| return | |
| try: | |
| df = carregar_dados(arquivo) | |
| except Exception as exc: | |
| st.error(f"Erro ao carregar dados: {exc}") | |
| return | |
| st.subheader("Pré-visualização dos dados") | |
| st.dataframe(df.head()) | |
| # Filtros simples por estado civil e educação, se existirem | |
| filtros = {} | |
| if "Marital_Status" in df.columns: | |
| opcoes_ms = ["(Todos)"] + sorted(df["Marital_Status"].dropna().unique().tolist()) | |
| escolha_ms = st.selectbox("Filtrar por estado civil", opcoes_ms) | |
| if escolha_ms != "(Todos)": | |
| filtros["Marital_Status"] = escolha_ms | |
| if "Education" in df.columns: | |
| opcoes_ed = ["(Todos)"] + sorted(df["Education"].dropna().unique().tolist()) | |
| escolha_ed = st.selectbox("Filtrar por nível educacional", opcoes_ed) | |
| if escolha_ed != "(Todos)": | |
| filtros["Education"] = escolha_ed | |
| df_filtrado = df.copy() | |
| for coluna, valor in filtros.items(): | |
| df_filtrado = df_filtrado[df_filtrado[coluna] == valor] | |
| st.write(f"Linhas após filtros: {df_filtrado.shape[0]}") | |
| st.dataframe(df_filtrado.head()) | |
| # Preparar base (usando df_filtrado se não ficar muito pequeno) | |
| try: | |
| df_para_modelo = df_filtrado if df_filtrado.shape[0] > 50 else df | |
| X_train, X_test, y_train, y_test, feature_names, scaler = preparar_base( | |
| df_para_modelo, aplicar_smote=aplicar_smote | |
| ) | |
| except Exception as exc: | |
| st.error(f"Erro ao preparar dados: {exc}") | |
| return | |
| # Seleção dinâmica de variáveis | |
| st.subheader("Seleção de variáveis") | |
| features_selecionadas = st.multiselect( | |
| "Selecione as variáveis explicativas para o modelo", | |
| feature_names, | |
| default=feature_names, | |
| ) | |
| if not features_selecionadas: | |
| st.warning("Selecione ao menos uma variável para treinar o modelo.") | |
| return | |
| # Precisamos descobrir os índices das features selecionadas | |
| indices = [feature_names.index(f) for f in features_selecionadas] | |
| X_train_sel = X_train[:, indices] | |
| X_test_sel = X_test[:, indices] | |
| # Treinar modelo | |
| st.subheader("Treinamento e avaliação do modelo") | |
| if st.button("Treinar modelo e calcular métricas"): | |
| try: | |
| modelo = treinar_modelo(nome_modelo, X_train_sel, y_train) | |
| except Exception as exc: | |
| st.error(f"Erro ao treinar modelo: {exc}") | |
| return | |
| # Métricas | |
| metricas = avaliar_modelo(modelo, X_test_sel, y_test) | |
| st.write("**Métricas de desempenho:**") | |
| st.table(pd.DataFrame([metricas])) | |
| # Curva ROC | |
| fpr, tpr, auc = calcular_curva_roc(modelo, X_test_sel, y_test) | |
| fig, ax = plt.subplots(figsize=(6, 4)) | |
| ax.plot(fpr, tpr, label=f"AUC = {auc:.3f}") | |
| ax.plot([0, 1], [0, 1], linestyle="--", color="gray") | |
| ax.set_xlabel("False Positive Rate") | |
| ax.set_ylabel("True Positive Rate") | |
| ax.set_title("Curva ROC") | |
| ax.legend() | |
| st.pyplot(fig) | |
| # Matriz de confusão | |
| st.write("**Matriz de confusão:**") | |
| from sklearn.metrics import confusion_matrix | |
| y_pred = modelo.predict(X_test_sel) | |
| cm = confusion_matrix(y_test, y_pred) | |
| fig2, ax2 = plt.subplots(figsize=(4, 3)) | |
| sns.heatmap(cm, annot=True, fmt="d", cmap="Blues", cbar=False, ax=ax2) | |
| ax2.set_xlabel("Previsto") | |
| ax2.set_ylabel("Real") | |
| st.pyplot(fig2) | |
| # Importância de variáveis | |
| df_imp = obter_importancias(modelo, features_selecionadas) | |
| st.write("**Importância das variáveis:**") | |
| st.dataframe(df_imp.head(15)) | |
| fig3, ax3 = plt.subplots(figsize=(8, 5)) | |
| sns.barplot(data=df_imp.head(10), x="Importancia", y="Variavel", ax=ax3) | |
| ax3.set_title("Top variáveis mais importantes") | |
| st.pyplot(fig3) | |
| # Interpretação automática | |
| st.subheader("Interpretação automática") | |
| texto_interpretacao = interpretar_importancias(df_imp) | |
| st.write(texto_interpretacao) | |
| def processar() -> None: | |
| """ | |
| Função principal de processamento para uso via linha de comando. | |
| Observação: | |
| Aqui apenas exibimos uma mensagem orientando a usar o Streamlit. | |
| """ | |
| print( | |
| "Este projeto foi feito para rodar via Streamlit.\n" | |
| "Use o comando:\n" | |
| " streamlit run src/main.py\n" | |
| ) | |
| def main() -> None: | |
| """ | |
| Ponto de entrada para a linha de comando com argparse. | |
| Exemplos: | |
| python -m src.main --modo cli | |
| """ | |
| parser = argparse.ArgumentParser( | |
| description="Dashboard de previsão de reclamações." | |
| ) | |
| parser.add_argument( | |
| "--modo", | |
| type=str, | |
| default="cli", | |
| help="Modo de execução: 'cli' apenas imprime instruções.", | |
| ) | |
| args = parser.parse_args() | |
| if args.modo == "cli": | |
| processar() | |
| else: | |
| print("Erro: modo inválido. Use '--modo cli'.") | |
| if __name__ == "__main__": | |
| main() | |