vsalgs commited on
Commit
33be9bb
·
verified ·
1 Parent(s): 45e81a9

Update src/streamlit_app.py

Browse files
Files changed (1) hide show
  1. src/streamlit_app.py +18 -52
src/streamlit_app.py CHANGED
@@ -35,13 +35,7 @@ def load_data_anova():
35
  if df is None:
36
  return None, None, [], []
37
 
38
- # Normalizar nomes de coluna: sem espaços, minúsculas, sem caracteres especiais
39
- df.columns = (
40
- df.columns
41
- .str.strip()
42
- .str.lower()
43
- .str.replace('[^0-9a-z_]+', '', regex=True)
44
- )
45
 
46
  coluna_preco_nome = None
47
  if 'saleprice' in df.columns:
@@ -75,18 +69,11 @@ def load_data_reg():
75
  try:
76
  df = pd.read_csv(fixed_url)
77
  url_carregada = fixed_url
78
- except Exception:
79
  return None, None, [], [], []
80
 
81
  st.success(f"Dataset carregado com sucesso de: {url_carregada} (Shape: {df.shape})")
82
-
83
- # Normalizar nomes de coluna: sem espaços, minúsculas, sem caracteres especiais
84
- df.columns = (
85
- df.columns
86
- .str.strip()
87
- .str.lower()
88
- .str.replace('[^0-9a-z_]+', '', regex=True)
89
- )
90
 
91
  coluna_preco_nome = None
92
  possible_price_cols = ['saleprice', 'sale_price', 'price']
@@ -139,9 +126,7 @@ def load_data_reg():
139
  col not in colunas_categoricas_potenciais or col in vars_sempre_continuas_para_reg
140
  ) and col != coluna_preco_nome
141
  ]
142
- colunas_continuas_potenciais = sorted(
143
- list(set(col for col in colunas_continuas_potenciais if col in df.columns))
144
- )
145
 
146
  return df, coluna_preco_nome, colunas_categoricas_potenciais, colunas_continuas_potenciais, df.columns.tolist()
147
 
@@ -153,7 +138,6 @@ def perform_anova_for_variable(df_analysis, var_cat, col_preco):
153
  results = {"var_cat": var_cat, "plots": {}}
154
  df_var = df_analysis[[var_cat, col_preco]].copy()
155
 
156
- # Garantir que a variável categórica seja do tipo category
157
  if df_var[var_cat].dtype != 'object' and not pd.api.types.is_categorical_dtype(df_var[var_cat]):
158
  df_var[var_cat] = df_var[var_cat].astype('category')
159
 
@@ -162,23 +146,19 @@ def perform_anova_for_variable(df_analysis, var_cat, col_preco):
162
  results["error"] = "Dados insuficientes ou poucos níveis após limpeza."
163
  return results
164
 
165
- # Usar backticks para nomes de coluna na fórmula
166
- formula = f'{col_preco} ~ C(`{var_cat}`)'
167
-
168
  try:
169
  modelo = ols(formula, data=df_var).fit()
170
  results["anova_table"] = sm.stats.anova_lm(modelo, typ=2)
171
 
172
- key = f'C(`{var_cat}`)'
173
- if key in results["anova_table"].index:
174
- results["p_valor_anova"] = results["anova_table"].loc[key, 'PR(>F)']
175
  else:
176
  results["p_valor_anova"] = results["anova_table"]['PR(>F)'].iloc[0]
177
 
178
  residuos = modelo.resid
179
  results["residuos_count"] = len(residuos)
180
 
181
- # 1. Normalidade dos resíduos
182
  normalidade_ok = False
183
  if len(residuos) >= 3:
184
  if len(residuos) <= 5000:
@@ -199,8 +179,7 @@ def perform_anova_for_variable(df_analysis, var_cat, col_preco):
199
  if len(residuos) > 1:
200
  sns.histplot(residuos, kde=True, ax=ax_norm[0], stat="density", bins=30)
201
  ax_norm[0].set_title(f'Histograma Resíduos ({var_cat})', fontsize=10)
202
- sm.qqplot(residuos, line='s', ax=ax_norm[1],
203
- markerfacecolor="skyblue", markeredgecolor="dodgerblue", alpha=0.7)
204
  ax_norm[1].set_title(f'Q-Q Plot Resíduos ({var_cat})', fontsize=10)
205
  else:
206
  ax_norm[0].text(0.5, 0.5, "Poucos dados", ha='center', va='center')
@@ -208,10 +187,9 @@ def perform_anova_for_variable(df_analysis, var_cat, col_preco):
208
  plt.tight_layout()
209
  results["plots"]["normalidade"] = fig_norm
210
 
211
- # 2. Homocedasticidade (Teste de Levene)
212
  homocedasticidade_ok = False
