import streamlit as st import pandas as pd import numpy as np import plotly.express as px import plotly.graph_objects as go import matplotlib.pyplot as plt import shap from sklearn.model_selection import train_test_split from sklearn.preprocessing import StandardScaler, OneHotEncoder from sklearn.impute import SimpleImputer from sklearn.metrics import confusion_matrix, accuracy_score, recall_score, precision_score, roc_auc_score from sklearn.cluster import KMeans, DBSCAN from sklearn.decomposition import PCA from xgboost import XGBClassifier from imblearn.over_sampling import SMOTE import warnings # Configuração da Página st.set_page_config(page_title="CrediFast - Risco de Crédito", layout="wide", page_icon="💰") st.set_option('deprecation.showPyplotGlobalUse', False) warnings.filterwarnings('ignore') # Título e Cabeçalho st.title("💰 CrediFast: Sistema Inteligente de Risco de Crédito") st.markdown("---") # --- FUNÇÕES DE CACHE (Para performance) --- @st.cache_data def carregar_dados(uploaded_file): if uploaded_file is not None: df = pd.read_csv(uploaded_file) else: # Tenta carregar localmente se nenhum arquivo for enviado try: df = pd.read_csv('credit_risk_dataset.csv') except: return None return df @st.cache_data def processar_dados(df): # 1. Limpeza df = df.drop_duplicates() # 2. Separação target = 'loan_status' X = df.drop(columns=[target]) y = df[target] # 3. Tratamento de Nulos e Encoding # Numéricas num_cols = X.select_dtypes(include=['number']).columns.tolist() imputer_num = SimpleImputer(strategy='median') X[num_cols] = imputer_num.fit_transform(X[num_cols]) # Categóricas - OneHot Manual para garantir consistência X = pd.get_dummies(X, drop_first=True) X = X.fillna(0) return X, y, df # Retorna df original limpo para visualização @st.cache_resource def treinar_modelo(X, y): # Split X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.2, random_state=42, stratify=y) # SMOTE smote = SMOTE(random_state=42) X_train_bal, y_train_bal = smote.fit_resample(X_train, y_train) # Scaling (necessário para Clusters, opcional para XGBoost mas bom para padronizar) scaler = StandardScaler() X_train_scaled = scaler.fit_transform(X_train_bal) X_test_scaled = scaler.transform(X_test) # Recuperar nomes das colunas para interpretabilidade feature_names = X.columns.tolist() X_train_final = pd.DataFrame(X_train_scaled, columns=feature_names) X_test_final = pd.DataFrame(X_test_scaled, columns=feature_names) # Treinamento XGBoost model = XGBClassifier(use_label_encoder=False, eval_metric='logloss', random_state=42) model.fit(X_train_final, y_train_bal) return model, scaler, X_test_final, y_test, X_train_final, feature_names # --- SIDEBAR (Upload e Simulador) --- st.sidebar.header("📂 Configuração") uploaded_file = st.sidebar.file_uploader("Upload do CSV (credit_risk_dataset.csv)", type="csv") df_raw = carregar_dados(uploaded_file) if df_raw is not None: # Processamento with st.spinner('Processando dados e treinando modelo...'): X, y, df_clean = processar_dados(df_raw) model, scaler, X_test, y_test, X_train, feature_names = treinar_modelo(X, y) st.sidebar.success("Modelo Treinado!") # Simulador (Bônus) st.sidebar.markdown("---") st.sidebar.subheader("🎲 Simulador de Crédito") st.sidebar.info("Simule um perfil para ver a probabilidade de calote.") # Inputs do Simulador (Principais features) sim_income = st.sidebar.number_input("Renda Anual", value=50000) sim_age = st.sidebar.number_input("Idade", value=25) sim_loan = st.sidebar.number_input("Valor do Empréstimo", value=10000) sim_int_rate = st.sidebar.number_input("Taxa de Juros (%)", value=10.0) sim_emp_length = st.sidebar.number_input("Anos de Emprego", value=2) # Botão Simular if st.sidebar.button("Calcular Risco"): # Lógica simplificada de simulação (cria um vetor zerado e preenche o que temos) input_data = pd.DataFrame(0, index=[0], columns=feature_names) input_data['person_income'] = sim_income input_data['person_age'] = sim_age input_data['loan_amnt'] = sim_loan input_data['loan_int_rate'] = sim_int_rate input_data['person_emp_length'] = sim_emp_length input_data['loan_percent_income'] = sim_loan / sim_income if sim_income > 0 else 0 # Escalonar input_scaled = scaler.transform(input_data) prob = model.predict_proba(input_scaled)[0][1] if prob > 0.5: st.sidebar.error(f"🔴 Risco Alto: {prob:.1%} de chance de Default") else: st.sidebar.success(f"🟢 Aprovado: {prob:.1%} de chance de Default") # --- TABS DO DASHBOARD --- tab1, tab2, tab3, tab4, tab5 = st.tabs([ "📊 Diagnóstico", "🤖 Performance do Modelo", "🧠 Explicabilidade (SHAP)", "🧩 Segmentação (Clusters)", "📝 Relatório Gerencial" ]) # TAB 1: Diagnóstico with tab1: st.subheader("Análise Exploratória Inicial") col1, col2 = st.columns(2) with col1: st.markdown("**Proporção de Inadimplência (Original)**") fig_pie = px.pie(names=['Good (0)', 'Bad (1)'], values=y.value_counts().values, color_discrete_sequence=['blue', 'red']) st.plotly_chart(fig_pie, use_container_width=True) with col2: st.markdown("**Distribuição de Renda vs Empréstimo**") fig_scatter = px.scatter(df_clean.head(1000), x='person_income', y='loan_amnt', color=y.head(1000).astype(str), color_discrete_map={'0': 'blue', '1': 'red'}, title="Amostra de 1000 clientes") st.plotly_chart(fig_scatter, use_container_width=True) st.warning("Nota: Foi aplicado SMOTE (Balanceamento) nos dados de treino para corrigir a desproporção vista acima.") # TAB 2: Performance with tab2: st.subheader("Avaliação do Modelo Vencedor (XGBoost)") y_pred = model.predict(X_test) y_proba = model.predict_proba(X_test)[:, 1] m1, m2, m3, m4 = st.columns(4) m1.metric("AUC Score", f"{roc_auc_score(y_test, y_proba):.3f}") m2.metric("Recall (Segurança)", f"{recall_score(y_test, y_pred):.3f}") m3.metric("Precisão", f"{precision_score(y_test, y_pred):.3f}") m4.metric("Acurácia", f"{accuracy_score(y_test, y_pred):.3f}") st.markdown("### Matriz de Confusão") cm = confusion_matrix(y_test, y_pred) fig_cm = px.imshow(cm, text_auto=True, color_continuous_scale='Blues', labels=dict(x="Predito", y="Real", color="Qtd"), x=['Good', 'Bad'], y=['Good', 'Bad']) st.plotly_chart(fig_cm) # TAB 3: SHAP with tab3: st.subheader("Por que o modelo toma essas decisões?") # Calcular SHAP (Cacheando se possível, mas aqui faremos direto para simplificar a demo) explainer = shap.TreeExplainer(model) shap_values = explainer.shap_values(X_test) st.markdown("**1. Visão Global (Quais variáveis importam mais?)**") st.pyplot(shap.summary_plot(shap_values, X_test, show=False)) st.markdown("---") st.markdown("**2. Visão Local (Análise caso a caso)**") # Seletor de índice idx = st.number_input("Selecione o ID do Cliente para auditar:", min_value=0, max_value=len(X_test)-1, value=0) real_val = y_test.iloc[idx] pred_val = y_pred[idx] st.write(f"Cliente ID {idx} | Real: {'Bad' if real_val==1 else 'Good'} | Predito: {'Bad' if pred_val==1 else 'Good'}") # Waterfall Plot fig_waterfall = plt.figure() shap.plots.waterfall(shap.Explanation(values=shap_values[idx], base_values=explainer.expected_value, data=X_test.iloc[idx], feature_names=X_test.columns.tolist()), max_display=10, show=False) st.pyplot(fig_waterfall) # TAB 4: Clusters with tab4: st.subheader("Segmentação de Clientes (KMeans & PCA)") with st.spinner("Gerando Clusters..."): # KMeans kmeans = KMeans(n_clusters=3, random_state=42, n_init=10) clusters = kmeans.fit_predict(X_test) # DBSCAN dbscan = DBSCAN(eps=3.0, min_samples=5) outliers = dbscan.fit_predict(X_test) # PCA pca = PCA(n_components=2) components = pca.fit_transform(X_test) df_viz = pd.DataFrame(data=components, columns=['PC1', 'PC2']) df_viz['Cluster'] = clusters.astype(str) df_viz['Outlier'] = outliers df_viz['Status Real'] = y_test.values df_viz['Status Real'] = df_viz['Status Real'].map({0: 'Good', 1: 'Bad'}) # Plot fig_cluster = px.scatter(df_viz, x='PC1', y='PC2', color='Cluster', symbol='Status Real', title="Mapa de Segmentação de Risco", color_discrete_sequence=px.colors.qualitative.Safe) st.plotly_chart(fig_cluster, use_container_width=True) st.markdown("#### Detecção de Anomalias (Outliers)") st.info(f"O algoritmo DBSCAN detectou {sum(outliers == -1)} casos anômalos que requerem revisão manual.") # TAB 5: Recomendações with tab5: st.subheader("📋 Relatório Gerencial e Estratégia") st.markdown(""" ### 1. Diagnóstico do Modelo 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. ### 2. Fatores Críticos (SHAP) * **Comprometimento de Renda (`loan_percent_income`):** É o maior preditor de risco. Clientes comprometendo >30% da renda disparam alarmes. * **Histórico (`cb_person_default_on_file`):** Inadimplência prévia é determinante. * **Taxa de Juros (`loan_int_rate`):** Juros abusivos correlacionam com maior inadimplência (seleção adversa). ### 3. Plano de Ação (Recomendações) | Ação | Descrição | Impacto Esperado | | :--- | :--- | :--- | | **Travas de Segurança** | Bloquear empréstimos onde parcela > 30% da renda sem garantia real. | Redução drástica de Default. | | **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. | | **Juros Inteligentes** | Evitar taxas predatórias que forçam o calote; focar em crédito sustentável. | Aumento do LTV (Lifetime Value). | --- *Relatório gerado automaticamente pelo Sistema CrediFast AI.* """) else: st.info("Aguardando upload do arquivo 'credit_risk_dataset.csv' na barra lateral.") st.write("Se estiver rodando localmente e o arquivo estiver na pasta, ele será carregado automaticamente.")