Spaces:
Sleeping
Sleeping
novo teste para parar de vibrar
Browse files
app.py
CHANGED
|
@@ -877,6 +877,21 @@ def main():
|
|
| 877 |
st.markdown('<h2 class="section-header">III. Explicabilidade com SHAP</h2>',
|
| 878 |
unsafe_allow_html=True)
|
| 879 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 880 |
if 'trained_models' not in st.session_state:
|
| 881 |
st.warning("⚠️ Por favor, treine os modelos primeiro na seção 'II. Modelagem Supervisionada'")
|
| 882 |
else:
|
|
@@ -900,39 +915,45 @@ def main():
|
|
| 900 |
key='shap_model_selector'
|
| 901 |
)
|
| 902 |
|
| 903 |
-
|
| 904 |
-
|
| 905 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 906 |
|
| 907 |
-
#
|
| 908 |
-
|
| 909 |
-
|
| 910 |
-
|
| 911 |
-
|
| 912 |
-
|
| 913 |
-
|
| 914 |
-
if
|
| 915 |
-
|
| 916 |
-
|
| 917 |
-
# Obter base_value corretamente
|
| 918 |
-
expected_val = explainer.expected_value
|
| 919 |
-
if isinstance(expected_val, (list, np.ndarray)):
|
| 920 |
-
if len(expected_val) > 1:
|
| 921 |
-
base_val = float(expected_val[1])
|
| 922 |
-
else:
|
| 923 |
-
base_val = float(expected_val[0])
|
| 924 |
else:
|
| 925 |
-
base_val = float(expected_val)
|
| 926 |
-
|
| 927 |
-
|
| 928 |
-
|
| 929 |
-
|
| 930 |
-
|
| 931 |
-
|
| 932 |
-
|
| 933 |
-
|
| 934 |
-
|
| 935 |
-
|
|
|
|
|
|
|
| 936 |
|
| 937 |
if 'shap_values' in st.session_state:
|
| 938 |
shap_values = st.session_state['shap_values']
|
|
@@ -953,14 +974,13 @@ def main():
|
|
| 953 |
</div>
|
| 954 |
""", unsafe_allow_html=True)
|
| 955 |
|
| 956 |
-
# Summary plot com matplotlib -
|
| 957 |
-
|
| 958 |
-
|
| 959 |
-
|
| 960 |
-
|
| 961 |
-
|
| 962 |
-
|
| 963 |
-
plt.close(fig_summary)
|
| 964 |
|
| 965 |
# Análise das principais variáveis
|
| 966 |
st.markdown("### 📈 Análise das Variáveis Mais Importantes")
|
|
@@ -1030,18 +1050,17 @@ def main():
|
|
| 1030 |
st.markdown("#### Análise de um Cliente Classificado como GOOD")
|
| 1031 |
idx_good = good_indices[0]
|
| 1032 |
|
| 1033 |
-
# Waterfall plot
|
| 1034 |
-
|
| 1035 |
-
|
| 1036 |
-
|
| 1037 |
-
|
| 1038 |
-
|
| 1039 |
-
|
| 1040 |
-
|
| 1041 |
-
|
| 1042 |
-
|
| 1043 |
-
|
| 1044 |
-
plt.close(fig_wf)
|
| 1045 |
|
| 1046 |
st.markdown("""
|
| 1047 |
**Interpretação:** Este cliente foi classificado como bom pagador porque:
|
|
@@ -1055,18 +1074,17 @@ def main():
|
|
| 1055 |
st.markdown("#### Análise de um Cliente Classificado como BAD")
|
| 1056 |
idx_bad = bad_indices[0]
|
| 1057 |
|
| 1058 |
-
# Waterfall plot
|
| 1059 |
-
|
| 1060 |
-
|
| 1061 |
-
|
| 1062 |
-
|
| 1063 |
-
|
| 1064 |
-
|
| 1065 |
-
|
| 1066 |
-
|
| 1067 |
-
|
| 1068 |
-
|
| 1069 |
-
plt.close(fig_wf2)
|
| 1070 |
|
| 1071 |
st.markdown("""
|
| 1072 |
**Interpretação:** Este cliente foi classificado como inadimplente porque:
|
|
|
|
| 877 |
st.markdown('<h2 class="section-header">III. Explicabilidade com SHAP</h2>',
|
| 878 |
unsafe_allow_html=True)
|
| 879 |
|
| 880 |
+
# CSS para estabilizar o layout e evitar tremidas
|
| 881 |
+
st.markdown("""
|
| 882 |
+
<style>
|
| 883 |
+
.shap-container {
|
| 884 |
+
min-height: 600px;
|
| 885 |
+
}
|
| 886 |
+
.stSpinner {
|
| 887 |
+
min-height: 50px;
|
| 888 |
+
}
|
| 889 |
+
div[data-testid="stVerticalBlock"] > div {
|
| 890 |
+
transition: none !important;
|
| 891 |
+
}
|
| 892 |
+
</style>
|
| 893 |
+
""", unsafe_allow_html=True)
|
| 894 |
+
|
| 895 |
if 'trained_models' not in st.session_state:
|
| 896 |
st.warning("⚠️ Por favor, treine os modelos primeiro na seção 'II. Modelagem Supervisionada'")
|
| 897 |
else:
|
|
|
|
| 915 |
key='shap_model_selector'
|
| 916 |
)
|
| 917 |
|
| 918 |
+
# Usar form para evitar reruns durante o cálculo
|
| 919 |
+
calc_shap = st.button("🔬 Calcular SHAP Values", type="primary", key='calc_shap_btn')
|
| 920 |
+
|
| 921 |
+
# Placeholder para mensagens de status
|
| 922 |
+
status_placeholder = st.empty()
|
| 923 |
+
|
| 924 |
+
if calc_shap:
|
| 925 |
+
status_placeholder.info("⏳ Calculando SHAP values... Isso pode levar alguns minutos.")
|
| 926 |
+
model = trained_models[model_for_shap]
|
| 927 |
+
|
| 928 |
+
# Usar TreeExplainer para modelos de árvore
|
| 929 |
+
try:
|
| 930 |
+
explainer = shap.TreeExplainer(model)
|
| 931 |
+
X_sample = X_test_scaled[:500]
|
| 932 |
+
shap_values = explainer.shap_values(X_sample)
|
| 933 |
|
| 934 |
+
# Para modelos de classificação binária
|
| 935 |
+
if isinstance(shap_values, list):
|
| 936 |
+
shap_values = shap_values[1] # Classe positiva (bad)
|
| 937 |
+
|
| 938 |
+
# Obter base_value corretamente
|
| 939 |
+
expected_val = explainer.expected_value
|
| 940 |
+
if isinstance(expected_val, (list, np.ndarray)):
|
| 941 |
+
if len(expected_val) > 1:
|
| 942 |
+
base_val = float(expected_val[1])
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 943 |
else:
|
| 944 |
+
base_val = float(expected_val[0])
|
| 945 |
+
else:
|
| 946 |
+
base_val = float(expected_val)
|
| 947 |
+
|
| 948 |
+
st.session_state['shap_explainer'] = explainer
|
| 949 |
+
st.session_state['shap_values'] = shap_values
|
| 950 |
+
st.session_state['X_sample_shap'] = X_sample
|
| 951 |
+
st.session_state['shap_model'] = model_for_shap
|
| 952 |
+
st.session_state['shap_base_val'] = base_val
|
| 953 |
+
|
| 954 |
+
status_placeholder.success("✅ SHAP values calculados com sucesso!")
|
| 955 |
+
except Exception as e:
|
| 956 |
+
status_placeholder.error(f"Erro ao calcular SHAP values: {str(e)}")
|
| 957 |
|
| 958 |
if 'shap_values' in st.session_state:
|
| 959 |
shap_values = st.session_state['shap_values']
|
|
|
|
| 974 |
</div>
|
| 975 |
""", unsafe_allow_html=True)
|
| 976 |
|
| 977 |
+
# Summary plot com matplotlib - fechar figuras anteriores para evitar tremidas
|
| 978 |
+
plt.close('all')
|
| 979 |
+
fig_summary, ax_summary = plt.subplots(figsize=(10, 8))
|
| 980 |
+
shap.summary_plot(shap_values, X_sample, feature_names=feature_names,
|
| 981 |
+
plot_type="dot", show=False)
|
| 982 |
+
st.pyplot(fig_summary, clear_figure=True)
|
| 983 |
+
plt.close('all')
|
|
|
|
| 984 |
|
| 985 |
# Análise das principais variáveis
|
| 986 |
st.markdown("### 📈 Análise das Variáveis Mais Importantes")
|
|
|
|
| 1050 |
st.markdown("#### Análise de um Cliente Classificado como GOOD")
|
| 1051 |
idx_good = good_indices[0]
|
| 1052 |
|
| 1053 |
+
# Waterfall plot - fechar figuras anteriores
|
| 1054 |
+
plt.close('all')
|
| 1055 |
+
fig_wf = plt.figure(figsize=(10, 6))
|
| 1056 |
+
shap.waterfall_plot(shap.Explanation(
|
| 1057 |
+
values=shap_values[idx_good],
|
| 1058 |
+
base_values=base_val,
|
| 1059 |
+
data=X_sample[idx_good],
|
| 1060 |
+
feature_names=feature_names
|
| 1061 |
+
), show=False)
|
| 1062 |
+
st.pyplot(fig_wf, clear_figure=True)
|
| 1063 |
+
plt.close('all')
|
|
|
|
| 1064 |
|
| 1065 |
st.markdown("""
|
| 1066 |
**Interpretação:** Este cliente foi classificado como bom pagador porque:
|
|
|
|
| 1074 |
st.markdown("#### Análise de um Cliente Classificado como BAD")
|
| 1075 |
idx_bad = bad_indices[0]
|
| 1076 |
|
| 1077 |
+
# Waterfall plot - fechar figuras anteriores
|
| 1078 |
+
plt.close('all')
|
| 1079 |
+
fig_wf2 = plt.figure(figsize=(10, 6))
|
| 1080 |
+
shap.waterfall_plot(shap.Explanation(
|
| 1081 |
+
values=shap_values[idx_bad],
|
| 1082 |
+
base_values=base_val,
|
| 1083 |
+
data=X_sample[idx_bad],
|
| 1084 |
+
feature_names=feature_names
|
| 1085 |
+
), show=False)
|
| 1086 |
+
st.pyplot(fig_wf2, clear_figure=True)
|
| 1087 |
+
plt.close('all')
|
|
|
|
| 1088 |
|
| 1089 |
st.markdown("""
|
| 1090 |
**Interpretação:** Este cliente foi classificado como inadimplente porque:
|