Spaces:
Sleeping
Sleeping
Update model_utils.py
Browse fileslinha com erro remoção
- model_utils.py +30 -44
model_utils.py
CHANGED
|
@@ -271,7 +271,7 @@ class ChurnModelPipeline:
|
|
| 271 |
latex_story.append(NoEscape(sample_display_df.to_latex(index=False, caption='Características do Cliente Simulado', label='tab:sim_customer', longtable=False)))
|
| 272 |
|
| 273 |
markdown_story.append(f"**Resultado da Simulação:** O cliente **{churn_status_sample}** (Probabilidade de Churn: **{prob_sample:.2%}**)\n")
|
| 274 |
-
# Corrigido
|
| 275 |
latex_story.append(NoEscape(f'\textbf{{Resultado da Simulação:}} O cliente \textbf{{{churn_status_sample}}} (Probabilidade de Churn: \textbf{{{prob_sample:.2f}\%}})\n\n'))
|
| 276 |
else:
|
| 277 |
markdown_story.append("Não foi possível realizar uma simulação pois o DataFrame de teste ou dados interativos não estão disponíveis.\n")
|
|
@@ -307,9 +307,9 @@ class ChurnModelPipeline:
|
|
| 307 |
training_details_latex += fr'\item \textbf{{Dataset Carregado:}} {self.training_details.get("dataset_rows", "N/A")} linhas' + '\n'
|
| 308 |
training_details_latex += fr'\item \textbf{{Features Preditivas:}} \texttt{{{", ".join(self.training_details.get("predictor_features", ["N/A"]))}}}.' + '\n'
|
| 309 |
training_details_latex += fr'\item \textbf{{Coluna Alvo:}} \texttt{{{self.training_details.get("target_column", "N/A")}}}.' + '\n'
|
| 310 |
-
training_details_latex += fr'\item \textbf{{Shape $X_{{train}}$ (antes pré-processamento):}} {self.training_details.get("X_train_shape", "N/A")}.' + '\n' # $X_{train}$ corrigido
|
| 311 |
training_details_latex += fr'\item \textbf{{Balanceamento \texttt{{Exited}} (antes SMOTE):}} Não Churn: {y_train_before_smote.get(0, "N/A")}, Churn: {y_train_before_smote.get(1, "N/A")}.' + '\n'
|
| 312 |
-
training_details_latex += fr'\item \textbf{{Shape $X_{{train}}$ (após pré-processamento):}} {self.training_details.get("X_train_processed_shape", "N/A")}.' + '\n' # $X_{train}$ corrigido
|
| 313 |
training_details_latex += fr'\item \textbf{{Balanceamento \texttt{{Exited}} (após SMOTE):}} Não Churn: {y_train_after_smote.get(0, "N/A")}, Churn: {y_train_after_smote.get(1, "N/A")}.' + '\n'
|
| 314 |
training_details_latex += fr'\item \textbf{{Modelo Treinado:}} {"Sim" if self.training_details.get("model_trained_successfully", False) else "Não"}.' + '\n'
|
| 315 |
training_details_latex += r'\end{itemize}' + '\n\n'
|
|
@@ -337,7 +337,7 @@ class ChurnModelPipeline:
|
|
| 337 |
latex_story.append(NoEscape(r'\end{itemize}' + '\n'))
|
| 338 |
|
| 339 |
markdown_story.append("- **Balanceamento de Classes (SMOTE):** O conjunto de dados original apresentava desbalanceamento significativo na variável alvo (`Exited`). O algoritmo SMOTE (Synthetic Minority Over-sampling Technique) foi aplicado para gerar amostras sintéticas da classe minoritária (clientes que saem), garantindo que o modelo não seja viesado para a classe majoritária (clientes que permanecem).\n")
|
| 340 |
-
latex_story.append(NoEscape(r'\item \textbf{Balanceamento de Classes (SMOTE):} O conjunto de dados original apresentava desbalanceamento significativo na variável alvo (\texttt{Exited}). O algoritmo SMOTE (Synthetic Minority Over-sampling Technique) foi aplicado para gerar amostras sintéticas da classe minoritária (clientes que saem), garantindo que o modelo não seja viesado para a classe majoritária (clientes que permanecem
|
| 341 |
|
| 342 |
markdown_story.append("- **Regularização (L2):** A Regressão Logística foi configurada com um parâmetro `C=0.1` (inverso da força de regularização), que aplica regularização L2. Isso ajuda a prevenir o overfitting, penalizando coeficientes grandes e promovendo um modelo mais generalizável.\n")
|
| 343 |
latex_story.append(NoEscape(r'\item \textbf{Regularização (L2):} A Regressão Logística foi configurada com um parâmetro \texttt{C=0.1} (inverso da força de regularização), que aplica regularização L2. Isso ajuda a prevenir o overfitting, penalizando coeficientes grandes e promovendo um modelo mais generalizável.' + '\n'))
|
|
@@ -401,19 +401,19 @@ class ChurnModelPipeline:
|
|
| 401 |
latex_story.append(NoEscape(r'\begin{itemize}' + '\n'))
|
| 402 |
|
| 403 |
markdown_story.append(f"- **Acurácia ({self.metrics_dict.get('Acurácia', 0):.2%}):** Proporção de previsões corretas (tanto churn quanto não-churn) em relação ao total. Indica a precisão geral do modelo. Um valor de {self.metrics_dict.get('Acurácia', 0):.2%} significa que o modelo acertou essa porcentagem das vezes no conjunto de teste.\n")
|
| 404 |
-
latex_story.append(NoEscape(fr'\item \textbf{{Acurácia ({self.metrics_dict.get("Acurácia", 0):.2f}\%)
|
| 405 |
|
| 406 |
markdown_story.append(f"- **AUC ROC ({self.metrics_dict.get('AUC ROC', 0):.4f}):** A Área sob a Curva Característica de Operação do Receptor mede a capacidade do modelo de distinguir entre as classes. Um valor de 0.5 indica desempenho aleatório, enquanto 1.0 indica um classificador perfeito. Seu modelo obteve um AUC de **{self.metrics_dict.get('AUC ROC', 0):.4f}**.\n")
|
| 407 |
-
latex_story.append(NoEscape(fr'\item \textbf{{AUC ROC ({self.metrics_dict.get("AUC ROC", 0):.4f})
|
| 408 |
|
| 409 |
markdown_story.append(f"- **Precisão ({self.metrics_dict.get('Precisão', 0):.2%}):** Das previsões de churn (`1`), quantos realmente foram churn. É importante para o banco não abordar clientes que não iriam dar churn (reduzir falsos positivos). Um valor de {self.metrics_dict.get('Precisão', 0):.2%} significa que das vezes que o modelo previu churn, essa porcentagem estava correta.\n")
|
| 410 |
-
latex_story.append(NoEscape(fr'\item \textbf{{Precisão ({self.metrics_dict.get("Precisão", 0):.2f}\%)
|
| 411 |
|
| 412 |
markdown_story.append(f"- **Recall (Sensibilidade) ({self.metrics_dict.get('Recall (Sensibilidade)', 0):.2%}):** Dos clientes que realmente deram churn (`1`), quantos o modelo identificou. É crucial para o banco identificar o máximo de clientes em risco (reduzir falsos negativos). Um valor de {self.metrics_dict.get('Recall (Sensibilidade)', 0):.2%} significa que essa porcentagem de clientes que de fato deram churn foi corretamente identificada pelo modelo.\n")
|
| 413 |
-
latex_story.append(NoEscape(fr'\item \textbf{{Recall (Sensibilidade) ({self.metrics_dict.get("Recall (Sensibilidade)", 0):.2f}\%)
|
| 414 |
|
| 415 |
markdown_story.append(f"- **F1-Score ({self.metrics_dict.get('F1-Score', 0):.4f}):** É a média harmônica entre Precisão e Recall, útil quando há um desequilíbrio de classes e você precisa de um balanço entre identificar corretamente e não levantar falsos alarmes.\n")
|
| 416 |
-
latex_story.append(NoEscape(fr'\item \textbf{{F1-Score ({self.metrics_dict.get("F1-Score", 0):.4f})
|
| 417 |
|
| 418 |
latex_story.append(NoEscape(r'\end{itemize}' + '\n\n'))
|
| 419 |
else:
|
|
@@ -442,12 +442,6 @@ class ChurnModelPipeline:
|
|
| 442 |
doc.append(Command('geometry', 'margin=1in')) # Margens de 1 polegada
|
| 443 |
doc.append(Command('graphicspath', NoEscape(r'{./}'))) # Para imagens no mesmo diretório
|
| 444 |
|
| 445 |
-
# --- Cabeçalho Personalizado (com base nas informações do usuário) ---
|
| 446 |
-
# Removendo title, author, date pois o titlepage vai sobrescrevê-los
|
| 447 |
-
# doc.append(NoEscape(r'\title{MODELAGEM PREDITIVA DE CHURN DE CLIENTES BANCÁRIOS UTILIZANDO REGRESSÃO LOGÍSTICA}'))
|
| 448 |
-
# doc.append(NoEscape(r'\author{ÉDER MARCELO PONTES CUNHA}'))
|
| 449 |
-
# doc.append(NoEscape(r'\date{26 de Outubro de 2025}')) # Ajuste conforme necessário
|
| 450 |
-
|
| 451 |
doc.append(NoEscape(r'\begin{titlepage}'))
|
| 452 |
doc.append(Command('centering'))
|
| 453 |
|
|
@@ -477,39 +471,32 @@ class ChurnModelPipeline:
|
|
| 477 |
doc.append(Command('vspace', '1.0cm'))
|
| 478 |
|
| 479 |
# Título do Trabalho (do usuário, ajustado para LaTeX)
|
| 480 |
-
#
|
| 481 |
-
#
|
| 482 |
-
# AQUI FOI O ERRO DE SYNTAX. Deve ser assim:
|
| 483 |
-
title_parts_raw = header_info["titulo_trabalho"].replace(' UTILIZANDO ', r'\ \large ').split(r'\')
|
| 484 |
-
doc.append(Command('Huge'))
|
| 485 |
-
doc.append(Command('textbf', NoEscape(title_parts_raw[0]))) # Primeira parte do título
|
| 486 |
-
|
| 487 |
-
# As partes restantes são separadas por `\` que adicionamos
|
| 488 |
-
# Iterar sobre as partes restantes e adicionar com quebra de linha
|
| 489 |
-
# A lógica de split mudou para apenas quebrar em ' ' e adicionar o comando LaTeX manualmente
|
| 490 |
-
title_words = header_info["titulo_trabalho"].split()
|
| 491 |
latex_title_lines = []
|
| 492 |
-
|
| 493 |
-
|
|
|
|
|
|
|
| 494 |
if word == 'UTILIZANDO':
|
| 495 |
-
if
|
| 496 |
-
latex_title_lines.append(" ".join(
|
| 497 |
-
|
| 498 |
-
latex_title_lines.append(r'\ \large UTILIZANDO') #
|
| 499 |
else:
|
| 500 |
-
|
| 501 |
-
if
|
| 502 |
-
latex_title_lines.append(" ".join(
|
| 503 |
|
| 504 |
doc.append(Command('Huge'))
|
| 505 |
-
|
| 506 |
-
|
| 507 |
-
|
| 508 |
-
|
| 509 |
-
|
| 510 |
-
|
| 511 |
-
|
| 512 |
-
|
| 513 |
|
| 514 |
doc.append(Command('vspace', '1.0cm'))
|
| 515 |
|
|
@@ -532,7 +519,6 @@ class ChurnModelPipeline:
|
|
| 532 |
doc.append(Command('vfill')) # Empurra o conteúdo para cima
|
| 533 |
doc.append(Command('end{titlepage}'))
|
| 534 |
|
| 535 |
-
# doc.append(Command('maketitle')) # Não precisamos de maketitle pois usamos titlepage
|
| 536 |
doc.append(Command('clearpage'))
|
| 537 |
doc.append(Command('tableofcontents')) # Sumário
|
| 538 |
doc.append(Command('clearpage'))
|
|
|
|
| 271 |
latex_story.append(NoEscape(sample_display_df.to_latex(index=False, caption='Características do Cliente Simulado', label='tab:sim_customer', longtable=False)))
|
| 272 |
|
| 273 |
markdown_story.append(f"**Resultado da Simulação:** O cliente **{churn_status_sample}** (Probabilidade de Churn: **{prob_sample:.2%}**)\n")
|
| 274 |
+
# Corrigido: 'prob_sample:.2f}\%' foi para '{prob_sample:.2f}\%\'
|
| 275 |
latex_story.append(NoEscape(f'\textbf{{Resultado da Simulação:}} O cliente \textbf{{{churn_status_sample}}} (Probabilidade de Churn: \textbf{{{prob_sample:.2f}\%}})\n\n'))
|
| 276 |
else:
|
| 277 |
markdown_story.append("Não foi possível realizar uma simulação pois o DataFrame de teste ou dados interativos não estão disponíveis.\n")
|
|
|
|
| 307 |
training_details_latex += fr'\item \textbf{{Dataset Carregado:}} {self.training_details.get("dataset_rows", "N/A")} linhas' + '\n'
|
| 308 |
training_details_latex += fr'\item \textbf{{Features Preditivas:}} \texttt{{{", ".join(self.training_details.get("predictor_features", ["N/A"]))}}}.' + '\n'
|
| 309 |
training_details_latex += fr'\item \textbf{{Coluna Alvo:}} \texttt{{{self.training_details.get("target_column", "N/A")}}}.' + '\n'
|
| 310 |
+
training_details_latex += fr'\item \textbf{{Shape $X_{{\text{train}}}$ (antes pré-processamento):}} {self.training_details.get("X_train_shape", "N/A")}.' + '\n' # $X_{\text{train}}$ corrigido para LaTeX
|
| 311 |
training_details_latex += fr'\item \textbf{{Balanceamento \texttt{{Exited}} (antes SMOTE):}} Não Churn: {y_train_before_smote.get(0, "N/A")}, Churn: {y_train_before_smote.get(1, "N/A")}.' + '\n'
|
| 312 |
+
training_details_latex += fr'\item \textbf{{Shape $X_{{\text{train}}}$ (após pré-processamento):}} {self.training_details.get("X_train_processed_shape", "N/A")}.' + '\n' # $X_{\text{train}}$ corrigido para LaTeX
|
| 313 |
training_details_latex += fr'\item \textbf{{Balanceamento \texttt{{Exited}} (após SMOTE):}} Não Churn: {y_train_after_smote.get(0, "N/A")}, Churn: {y_train_after_smote.get(1, "N/A")}.' + '\n'
|
| 314 |
training_details_latex += fr'\item \textbf{{Modelo Treinado:}} {"Sim" if self.training_details.get("model_trained_successfully", False) else "Não"}.' + '\n'
|
| 315 |
training_details_latex += r'\end{itemize}' + '\n\n'
|
|
|
|
| 337 |
latex_story.append(NoEscape(r'\end{itemize}' + '\n'))
|
| 338 |
|
| 339 |
markdown_story.append("- **Balanceamento de Classes (SMOTE):** O conjunto de dados original apresentava desbalanceamento significativo na variável alvo (`Exited`). O algoritmo SMOTE (Synthetic Minority Over-sampling Technique) foi aplicado para gerar amostras sintéticas da classe minoritária (clientes que saem), garantindo que o modelo não seja viesado para a classe majoritária (clientes que permanecem).\n")
|
| 340 |
+
latex_story.append(NoEscape(r'\item \textbf{Balanceamento de Classes (SMOTE):} O conjunto de dados original apresentava desbalanceamento significativo na variável alvo (\texttt{Exited}). O algoritmo SMOTE (Synthetic Minority Over-sampling Technique) foi aplicado para gerar amostras sintéticas da classe minoritária (clientes que saem), garantindo que o modelo não seja viesado para a classe majoritária (clientes que permanecem}.' + '\n')) # Corrigido fechamento de chave.
|
| 341 |
|
| 342 |
markdown_story.append("- **Regularização (L2):** A Regressão Logística foi configurada com um parâmetro `C=0.1` (inverso da força de regularização), que aplica regularização L2. Isso ajuda a prevenir o overfitting, penalizando coeficientes grandes e promovendo um modelo mais generalizável.\n")
|
| 343 |
latex_story.append(NoEscape(r'\item \textbf{Regularização (L2):} A Regressão Logística foi configurada com um parâmetro \texttt{C=0.1} (inverso da força de regularização), que aplica regularização L2. Isso ajuda a prevenir o overfitting, penalizando coeficientes grandes e promovendo um modelo mais generalizável.' + '\n'))
|
|
|
|
| 401 |
latex_story.append(NoEscape(r'\begin{itemize}' + '\n'))
|
| 402 |
|
| 403 |
markdown_story.append(f"- **Acurácia ({self.metrics_dict.get('Acurácia', 0):.2%}):** Proporção de previsões corretas (tanto churn quanto não-churn) em relação ao total. Indica a precisão geral do modelo. Um valor de {self.metrics_dict.get('Acurácia', 0):.2%} significa que o modelo acertou essa porcentagem das vezes no conjunto de teste.\n")
|
| 404 |
+
latex_story.append(NoEscape(fr'\item \textbf{{Acurácia ({self.metrics_dict.get("Acurácia", 0):.2f}\%)}}: Proporção de previsões corretas (tanto churn quanto não-churn) em relação ao total. Indica a precisão geral do modelo. Um valor de {self.metrics_dict.get("Acurácia", 0):.2f}\% significa que o modelo acertou essa porcentagem das vezes no conjunto de teste.' + '\n'))
|
| 405 |
|
| 406 |
markdown_story.append(f"- **AUC ROC ({self.metrics_dict.get('AUC ROC', 0):.4f}):** A Área sob a Curva Característica de Operação do Receptor mede a capacidade do modelo de distinguir entre as classes. Um valor de 0.5 indica desempenho aleatório, enquanto 1.0 indica um classificador perfeito. Seu modelo obteve um AUC de **{self.metrics_dict.get('AUC ROC', 0):.4f}**.\n")
|
| 407 |
+
latex_story.append(NoEscape(fr'\item \textbf{{AUC ROC ({self.metrics_dict.get("AUC ROC", 0):.4f})}}: A Área sob a Curva Característica de Operação do Receptor mede a capacidade do modelo de distinguir entre as classes. Um valor de 0.5 indica desempenho aleatório, enquanto 1.0 indica um classificador perfeito. Seu modelo obteve um AUC de \textbf{{{self.metrics_dict.get("AUC ROC", 0):.4f}}}.' + '\n'))
|
| 408 |
|
| 409 |
markdown_story.append(f"- **Precisão ({self.metrics_dict.get('Precisão', 0):.2%}):** Das previsões de churn (`1`), quantos realmente foram churn. É importante para o banco não abordar clientes que não iriam dar churn (reduzir falsos positivos). Um valor de {self.metrics_dict.get('Precisão', 0):.2%} significa que das vezes que o modelo previu churn, essa porcentagem estava correta.\n")
|
| 410 |
+
latex_story.append(NoEscape(fr'\item \textbf{{Precisão ({self.metrics_dict.get("Precisão", 0):.2f}\%)}}: Das previsões de churn (\texttt{1}), quantos realmente foram churn. É importante para o banco não abordar clientes que não iriam dar churn (reduzir falsos positivos). Um valor de {self.metrics_dict.get("Precisão", 0):.2f}\% significa que das vezes que o modelo previu churn, essa porcentagem estava correta.' + '\n'))
|
| 411 |
|
| 412 |
markdown_story.append(f"- **Recall (Sensibilidade) ({self.metrics_dict.get('Recall (Sensibilidade)', 0):.2%}):** Dos clientes que realmente deram churn (`1`), quantos o modelo identificou. É crucial para o banco identificar o máximo de clientes em risco (reduzir falsos negativos). Um valor de {self.metrics_dict.get('Recall (Sensibilidade)', 0):.2%} significa que essa porcentagem de clientes que de fato deram churn foi corretamente identificada pelo modelo.\n")
|
| 413 |
+
latex_story.append(NoEscape(fr'\item \textbf{{Recall (Sensibilidade) ({self.metrics_dict.get("Recall (Sensibilidade)", 0):.2f}\%)}}: Dos clientes que realmente deram churn (\texttt{1}), quantos o modelo identificou. É crucial para o banco identificar o máximo de clientes em risco (reduzir falsos negativos). Um valor de {self.metrics_dict.get("Recall (Sensibilidade)", 0):.2f}\% significa que essa porcentagem de clientes que de fato deram churn foi corretamente identificada pelo modelo.' + '\n'))
|
| 414 |
|
| 415 |
markdown_story.append(f"- **F1-Score ({self.metrics_dict.get('F1-Score', 0):.4f}):** É a média harmônica entre Precisão e Recall, útil quando há um desequilíbrio de classes e você precisa de um balanço entre identificar corretamente e não levantar falsos alarmes.\n")
|
| 416 |
+
latex_story.append(NoEscape(fr'\item \textbf{{F1-Score ({self.metrics_dict.get("F1-Score", 0):.4f})}}: É a média harmônica entre Precisão e Recall, útil quando há um desequilíbrio de classes e você precisa de um balanço entre identificar corretamente e não levantar falsos alarmes.' + '\n'))
|
| 417 |
|
| 418 |
latex_story.append(NoEscape(r'\end{itemize}' + '\n\n'))
|
| 419 |
else:
|
|
|
|
| 442 |
doc.append(Command('geometry', 'margin=1in')) # Margens de 1 polegada
|
| 443 |
doc.append(Command('graphicspath', NoEscape(r'{./}'))) # Para imagens no mesmo diretório
|
| 444 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 445 |
doc.append(NoEscape(r'\begin{titlepage}'))
|
| 446 |
doc.append(Command('centering'))
|
| 447 |
|
|
|
|
| 471 |
doc.append(Command('vspace', '1.0cm'))
|
| 472 |
|
| 473 |
# Título do Trabalho (do usuário, ajustado para LaTeX)
|
| 474 |
+
# CORRIGIDO: Esta lógica foi o problema do SyntaxError
|
| 475 |
+
# A nova lógica é mais robusta e não usa raw string literal no split problemático
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 476 |
latex_title_lines = []
|
| 477 |
+
current_line_parts = []
|
| 478 |
+
words = header_info["titulo_trabalho"].split()
|
| 479 |
+
|
| 480 |
+
for word in words:
|
| 481 |
if word == 'UTILIZANDO':
|
| 482 |
+
if current_line_parts:
|
| 483 |
+
latex_title_lines.append(" ".join(current_line_parts))
|
| 484 |
+
current_line_parts = []
|
| 485 |
+
latex_title_lines.append(r'\ \large UTILIZANDO') # LaTeX line break and large font size
|
| 486 |
else:
|
| 487 |
+
current_line_parts.append(word)
|
| 488 |
+
if current_line_parts:
|
| 489 |
+
latex_title_lines.append(" ".join(current_line_parts))
|
| 490 |
|
| 491 |
doc.append(Command('Huge'))
|
| 492 |
+
if latex_title_lines:
|
| 493 |
+
doc.append(Command('textbf', NoEscape(latex_title_lines[0])))
|
| 494 |
+
for line_idx in range(1, len(latex_title_lines)):
|
| 495 |
+
doc.append(LineBreak())
|
| 496 |
+
if '\large' in latex_title_lines[line_idx]: # Check for the large font command
|
| 497 |
+
doc.append(NoEscape(latex_title_lines[line_idx]))
|
| 498 |
+
else:
|
| 499 |
+
doc.append(Command('textbf', NoEscape(latex_title_lines[line_idx])))
|
| 500 |
|
| 501 |
doc.append(Command('vspace', '1.0cm'))
|
| 502 |
|
|
|
|
| 519 |
doc.append(Command('vfill')) # Empurra o conteúdo para cima
|
| 520 |
doc.append(Command('end{titlepage}'))
|
| 521 |
|
|
|
|
| 522 |
doc.append(Command('clearpage'))
|
| 523 |
doc.append(Command('tableofcontents')) # Sumário
|
| 524 |
doc.append(Command('clearpage'))
|