213
- grupos = [df_var[col_preco][df_var[var_cat] == categoria].dropna()
214
- for categoria in df_var[var_cat].unique()]
215
  grupos_validos = [g for g in grupos if len(g) >= 2]
216
  if len(grupos_validos) >= 2:
217
  stat_levene, p_levene = levene(*grupos_validos)
@@ -220,7 +198,7 @@ def perform_anova_for_variable(df_analysis, var_cat, col_preco):
220
  homocedasticidade_ok = True
221
  results["homocedasticidade_ok"] = homocedasticidade_ok
222
 
223
- # 3. Kruskal-Wallis (se necessário)
224
  if not normalidade_ok or not homocedasticidade_ok:
225
  if len(grupos_validos) >= 2:
226
  stat_kruskal, p_kruskal = kruskal(*grupos_validos)
@@ -676,42 +654,28 @@ elif st.session_state.page == 'REGRESSAO':
676
  model_summary_obj = output_reg.get('model_summary_obj')
677
  if model_summary_obj:
678
  st.markdown("##### Sumário Geral do Modelo:")
679
- # Usar flavor='bs4' para evitar a dependência de lxml
680
- sum_table0 = pd.read_html(
681
- model_summary_obj.tables[0].as_html(),
682
- header=None,
683
- index_col=None,
684
- flavor='bs4'
685
- )[0]
686
  st.table(sum_table0.iloc[:, :2].rename(columns={0: "Métrica", 1: "Valor"}))
687
  st.table(sum_table0.iloc[:, 2:].rename(columns={2: "Métrica", 3: "Valor"}))
688
 
689
  st.markdown("##### Coeficientes do Modelo:")
690
- sum_table1 = pd.read_html(
691
- model_summary_obj.tables[1].as_html(),
692
- header=0,
693
- index_col=0,
694
- flavor='bs4'
695
- )[0]
696
  st.dataframe(sum_table1.style.format({
697
- "coef": "{:.4f}", "std err": "{:.4f}",
698
- "t": "{:.3f}", "P>|t|": "{:.3e}",
699
  "[0.025": "{:.4f}", "0.975]": "{:.4f}"
700
  }))
701
 
702
  if len(model_summary_obj.tables) > 2:
703
  st.markdown("##### Outras Estatísticas e Notas:")
704
  notes_html = model_summary_obj.tables[2].as_html()
705
- notes_df = pd.read_html(notes_html, header=None, index_col=None, flavor='bs4')[0]
706
  for i in range(len(notes_df)):
707
  line = notes_df.iloc[i].tolist()
708
  st.text(" ".join([str(x) for x in line if pd.notna(x)]))
709
 
710
  st.subheader("Métricas de Desempenho")
711
  if 'performance_metrics' in output_reg:
712
- metrics_df = pd.DataFrame.from_dict(
713
- output_reg['performance_metrics'], orient='index', columns=['Valor']
714
- )
715
  st.table(metrics_df.style.format("{:.4f}"))
