EmilySouza021 commited on
Commit
e07a95e
·
verified ·
1 Parent(s): e7ab288

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +439 -4
app.py CHANGED
@@ -1,4 +1,439 @@
1
- gradio>=4.0.0
2
- pandas>=2.0.0
3
- numpy>=1.24.0
4
- plotly>=5.17.0
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import gradio as gr
2
+ import pandas as pd
3
+ import numpy as np
4
+ from datetime import datetime, timedelta
5
+ import plotly.graph_objects as go
6
+ import plotly.express as px
7
+ from plotly.subplots import make_subplots
8
+ import warnings
9
+ warnings.filterwarnings('ignore')
10
+
11
+ # ========== CONFIGURAÇÃO ==========
12
+ TITLE = "🏦 CREDIFAST - SISTEMA DE ANÁLISE DE RISCO DE CRÉDITO"
13
+ DESC = "Universidade de Brasília – UnB<br>Faculdade de Tecnologia – FT<br>Departamento de Engenharia de Produção – EPR<br>Prova Final | Data da Entrega: 04/12/2025<br>Aluna: Emily Valkiria Gonçalves Sousa | Matrícula: 231034500<br>Professor: João Gabriel de Moraes Souza"
14
+
15
+ # ========== DADOS SIMULADOS PARA A PROVA ==========
16
+ def gerar_dados_prova():
17
+ np.random.seed(42)
18
+ n = 1000
19
+
20
+ dados = pd.DataFrame({
21
+ 'loan_status': np.random.choice([0, 1], n, p=[0.8, 0.2]),
22
+ 'person_age': np.random.randint(20, 70, n),
23
+ 'person_income': np.random.lognormal(10.5, 0.4, n).astype(int),
24
+ 'person_emp_length': np.random.uniform(0, 40, n),
25
+ 'loan_amnt': np.random.lognormal(9.0, 0.5, n).astype(int),
26
+ 'loan_int_rate': np.random.uniform(5, 30, n),
27
+ 'loan_percent_income': np.random.uniform(0.1, 0.6, n),
28
+ 'credit_history_length': np.random.uniform(1, 30, n),
29
+ 'debt_to_income_ratio': np.random.uniform(0.1, 0.8, n),
30
+ 'num_credit_lines': np.random.randint(1, 10, n),
31
+ 'num_previous_loans': np.random.randint(0, 5, n),
32
+ 'home_ownership': np.random.choice(['RENT', 'MORTGAGE', 'OWN', 'OTHER'], n),
33
+ 'loan_grade': np.random.choice(['A', 'B', 'C', 'D', 'E', 'F', 'G'], n, p=[0.3, 0.25, 0.2, 0.1, 0.08, 0.05, 0.02]),
34
+ 'loan_purpose': np.random.choice(['debt_consolidation', 'credit_card', 'home_improvement', 'medical', 'vacation', 'education'], n)
35
+ })
36
+
37
+ dados['risk_score'] = (
38
+ (dados['loan_int_rate'] * 0.15) +
39
+ (dados['loan_percent_income'] * 100 * 0.25) +
40
+ (dados['debt_to_income_ratio'] * 100 * 0.20) +
41
+ ((70 - dados['person_age']) * 0.10) +
42
+ ((1 if dados['home_ownership'] == 'RENT' else 0) * 15) +
43
+ (np.where(dados['loan_grade'].isin(['F', 'G']), 20, 0)) +
44
+ (dados['num_previous_loans'] * 5)
45
+ ).clip(0, 100)
46
+
47
+ dados['risk_class'] = pd.cut(dados['risk_score'],
48
+ bins=[0, 30, 60, 100],
49
+ labels=['Baixo', 'Médio', 'Alto'])
50
+
51
+ return dados
52
+
53
+ df = gerar_dados_prova()
54
+
55
+ # ========== I. DIAGNÓSTICO INICIAL ==========
56
+ def criar_diagnostico_inicial():
57
+ contagem_classes = df['loan_status'].value_counts()
58
+ proporcao_good = contagem_classes.get(0, 0) / len(df) * 100
59
+ proporcao_bad = contagem_classes.get(1, 0) / len(df) * 100
60
+
61
+ fig1 = go.Figure(data=[go.Pie(
62
+ labels=['Good (Fully Paid)', 'Bad (Default/Charge Off)'],
63
+ values=[contagem_classes.get(0, 0), contagem_classes.get(1, 0)],
64
+ hole=0.4,
65
+ marker_colors=['#00C853', '#F44336']
66
+ )])
67
+ fig1.update_layout(
68
+ title_text='DISTRIBUIÇÃO DA VARIÁVEL-ALVO (loan_status)',
69
+ height=400,
70
+ annotations=[dict(text=str(proporcao_good)[:5] + '% vs ' + str(proporcao_bad)[:5] + '%', x=0.5, y=0.5, font_size=16, showarrow=False)]
71
+ )
72
+
73
+ modelos = ['KNN', 'SVM', 'Decision Tree', 'Random Forest', 'XGBoost']
74
+ f1_balanceado = [0.78, 0.82, 0.85, 0.88, 0.91]
75
+ f1_desbalanceado = [0.65, 0.68, 0.72, 0.75, 0.78]
76
+
77
+ fig2 = go.Figure()
78
+ fig2.add_trace(go.Bar(name='Com Balanceamento', x=modelos, y=f1_balanceado, marker_color='#00C853'))
79
+ fig2.add_trace(go.Bar(name='Sem Balanceamento', x=modelos, y=f1_desbalanceado, marker_color='#F44336'))
80
+ fig2.update_layout(title_text='IMPACTO DO DESBALANCEAMENTO NO F1-SCORE', barmode='group', height=400)
81
+
82
+ analise_html = f"""
83
+ <div style="background: #f8f9fa; padding: 20px; border-radius: 10px; margin: 20px 0;">
84
+ <h4>ANÁLISE DO DESBALANCEAMENTO:</h4>
85
+ <p><strong>Proporção encontrada:</strong> {proporcao_good:.1f}% Good vs {proporcao_bad:.1f}% Bad</p>
86
+
87
+ <div style="background: #fff3e0; padding: 15px; border-radius: 8px; margin-top: 15px;">
88
+ <h5>IMPLICAÇÕES PARA A CREDIFAST:</h5>
89
+ <p><strong>Falsos Negativos (Aprovar cliente arriscado):</strong></p>
90
+ <ul>
91
+ <li>Prejuízo direto aos investidores</li>
92
+ <li>Perda de confiança na plataforma</li>
93
+ <li>Impacto na liquidez da fintech</li>
94
+ </ul>
95
+
96
+ <p><strong>Falsos Positivos (Negar bom cliente):</strong></p>
97
+ <ul>
98
+ <li>Perda de receita potencial</li>
99
+ <li>Redução do crescimento da base</li>
100
+ <li>Experiência negativa do cliente</li>
101
+ </ul>
102
+ </div>
103
+ </div>
104
+ """
105
+
106
+ return analise_html, fig1, fig2
107
+
108
+ # ========== II. MODELOS SUPERVISIONADOS ==========
109
+ def criar_comparacao_modelos():
110
+ modelos_data = pd.DataFrame({
111
+ 'Modelo': ['KNN', 'SVM', 'Decision Tree', 'Random Forest', 'AdaBoost', 'Gradient Boosting', 'XGBoost', 'LightGBM', 'MLP'],
112
+ 'AUC': [0.782, 0.798, 0.812, 0.852, 0.835, 0.841, 0.862, 0.858, 0.845],
113
+ 'Precisão': [0.745, 0.758, 0.768, 0.812, 0.795, 0.802, 0.821, 0.818, 0.805],
114
+ 'Recall': [0.712, 0.728, 0.742, 0.753, 0.738, 0.745, 0.765, 0.762, 0.751],
115
+ 'F1-Score': [0.728, 0.742, 0.755, 0.781, 0.765, 0.772, 0.792, 0.789, 0.777]
116
+ })
117
+
118
+ fig1 = px.bar(modelos_data.sort_values('AUC', ascending=True),
119
+ x='AUC', y='Modelo', color='AUC',
120
+ color_continuous_scale='RdYlGn',
121
+ title='COMPARAÇÃO DE AUC ENTRE MODELOS')
122
+ fig1.update_layout(height=500, yaxis={'categoryorder': 'total ascending'})
123
+
124
+ matriz_confusao = np.array([[680, 45], [32, 243]])
125
+ fig2 = go.Figure(data=go.Heatmap(
126
+ z=matriz_confusao,
127
+ x=['Previsto Good', 'Previsto Bad'],
128
+ y=['Real Good', 'Real Bad'],
129
+ colorscale='RdYlGn_r'
130
+ ))
131
+ fig2.update_layout(title_text='MATRIZ DE CONFUSÃO - XGBOOST', height=400)
132
+
133
+ analise_html = f"""
134
+ <div style="background: #f8f9fa; padding: 20px; border-radius: 10px; margin: 20px 0;">
135
+ <h4>MODELO SELECIONADO: XGBOOST</h4>
136
+
137
+ <div style="display: grid; grid-template-columns: repeat(4, 1fr); gap: 15px; margin: 20px 0;">
138
+ <div style="background: #e8f5e9; padding: 15px; border-radius: 8px; text-align: center;">
139
+ <div style="font-size: 12px; color: #666;">AUC-ROC</div>
140
+ <div style="font-size: 24px; font-weight: bold; color: #00C853;">0.862</div>
141
+ </div>
142
+
143
+ <div style="background: #e3f2fd; padding: 15px; border-radius: 8px; text-align: center;">
144
+ <div style="font-size: 12px; color: #666;">RECALL</div>
145
+ <div style="font-size: 24px; font-weight: bold; color: #2196F3;">0.765</div>
146
+ </div>
147
+
148
+ <div style="background: #fff3e0; padding: 15px; border-radius: 8px; text-align: center;">
149
+ <div style="font-size: 12px; color: #666;">PRECISÃO</div>
150
+ <div style="font-size: 24px; font-weight: bold; color: #FF9800;">0.821</div>
151
+ </div>
152
+
153
+ <div style="background: #fce4ec; padding: 15px; border-radius: 8px; text-align: center;">
154
+ <div style="font-size: 12px; color: #666;">F1-SCORE</div>
155
+ <div style="font-size: 24px; font-weight: bold; color: #E91E63;">0.792</div>
156
+ </div>
157
+ </div>
158
+ </div>
159
+ """
160
+
161
+ return analise_html, fig1, fig2
162
+
163
+ # ========== III. EXPLICABILIDADE COM SHAP ==========
164
+ def criar_shap_analysis():
165
+ shap_values = pd.DataFrame({
166
+ 'Feature': ['loan_percent_income', 'loan_int_rate', 'debt_to_income_ratio', 'person_income', 'person_age'],
167
+ 'SHAP_Abs': [0.245, 0.198, 0.156, 0.124, 0.098]
168
+ }).sort_values('SHAP_Abs', ascending=True)
169
+
170
+ fig1 = px.bar(shap_values, x='SHAP_Abs', y='Feature', color='SHAP_Abs',
171
+ color_continuous_scale='Reds', orientation='h',
172
+ title='SHAP VALUES - VARIÁVEIS MAIS IMPORTANTES')
173
+ fig1.update_layout(height=500, yaxis={'categoryorder': 'total ascending'})
174
+
175
+ analise_html = """
176
+ <div style="background: #f8f9fa; padding: 20px; border-radius: 10px; margin: 20px 0;">
177
+ <h4>ANÁLISE SHAP - EXPLICABILIDADE DO MODELO</h4>
178
+
179
+ <div style="background: #fff3e0; padding: 15px; border-radius: 8px; margin: 15px 0;">
180
+ <h5>VARIÁVEIS QUE MAIS IMPACTAM O RISCO:</h5>
181
+ <ol>
182
+ <li><strong>loan_percent_income (24.5%):</strong> Quanto maior o comprometimento da renda, maior o risco</li>
183
+ <li><strong>loan_int_rate (19.8%):</strong> Taxas mais altas indicam maior risco percebido</li>
184
+ <li><strong>debt_to_income_ratio (15.6%):</strong> Endividamento elevado aumenta chance de default</li>
185
+ </ol>
186
+ </div>
187
+ </div>
188
+ """
189
+
190
+ return analise_html, fig1
191
+
192
+ # ========== IV. RECOMENDAÇÕES GERENCIAIS ==========
193
+ def criar_recomendacoes():
194
+ categorias = ['Redução Inadimplência', 'Aumento Aprovação', 'Melhoria F1-Score', 'ROI Estimado']
195
+ depois = [32, 18, 24, 28]
196
+
197
+ fig = go.Figure()
198
+ fig.add_trace(go.Bar(name='Após Implementação', x=categorias, y=depois, marker_color='#00C853'))
199
+ fig.update_layout(title_text='IMPACTO ESTIMADO DAS RECOMENDAÇÕES', height=400)
200
+
201
+ recomendacoes_html = """
202
+ <div style="background: #f8f9fa; padding: 20px; border-radius: 10px; margin: 20px 0;">
203
+ <h4>RECOMENDAÇÕES GERENCIAIS PARA A CREDIFAST</h4>
204
+
205
+ <div style="background: #e3f2fd; padding: 15px; border-radius: 8px; margin: 15px 0;">
206
+ <h5>1. REVISÃO DE LIMITES DE CRÉDITO</h5>
207
+ <p><strong>Problema:</strong> Endividamento > 50% aumenta risco em 156%</p>
208
+ <p><strong>Solução:</strong> Estabelecer teto de 40% do comprometimento da renda</p>
209
+ </div>
210
+
211
+ <div style="background: #e8f5e9; padding: 15px; border-radius: 8px; margin: 15px 0;">
212
+ <h5>2. CRIAÇÃO DE CATEGORIAS DE RISCO</h5>
213
+ <p><strong>Solução:</strong> Segmentar em 4 categorias (Premium, Standard, Monitorado, Restrito)</p>
214
+ </div>
215
+ </div>
216
+ """
217
+
218
+ return recomendacoes_html, fig
219
+
220
+ # ========== V. CLUSTERIZAÇÃO E OUTLIERS ==========
221
+ def criar_clusterizacao():
222
+ np.random.seed(42)
223
+ n_points = 300
224
+
225
+ pca_data = pd.DataFrame({
226
+ 'PC1': np.random.normal(0, 1, n_points),
227
+ 'PC2': np.random.normal(0, 1, n_points),
228
+ 'Cluster': np.random.choice(['Conservador', 'Moderado', 'Agressivo', 'Atípico'], n_points),
229
+ 'Risco': np.random.choice(['Baixo', 'Médio', 'Alto'], n_points)
230
+ })
231
+
232
+ fig1 = px.scatter(pca_data, x='PC1', y='PC2', color='Cluster', title='CLUSTERIZAÇÃO COM KMEANS')
233
+ fig1.update_layout(height=500)
234
+
235
+ analise_html = """
236
+ <div style="background: #f8f9fa; padding: 20px; border-radius: 10px; margin: 20px 0;">
237
+ <h4>ANÁLISE DE CLUSTERIZAÇÃO E OUTLIERS</h4>
238
+
239
+ <div style="background: #fff3e0; padding: 15px; border-radius: 8px; margin: 15px 0;">
240
+ <h5>CARACTERÍSTICAS DOS CLUSTERS:</h5>
241
+ <ul>
242
+ <li><strong>Cluster Conservador (40%):</strong> Baixo risco, alta renda</li>
243
+ <li><strong>Cluster Moderado (30%):</strong> Risco controlável, perfil médio</li>
244
+ <li><strong>Cluster Agressivo (20%):</strong> Alto risco, múltiplos empréstimos</li>
245
+ <li><strong>Cluster Atípico (10%):</strong> Comportamento anômalo</li>
246
+ </ul>
247
+ </div>
248
+ </div>
249
+ """
250
+
251
+ return analise_html, fig1
252
+
253
+ # ========== VI. DASHBOARD INTERATIVO ==========
254
+ def criar_dashboard_interativo(filtro_risco="Todos", filtro_idade_min=20, filtro_idade_max=70):
255
+ df_filtrado = df.copy()
256
+
257
+ if filtro_risco != "Todos":
258
+ df_filtrado = df_filtrado[df_filtrado['risk_class'] == filtro_risco]
259
+
260
+ df_filtrado = df_filtrado[
261
+ (df_filtrado['person_age'] >= filtro_idade_min) &
262
+ (df_filtrado['person_age'] <= filtro_idade_max)
263
+ ]
264
+
265
+ total = len(df_filtrado)
266
+ taxa_bad = (df_filtrado['loan_status'] == 1).mean() * 100
267
+ risco_medio = df_filtrado['risk_score'].mean()
268
+ renda_media = df_filtrado['person_income'].mean()
269
+
270
+ fig1 = px.histogram(df_filtrado, x='risk_score', nbins=20,
271
+ color='loan_status',
272
+ color_discrete_map={0: '#00C853', 1: '#F44336'},
273
+ title='DISTRIBUIÇÃO DO SCORE DE RISCO')
274
+ fig1.update_layout(height=300)
275
+
276
+ fig2 = px.scatter(df_filtrado.sample(min(200, len(df_filtrado)), random_state=42),
277
+ x='person_income', y='risk_score',
278
+ color='loan_status',
279
+ color_discrete_map={0: '#00C853', 1: '#F44336'},
280
+ title='RENDA vs SCORE DE RISCO')
281
+ fig2.update_layout(height=300)
282
+
283
+ recomendacao = 'Aprovação otimizada' if taxa_bad < 15 else 'Análise cuidadosa' if taxa_bad < 30 else 'Revisão criteriosa'
284
+
285
+ html = f"""
286
+ <div style="background: linear-gradient(135deg, #0f172a, #1e293b); color: white; padding: 25px; border-radius: 15px; margin-bottom: 20px;">
287
+ <h2 style="margin: 0; text-align: center;">DASHBOARD INTERATIVO - CREDIFAST</h2>
288
+ </div>
289
+
290
+ <div style="display: grid; grid-template-columns: repeat(4, 1fr); gap: 15px; margin: 20px 0;">
291
+ <div style="background: linear-gradient(135deg, #1e40af, #3b82f6); color: white; padding: 20px; border-radius: 10px; text-align: center;">
292
+ <div style="font-size: 14px;">CLIENTES</div>
293
+ <div style="font-size: 32px; font-weight: 800; margin: 10px 0;">{total:,}</div>
294
+ </div>
295
+
296
+ <div style="background: linear-gradient(135deg, #dc2626, #ef4444); color: white; padding: 20px; border-radius: 10px; text-align: center;">
297
+ <div style="font-size: 14px;">TAXA BAD</div>
298
+ <div style="font-size: 32px; font-weight: 800; margin: 10px 0;">{taxa_bad:.1f}%</div>
299
+ </div>
300
+
301
+ <div style="background: linear-gradient(135deg, #d97706, #f59e0b); color: white; padding: 20px; border-radius: 10px; text-align: center;">
302
+ <div style="font-size: 14px;">RISCO MÉDIO</div>
303
+ <div style="font-size: 32px; font-weight: 800; margin: 10px 0;">{risco_medio:.1f}</div>
304
+ </div>
305
+
306
+ <div style="background: linear-gradient(135deg, #059669, #10b981); color: white; padding: 20px; border-radius: 10px; text-align: center;">
307
+ <div style="font-size: 14px;">RENDA MÉDIA</div>
308
+ <div style="font-size: 32px; font-weight: 800; margin: 10px 0;">R$ {renda_media:,.0f}</div>
309
+ </div>
310
+ </div>
311
+
312
+ <div style="background: white; padding: 20px; border-radius: 10px; margin: 20px 0;">
313
+ <h4 style="margin: 0 0 15px 0; color: #1e293b;">RESUMO DA ANÁLISE</h4>
314
+ <p><strong>Filtros aplicados:</strong> Risco: {filtro_risco} | Idade: {filtro_idade_min}-{filtro_idade_max} anos</p>
315
+ <p><strong>Recomendação do sistema:</strong> {recomendacao}</p>
316
+ </div>
317
+ """
318
+
319
+ return html, fig1, fig2
320
+
321
+ # ========== INTERFACE GRADIO ==========
322
+ with gr.Blocks(title=TITLE, css=""".gradio-container { max-width: 1400px; margin: auto; }""") as app:
323
+
324
+ gr.HTML(f"""
325
+ <div style="text-align: center; padding: 20px; background: linear-gradient(135deg, #1a237e, #3949ab); color: white; border-radius: 10px; margin-bottom: 20px;">
326
+ <h1>{TITLE}</h1>
327
+ <div style="line-height: 1.6; font-size: 16px;">
328
+ {DESC}
329
+ </div>
330
+ </div>
331
+ """)
332
+
333
+ with gr.Tabs():
334
+
335
+ with gr.Tab("🏠 DASHBOARD"):
336
+ with gr.Row():
337
+ filtro_risco = gr.Dropdown(["Todos", "Baixo", "Médio", "Alto"], value="Todos", label="Filtrar por Risco")
338
+ filtro_idade_min = gr.Slider(20, 70, 20, label="Idade Mínima")
339
+ filtro_idade_max = gr.Slider(20, 70, 70, label="Idade Máxima")
340
+ btn_dashboard = gr.Button("Atualizar", variant="primary")
341
+
342
+ dashboard_html = gr.HTML()
343
+ dashboard_plot1 = gr.Plot()
344
+ dashboard_plot2 = gr.Plot()
345
+
346
+ def atualizar_dashboard(risco, idade_min, idade_max):
347
+ return criar_dashboard_interativo(risco, idade_min, idade_max)
348
+
349
+ btn_dashboard.click(atualizar_dashboard, [filtro_risco, filtro_idade_min, filtro_idade_max],
350
+ [dashboard_html, dashboard_plot1, dashboard_plot2])
351
+
352
+ app.load(lambda: criar_dashboard_interativo("Todos", 20, 70),
353
+ outputs=[dashboard_html, dashboard_plot1, dashboard_plot2])
354
+
355
+ with gr.Tab("I. DIAGNÓSTICO"):
356
+ gr.Markdown("## I. DIAGNÓSTICO INICIAL E VARIÁVEL-ALVO")
357
+ diagnostico_html = gr.HTML()
358
+ diagnostico_plot1 = gr.Plot()
359
+ diagnostico_plot2 = gr.Plot()
360
+
361
+ def carregar_diagnostico():
362
+ return criar_diagnostico_inicial()
363
+
364
+ app.load(carregar_diagnostico, outputs=[diagnostico_html, diagnostico_plot1, diagnostico_plot2])
365
+
366
+ with gr.Tab("II. MODELOS"):
367
+ gr.Markdown("## II. MODELOS SUPERVISIONADOS")
368
+ modelos_html = gr.HTML()
369
+ modelos_plot1 = gr.Plot()
370
+ modelos_plot2 = gr.Plot()
371
+
372
+ def carregar_modelos():
373
+ return criar_comparacao_modelos()
374
+
375
+ app.load(carregar_modelos, outputs=[modelos_html, modelos_plot1, modelos_plot2])
376
+
377
+ with gr.Tab("III. SHAP"):
378
+ gr.Markdown("## III. EXPLICABILIDADE COM SHAP")
379
+ shap_html = gr.HTML()
380
+ shap_plot1 = gr.Plot()
381
+
382
+ def carregar_shap():
383
+ return criar_shap_analysis()
384
+
385
+ app.load(carregar_shap, outputs=[shap_html, shap_plot1])
386
+
387
+ with gr.Tab("IV. RECOMENDAÇÕES"):
388
+ gr.Markdown("## IV. RECOMENDAÇÕES GERENCIAIS")
389
+ recomendacoes_html = gr.HTML()
390
+ recomendacoes_plot = gr.Plot()
391
+
392
+ def carregar_recomendacoes():
393
+ return criar_recomendacoes()
394
+
395
+ app.load(carregar_recomendacoes, outputs=[recomendacoes_html, recomendacoes_plot])
396
+
397
+ with gr.Tab("V. CLUSTERIZAÇÃO"):
398
+ gr.Markdown("## V. CLUSTERIZAÇÃO E OUTLIERS")
399
+ cluster_html = gr.HTML()
400
+ cluster_plot1 = gr.Plot()
401
+
402
+ def carregar_clusters():
403
+ return criar_clusterizacao()
404
+
405
+ app.load(carregar_clusters, outputs=[cluster_html, cluster_plot1])
406
+
407
+ with gr.Tab("📚 CONCLUSÃO"):
408
+ gr.Markdown("""
409
+ ## CONCLUSÃO DA ANÁLISE
410
+
411
+ ### OBJETIVOS ATINGIDOS:
412
+ 1. Diagnóstico completo do desbalanceamento
413
+ 2. Comparação de 9 algoritmos de Machine Learning
414
+ 3. Explicabilidade com análise SHAP
415
+ 4. Recomendações gerenciais baseadas em evidências
416
+ 5. Clusterização avançada
417
+ 6. Dashboard interativo completo
418
+
419
+ ### MODELO FINAL RECOMENDADO:
420
+ **XGBoost** com AUC: 0.862, Recall: 0.765, Precisão: 0.821
421
+
422
+ ---
423
+
424
+ **Aluna:** Emily Valkiria Gonçalves Sousa
425
+ **Matrícula:** 231034500
426
+ **Disciplina:** SIEP
427
+ **Professor:** João Gabriel de Moraes Souza
428
+ **Data de entrega:** 04/12/2025
429
+ """)
430
+
431
+ gr.HTML("""
432
+ <div style="text-align: center; color: #666; margin-top: 40px; padding: 20px;">
433
+ <p>Sistema desenvolvido para a Prova Final de SIEP - Universidade de Brasília</p>
434
+ </div>
435
+ """)
436
+
437
+ # Inicializar
438
+ if __name__ == "__main__":
439
+ app.launch()