bfiguei's picture
Update app.py
d3e8237 verified
"""
Aplicação Streamlit para visualizar dados, treinar modelos
e avaliar a previsão de reclamações de clientes.
Esta app atende aos requisitos do bônus:
- Visualização com filtros dos dados;
- Seleção dinâmica de variáveis para modelagem;
- Exibição das métricas e curvas preditivas;
- Interpretação automática baseada na importância das variáveis.
"""
from typing import List
import matplotlib.pyplot as plt
import pandas as pd
import seaborn as sns
import streamlit as st
from sklearn.metrics import confusion_matrix
from src.utils import (
carregar_dados,
preparar_base,
treinar_modelo,
avaliar_modelo,
calcular_curva_roc,
obter_importancias,
)
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. "
"Verifique se as variáveis selecionadas são adequadas."
)
top_vars: List[str] = df_imp.head(top_n)["Variavel"].tolist()
texto = (
"Com base nas importâncias calculadas, as variáveis mais relevantes para "
"prever reclamações foram: "
+ ", ".join(top_vars)
+ ". Isso sugere que clientes associados a estas variáveis "
"apresentam maior probabilidade de registrar insatisfações e podem ser "
"priorizados em ações de atendimento preventivo."
)
return texto
def rodar_dashboard() -> None:
"""Executa a interface Streamlit do dashboard."""
st.set_page_config(page_title="Tarefa 4 - Reclamações", layout="wide")
st.title("📊 Tarefa 4 – Previsão de Reclamações de Clientes")
st.sidebar.header("Configurações")
st.sidebar.write("Carregue a base `marketing_campaign.csv` para iniciar.")
# Upload do CSV
arquivo = st.sidebar.file_uploader(
"Selecione o arquivo CSV",
type=["csv"],
help="Use a base original marketing_campaign.csv",
)
aplicar_smote = st.sidebar.checkbox(
"Aplicar SMOTE (balancear classes)", value=True
)
nome_modelo = st.sidebar.selectbox(
"Modelo de classificação",
["Regressão Logística", "Random Forest", "LightGBM"],
)
if arquivo is None:
st.info("⬅ Faça o upload do arquivo CSV na barra lateral para começar.")
return
# Carregar dados
try:
df = carregar_dados(arquivo)
except Exception as exc: # noqa: BLE001
st.error(f"Erro ao carregar dados: {exc}")
return
st.subheader("👀 Pré-visualização dos dados")
st.dataframe(df.head())
# Filtros simples
st.subheader("🔍 Filtros básicos")
filtros = {}
col1, col2 = st.columns(2)
if "Marital_Status" in df.columns:
opcoes_ms = ["(Todos)"] + sorted(df["Marital_Status"].dropna().unique().tolist())
escolha_ms = col1.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 = col2.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"Número de linhas após filtros: **{df_filtrado.shape[0]}**")
st.dataframe(df_filtrado.head())
# Preparar base
st.subheader("⚙️ Preparação da base para modelagem")
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
)
st.success("Base preparada com sucesso!")
st.write(f"Total de variáveis após tratamento: **{len(feature_names)}**")
except Exception as exc: # noqa: BLE001
st.error(f"Erro ao preparar a base: {exc}")
return
# Seleção dinâmica de variáveis
st.subheader("🧩 Seleção de variáveis para o modelo")
features_selecionadas = st.multiselect(
"Escolha as variáveis explicativas:",
feature_names,
default=feature_names,
help="Você pode reduzir o modelo selecionando apenas algumas variáveis.",
)
if not features_selecionadas:
st.warning("Selecione ao menos uma variável para treinar o modelo.")
return
indices = [feature_names.index(f) for f in features_selecionadas]
X_train_sel = X_train[:, indices]
X_test_sel = X_test[:, indices]
# Treinamento e avaliação
st.subheader("🤖 Treinamento e avaliação")
if st.button("Treinar modelo e calcular métricas"):
with st.spinner("Treinando modelo e calculando métricas..."):
try:
modelo = treinar_modelo(nome_modelo, X_train_sel, y_train)
except Exception as exc: # noqa: BLE001
st.error(f"Erro ao treinar o modelo: {exc}")
return
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_roc, ax_roc = plt.subplots(figsize=(6, 4))
ax_roc.plot(fpr, tpr, label=f"AUC = {auc:.3f}")
ax_roc.plot([0, 1], [0, 1], linestyle="--", color="gray")
ax_roc.set_xlabel("False Positive Rate")
ax_roc.set_ylabel("True Positive Rate")
ax_roc.set_title("Curva ROC")
ax_roc.legend()
st.pyplot(fig_roc)
# Matriz de confusão
st.write("### 🧮 Matriz de confusão")
y_pred = modelo.predict(X_test_sel)
cm = confusion_matrix(y_test, y_pred)
fig_cm, ax_cm = plt.subplots(figsize=(4, 3))
sns.heatmap(cm, annot=True, fmt="d", cmap="Blues", cbar=False, ax=ax_cm)
ax_cm.set_xlabel("Previsto")
ax_cm.set_ylabel("Real")
st.pyplot(fig_cm)
# Importância das variáveis
st.write("### ⭐ Importância das variáveis")
df_imp = obter_importancias(modelo, features_selecionadas)
st.dataframe(df_imp.head(15))
fig_imp, ax_imp = plt.subplots(figsize=(8, 5))
sns.barplot(
data=df_imp.head(10),
x="Importancia",
y="Variavel",
ax=ax_imp,
)
ax_imp.set_title("Top variáveis mais importantes")
st.pyplot(fig_imp)
# Interpretação automática
st.write("### 🧠 Interpretação automática")
texto_interpretacao = interpretar_importancias(df_imp)
st.write(texto_interpretacao)
if __name__ == "__main__":
rodar_dashboard()