716
  st.markdown("""
717
  * **R-squared / R-squared Ajustado:** Variância explicada pelo modelo.
@@ -741,3 +705,5 @@ elif st.session_state.page == 'REGRESSAO':
741
  else:
742
  if not colunas_categoricas_reg and not colunas_continuas_reg:
743
  st.error("Nenhuma coluna adequada identificada para regressão.")
 
 
 
35
  if df is None:
36
  return None, None, [], []
37
 
38
+ df.columns = df.columns.str.replace('[^A-Za-z0-9_]+', '', regex=True).str.lower()
 
 
 
 
 
 
39
 
40
  coluna_preco_nome = None
41
  if 'saleprice' in df.columns:
 
69
  try:
70
  df = pd.read_csv(fixed_url)
71
  url_carregada = fixed_url
72
+ except Exception as e:
73
  return None, None, [], [], []
74
 
75
  st.success(f"Dataset carregado com sucesso de: {url_carregada} (Shape: {df.shape})")
76
+ df.columns = df.columns.str.replace('[^A-Za-z0-9_]+', '', regex=True).str.lower()
 
 
 
 
 
 
 
77
 
78
  coluna_preco_nome = None
79
  possible_price_cols = ['saleprice', 'sale_price', 'price']
 
126
  col not in colunas_categoricas_potenciais or col in vars_sempre_continuas_para_reg
127
  ) and col != coluna_preco_nome
128
  ]
129
+ colunas_continuas_potenciais = sorted(list(set(col for col in colunas_continuas_potenciais if col in df.columns)))
 
 
130
 
131
  return df, coluna_preco_nome, colunas_categoricas_potenciais, colunas_continuas_potenciais, df.columns.tolist()
132
 
 
138
  results = {"var_cat": var_cat, "plots": {}}
139
  df_var = df_analysis[[var_cat, col_preco]].copy()
140
 
 
141
  if df_var[var_cat].dtype != 'object' and not pd.api.types.is_categorical_dtype(df_var[var_cat]):
142
  df_var[var_cat] = df_var[var_cat].astype('category')
143
 
 
146
  results["error"] = "Dados insuficientes ou poucos níveis após limpeza."
147
  return results
148
 
149
+ formula = f'{col_preco} ~ C({var_cat})'
 
 
150
  try:
151
  modelo = ols(formula, data=df_var).fit()
152
  results["anova_table"] = sm.stats.anova_lm(modelo, typ=2)
153
 
154
+ if f'C({var_cat})' in results["anova_table"].index:
155
+ results["p_valor_anova"] = results["anova_table"].loc[f'C({var_cat})', 'PR(>F)']
 
156
  else:
157
  results["p_valor_anova"] = results["anova_table"]['PR(>F)'].iloc[0]
158
 
159
  residuos = modelo.resid
160
  results["residuos_count"] = len(residuos)
161
 
 
162
  normalidade_ok = False
163
  if len(residuos) >= 3:
164
  if len(residuos) <= 5000:
 
179
  if len(residuos) > 1:
180
  sns.histplot(residuos, kde=True, ax=ax_norm[0], stat="density", bins=30)
181
  ax_norm[0].set_title(f'Histograma Resíduos ({var_cat})', fontsize=10)
182
+ sm.qqplot(residuos, line='s', ax=ax_norm[1], markerfacecolor="skyblue", markeredgecolor="dodgerblue", alpha=0.7)
 
183
  ax_norm[1].set_title(f'Q-Q Plot Resíduos ({var_cat})', fontsize=10)
184
  else:
185
  ax_norm[0].text(0.5, 0.5, "Poucos dados", ha='center', va='center')
 
187
  plt.tight_layout()
188
  results["plots"]["normalidade"] = fig_norm
189
 
190
+ # Teste de homocedasticidade (Levene)
191
  homocedasticidade_ok = False
192
+ grupos = [df_var[col_preco][df_var[var_cat] == categoria].dropna() for categoria in df_var[var_cat].unique()]
 
193
  grupos_validos = [g for g in grupos if len(g) >= 2]
194
  if len(grupos_validos) >= 2:
195
  stat_levene, p_levene = levene(*grupos_validos)
 
198
  homocedasticidade_ok = True
199
  results["homocedasticidade_ok"] = homocedasticidade_ok
200
 
201
+ # Teste de Kruskal-Wallis (se necessário)
202
  if not normalidade_ok or not homocedasticidade_ok:
203
  if len(grupos_validos) >= 2:
204
  stat_kruskal, p_kruskal = kruskal(*grupos_validos)
 
654
  model_summary_obj = output_reg.get('model_summary_obj')
655
  if model_summary_obj:
656
  st.markdown("##### Sumário Geral do Modelo:")
657
+ sum_table0 = pd.read_html(model_summary_obj.tables[0].as_html(), header=None, index_col=None)[0]
 
 
 
 
 
 
658
  st.table(sum_table0.iloc[:, :2].rename(columns={0: "Métrica", 1: "Valor"}))
659
  st.table(sum_table0.iloc[:, 2:].rename(columns={2: "Métrica", 3: "Valor"}))
660
 
661
  st.markdown("##### Coeficientes do Modelo:")
662
+ sum_table1 = pd.read_html(model_summary_obj.tables[1].as_html(), header=0, index_col=0)[0]
 
 
 
 
 
663
  st.dataframe(sum_table1.style.format({
664
+ "coef": "{:.4f}", "std err": "{:.4f}", "t": "{:.3f}", "P>|t|": "{:.3e}",
 
665
  "[0.025": "{:.4f}", "0.975]": "{:.4f}"
666
  }))
667
 
668
  if len(model_summary_obj.tables) > 2:
669
  st.markdown("##### Outras Estatísticas e Notas:")
670
  notes_html = model_summary_obj.tables[2].as_html()
671
+ notes_df = pd.read_html(notes_html, header=None, index_col=None)[0]
672
  for i in range(len(notes_df)):
673
  line = notes_df.iloc[i].tolist()
674
  st.text(" ".join([str(x) for x in line if pd.notna(x)]))
675
 
676
  st.subheader("Métricas de Desempenho")
677
  if 'performance_metrics' in output_reg:
678
+ metrics_df = pd.DataFrame.from_dict(output_reg['performance_metrics'], orient='index', columns=['Valor'])
 
 
679
  st.table(metrics_df.style.format("{:.4f}"))
680
  st.markdown("""
681
  * **R-squared / R-squared Ajustado:** Variância explicada pelo modelo.
 
705
  else:
706
  if not colunas_categoricas_reg and not colunas_continuas_reg:
707
  st.error("Nenhuma coluna adequada identificada para regressão.")
708
+
709
+