252106862eder commited on
Commit
2760434
·
verified ·
1 Parent(s): 4248ee7

Update model_utils.py

Browse files

linha com erro remoção

Files changed (1) hide show
  1. 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 o SyntaxWarning para '%' no f-string para LaTeX
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).' + '\n'))
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}\%):}} 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,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
- # Quebra de linha manual para o título
481
- # CORRIGIDO: title_parts = header_info["titulo_trabalho"].replace('UTILIZANDO', r'\UTILIZANDO').split(r'\')
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
- current_line = []
493
- for word in title_words:
 
 
494
  if word == 'UTILIZANDO':
495
- if current_line:
496
- latex_title_lines.append(" ".join(current_line))
497
- current_line = []
498
- latex_title_lines.append(r'\ \large UTILIZANDO') # Comando LaTeX para quebra de linha e tamanho da fonte
499
  else:
500
- current_line.append(word)
501
- if current_line:
502
- latex_title_lines.append(" ".join(current_line))
503
 
504
  doc.append(Command('Huge'))
505
- doc.append(Command('textbf', NoEscape(latex_title_lines[0]))) # Primeira linha do título
506
- for line_idx in range(1, len(latex_title_lines)):
507
- doc.append(LineBreak())
508
- # Se for a linha com 'UTILIZANDO', já está formatada, caso contrário, use textbf
509
- if 'UTILIZANDO' in latex_title_lines[line_idx]:
510
- doc.append(NoEscape(latex_title_lines[line_idx]))
511
- else:
512
- doc.append(Command('textbf', NoEscape(latex_title_lines[line_idx])))
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'))