File size: 7,461 Bytes
5eabfae
7fb4944
5eabfae
 
 
7fb4944
 
5eabfae
 
 
 
 
 
 
7fb4944
5eabfae
 
 
 
 
 
 
 
7fb4944
 
 
5eabfae
 
 
 
 
 
 
 
 
 
 
 
 
7fb4944
5eabfae
 
 
7fb4944
5eabfae
 
 
7fb4944
 
5eabfae
 
 
 
7fb4944
5eabfae
 
 
 
7fb4944
5eabfae
 
7fb4944
5eabfae
 
7fb4944
5eabfae
 
7fb4944
5eabfae
 
 
 
 
 
 
7fb4944
5eabfae
 
 
 
7fb4944
5eabfae
 
 
 
7fb4944
5eabfae
 
 
7fb4944
5eabfae
 
7fb4944
5eabfae
 
 
7fb4944
5eabfae
 
 
 
 
7fb4944
5eabfae
 
 
 
 
 
 
7fb4944
5eabfae
 
7fb4944
5eabfae
 
 
 
 
7fb4944
 
5eabfae
 
 
7fb4944
5eabfae
7fb4944
5eabfae
 
 
 
 
 
 
 
7fb4944
5eabfae
 
 
 
 
7fb4944
 
5eabfae
7fb4944
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
5eabfae
 
 
7fb4944
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
"""
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()