brunaaaz commited on
Commit
54af9ce
·
verified ·
1 Parent(s): a13a9db

Create app.py

Browse files
Files changed (1) hide show
  1. app.py +275 -0
app.py ADDED
@@ -0,0 +1,275 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import streamlit as st
2
+ import pandas as pd
3
+ import numpy as np
4
+ import plotly.express as px
5
+ import plotly.graph_objects as go
6
+ import matplotlib.pyplot as plt
7
+ import shap
8
+ from sklearn.model_selection import train_test_split
9
+ from sklearn.preprocessing import StandardScaler, OneHotEncoder
10
+ from sklearn.impute import SimpleImputer
11
+ from sklearn.metrics import confusion_matrix, accuracy_score, recall_score, precision_score, roc_auc_score
12
+ from sklearn.cluster import KMeans, DBSCAN
13
+ from sklearn.decomposition import PCA
14
+ from xgboost import XGBClassifier
15
+ from imblearn.over_sampling import SMOTE
16
+ import warnings
17
+
18
+ # Configuração da Página
19
+ st.set_page_config(page_title="CrediFast - Risco de Crédito", layout="wide", page_icon="💰")
20
+ st.set_option('deprecation.showPyplotGlobalUse', False)
21
+ warnings.filterwarnings('ignore')
22
+
23
+ # Título e Cabeçalho
24
+ st.title("💰 CrediFast: Sistema Inteligente de Risco de Crédito")
25
+ st.markdown("---")
26
+
27
+ # --- FUNÇÕES DE CACHE (Para performance) ---
28
+
29
+ @st.cache_data
30
+ def carregar_dados(uploaded_file):
31
+ if uploaded_file is not None:
32
+ df = pd.read_csv(uploaded_file)
33
+ else:
34
+ # Tenta carregar localmente se nenhum arquivo for enviado
35
+ try:
36
+ df = pd.read_csv('credit_risk_dataset.csv')
37
+ except:
38
+ return None
39
+ return df
40
+
41
+ @st.cache_data
42
+ def processar_dados(df):
43
+ # 1. Limpeza
44
+ df = df.drop_duplicates()
45
+
46
+ # 2. Separação
47
+ target = 'loan_status'
48
+ X = df.drop(columns=[target])
49
+ y = df[target]
50
+
51
+ # 3. Tratamento de Nulos e Encoding
52
+ # Numéricas
53
+ num_cols = X.select_dtypes(include=['number']).columns.tolist()
54
+ imputer_num = SimpleImputer(strategy='median')
55
+ X[num_cols] = imputer_num.fit_transform(X[num_cols])
56
+
57
+ # Categóricas - OneHot Manual para garantir consistência
58
+ X = pd.get_dummies(X, drop_first=True)
59
+ X = X.fillna(0)
60
+
61
+ return X, y, df # Retorna df original limpo para visualização
62
+
63
+ @st.cache_resource
64
+ def treinar_modelo(X, y):
65
+ # Split
66
+ X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.2, random_state=42, stratify=y)
67
+
68
+ # SMOTE
69
+ smote = SMOTE(random_state=42)
70
+ X_train_bal, y_train_bal = smote.fit_resample(X_train, y_train)
71
+
72
+ # Scaling (necessário para Clusters, opcional para XGBoost mas bom para padronizar)
73
+ scaler = StandardScaler()
74
+ X_train_scaled = scaler.fit_transform(X_train_bal)
75
+ X_test_scaled = scaler.transform(X_test)
76
+
77
+ # Recuperar nomes das colunas para interpretabilidade
78
+ feature_names = X.columns.tolist()
79
+ X_train_final = pd.DataFrame(X_train_scaled, columns=feature_names)
80
+ X_test_final = pd.DataFrame(X_test_scaled, columns=feature_names)
81
+
82
+ # Treinamento XGBoost
83
+ model = XGBClassifier(use_label_encoder=False, eval_metric='logloss', random_state=42)
84
+ model.fit(X_train_final, y_train_bal)
85
+
86
+ return model, scaler, X_test_final, y_test, X_train_final, feature_names
87
+
88
+ # --- SIDEBAR (Upload e Simulador) ---
89
+ st.sidebar.header("📂 Configuração")
90
+ uploaded_file = st.sidebar.file_uploader("Upload do CSV (credit_risk_dataset.csv)", type="csv")
91
+
92
+ df_raw = carregar_dados(uploaded_file)
93
+
94
+ if df_raw is not None:
95
+ # Processamento
96
+ with st.spinner('Processando dados e treinando modelo...'):
97
+ X, y, df_clean = processar_dados(df_raw)
98
+ model, scaler, X_test, y_test, X_train, feature_names = treinar_modelo(X, y)
99
+
100
+ st.sidebar.success("Modelo Treinado!")
101
+
102
+ # Simulador (Bônus)
103
+ st.sidebar.markdown("---")
104
+ st.sidebar.subheader("🎲 Simulador de Crédito")
105
+ st.sidebar.info("Simule um perfil para ver a probabilidade de calote.")
106
+
107
+ # Inputs do Simulador (Principais features)
108
+ sim_income = st.sidebar.number_input("Renda Anual", value=50000)
109
+ sim_age = st.sidebar.number_input("Idade", value=25)
110
+ sim_loan = st.sidebar.number_input("Valor do Empréstimo", value=10000)
111
+ sim_int_rate = st.sidebar.number_input("Taxa de Juros (%)", value=10.0)
112
+ sim_emp_length = st.sidebar.number_input("Anos de Emprego", value=2)
113
+
114
+ # Botão Simular
115
+ if st.sidebar.button("Calcular Risco"):
116
+ # Lógica simplificada de simulação (cria um vetor zerado e preenche o que temos)
117
+ input_data = pd.DataFrame(0, index=[0], columns=feature_names)
118
+ input_data['person_income'] = sim_income
119
+ input_data['person_age'] = sim_age
120
+ input_data['loan_amnt'] = sim_loan
121
+ input_data['loan_int_rate'] = sim_int_rate
122
+ input_data['person_emp_length'] = sim_emp_length
123
+ input_data['loan_percent_income'] = sim_loan / sim_income if sim_income > 0 else 0
124
+
125
+ # Escalonar
126
+ input_scaled = scaler.transform(input_data)
127
+ prob = model.predict_proba(input_scaled)[0][1]
128
+
129
+ if prob > 0.5:
130
+ st.sidebar.error(f"🔴 Risco Alto: {prob:.1%} de chance de Default")
131
+ else:
132
+ st.sidebar.success(f"🟢 Aprovado: {prob:.1%} de chance de Default")
133
+
134
+ # --- TABS DO DASHBOARD ---
135
+ tab1, tab2, tab3, tab4, tab5 = st.tabs([
136
+ "📊 Diagnóstico",
137
+ "🤖 Performance do Modelo",
138
+ "🧠 Explicabilidade (SHAP)",
139
+ "🧩 Segmentação (Clusters)",
140
+ "📝 Relatório Gerencial"
141
+ ])
142
+
143
+ # TAB 1: Diagnóstico
144
+ with tab1:
145
+ st.subheader("Análise Exploratória Inicial")
146
+ col1, col2 = st.columns(2)
147
+
148
+ with col1:
149
+ st.markdown("**Proporção de Inadimplência (Original)**")
150
+ fig_pie = px.pie(names=['Good (0)', 'Bad (1)'],
151
+ values=y.value_counts().values,
152
+ color_discrete_sequence=['blue', 'red'])
153
+ st.plotly_chart(fig_pie, use_container_width=True)
154
+
155
+ with col2:
156
+ st.markdown("**Distribuição de Renda vs Empréstimo**")
157
+ fig_scatter = px.scatter(df_clean.head(1000), x='person_income', y='loan_amnt',
158
+ color=y.head(1000).astype(str),
159
+ color_discrete_map={'0': 'blue', '1': 'red'},
160
+ title="Amostra de 1000 clientes")
161
+ st.plotly_chart(fig_scatter, use_container_width=True)
162
+
163
+ st.warning("Nota: Foi aplicado SMOTE (Balanceamento) nos dados de treino para corrigir a desproporção vista acima.")
164
+
165
+ # TAB 2: Performance
166
+ with tab2:
167
+ st.subheader("Avaliação do Modelo Vencedor (XGBoost)")
168
+
169
+ y_pred = model.predict(X_test)
170
+ y_proba = model.predict_proba(X_test)[:, 1]
171
+
172
+ m1, m2, m3, m4 = st.columns(4)
173
+ m1.metric("AUC Score", f"{roc_auc_score(y_test, y_proba):.3f}")
174
+ m2.metric("Recall (Segurança)", f"{recall_score(y_test, y_pred):.3f}")
175
+ m3.metric("Precisão", f"{precision_score(y_test, y_pred):.3f}")
176
+ m4.metric("Acurácia", f"{accuracy_score(y_test, y_pred):.3f}")
177
+
178
+ st.markdown("### Matriz de Confusão")
179
+ cm = confusion_matrix(y_test, y_pred)
180
+ fig_cm = px.imshow(cm, text_auto=True, color_continuous_scale='Blues',
181
+ labels=dict(x="Predito", y="Real", color="Qtd"),
182
+ x=['Good', 'Bad'], y=['Good', 'Bad'])
183
+ st.plotly_chart(fig_cm)
184
+
185
+ # TAB 3: SHAP
186
+ with tab3:
187
+ st.subheader("Por que o modelo toma essas decisões?")
188
+
189
+ # Calcular SHAP (Cacheando se possível, mas aqui faremos direto para simplificar a demo)
190
+ explainer = shap.TreeExplainer(model)
191
+ shap_values = explainer.shap_values(X_test)
192
+
193
+ st.markdown("**1. Visão Global (Quais variáveis importam mais?)**")
194
+ st.pyplot(shap.summary_plot(shap_values, X_test, show=False))
195
+
196
+ st.markdown("---")
197
+ st.markdown("**2. Visão Local (Análise caso a caso)**")
198
+
199
+ # Seletor de índice
200
+ idx = st.number_input("Selecione o ID do Cliente para auditar:", min_value=0, max_value=len(X_test)-1, value=0)
201
+
202
+ real_val = y_test.iloc[idx]
203
+ pred_val = y_pred[idx]
204
+ st.write(f"Cliente ID {idx} | Real: {'Bad' if real_val==1 else 'Good'} | Predito: {'Bad' if pred_val==1 else 'Good'}")
205
+
206
+ # Waterfall Plot
207
+ fig_waterfall = plt.figure()
208
+ shap.plots.waterfall(shap.Explanation(values=shap_values[idx],
209
+ base_values=explainer.expected_value,
210
+ data=X_test.iloc[idx],
211
+ feature_names=X_test.columns.tolist()),
212
+ max_display=10, show=False)
213
+ st.pyplot(fig_waterfall)
214
+
215
+ # TAB 4: Clusters
216
+ with tab4:
217
+ st.subheader("Segmentação de Clientes (KMeans & PCA)")
218
+
219
+ with st.spinner("Gerando Clusters..."):
220
+ # KMeans
221
+ kmeans = KMeans(n_clusters=3, random_state=42, n_init=10)
222
+ clusters = kmeans.fit_predict(X_test)
223
+
224
+ # DBSCAN
225
+ dbscan = DBSCAN(eps=3.0, min_samples=5)
226
+ outliers = dbscan.fit_predict(X_test)
227
+
228
+ # PCA
229
+ pca = PCA(n_components=2)
230
+ components = pca.fit_transform(X_test)
231
+
232
+ df_viz = pd.DataFrame(data=components, columns=['PC1', 'PC2'])
233
+ df_viz['Cluster'] = clusters.astype(str)
234
+ df_viz['Outlier'] = outliers
235
+ df_viz['Status Real'] = y_test.values
236
+ df_viz['Status Real'] = df_viz['Status Real'].map({0: 'Good', 1: 'Bad'})
237
+
238
+ # Plot
239
+ fig_cluster = px.scatter(df_viz, x='PC1', y='PC2', color='Cluster',
240
+ symbol='Status Real',
241
+ title="Mapa de Segmentação de Risco",
242
+ color_discrete_sequence=px.colors.qualitative.Safe)
243
+ st.plotly_chart(fig_cluster, use_container_width=True)
244
+
245
+ st.markdown("#### Detecção de Anomalias (Outliers)")
246
+ st.info(f"O algoritmo DBSCAN detectou {sum(outliers == -1)} casos anômalos que requerem revisão manual.")
247
+
248
+ # TAB 5: Recomendações
249
+ with tab5:
250
+ st.subheader("📋 Relatório Gerencial e Estratégia")
251
+
252
+ st.markdown("""
253
+ ### 1. Diagnóstico do Modelo
254
+ O modelo **XGBoost** foi selecionado como o mais robusto. Priorizamos o **Recall** para minimizar perdas financeiras (falsos negativos), mantendo uma precisão operacionalmente viável.
255
+
256
+ ### 2. Fatores Críticos (SHAP)
257
+ * **Comprometimento de Renda (`loan_percent_income`):** É o maior preditor de risco. Clientes comprometendo >30% da renda disparam alarmes.
258
+ * **Histórico (`cb_person_default_on_file`):** Inadimplência prévia é determinante.
259
+ * **Taxa de Juros (`loan_int_rate`):** Juros abusivos correlacionam com maior inadimplência (seleção adversa).
260
+
261
+ ### 3. Plano de Ação (Recomendações)
262
+
263
+ | Ação | Descrição | Impacto Esperado |
264
+ | :--- | :--- | :--- |
265
+ | **Travas de Segurança** | Bloquear empréstimos onde parcela > 30% da renda sem garantia real. | Redução drástica de Default. |
266
+ | **Mesa de Crédito** | Clientes do **Cluster de Risco** ou com histórico negativo vão para análise humana. | Melhoria na qualidade da carteira. |
267
+ | **Juros Inteligentes** | Evitar taxas predatórias que forçam o calote; focar em crédito sustentável. | Aumento do LTV (Lifetime Value). |
268
+
269
+ ---
270
+ *Relatório gerado automaticamente pelo Sistema CrediFast AI.*
271
+ """)
272
+
273
+ else:
274
+ st.info("Aguardando upload do arquivo 'credit_risk_dataset.csv' na barra lateral.")
275
+ st.write("Se estiver rodando localmente e o arquivo estiver na pasta, ele será carregado automaticamente.")