ricardoadriano commited on
Commit
b2737bb
·
verified ·
1 Parent(s): 6bbdfd0

Update src/streamlit_app.py

Browse files
Files changed (1) hide show
  1. src/streamlit_app.py +30 -25
src/streamlit_app.py CHANGED
@@ -5,6 +5,7 @@
5
  # Objetivo: permitir escolher ALVO e PREDITORES e produzir INFERÊNCIA (item e),
6
  # usando Logit (alvo binário) ou OLS (alvo contínuo).
7
  # Se o alvo for categórico com >2 classes, permite one-vs-rest.
 
8
  # -------------------------------------------------------------------
9
  import os
10
  import numpy as np
@@ -58,13 +59,11 @@ def coerce_numeric_series(s: pd.Series) -> pd.Series:
58
  """Tenta converter strings numéricas para float (lida com vírgula decimal)."""
59
  if np.issubdtype(s.dtype, np.number):
60
  return s.astype(float)
61
- # troca vírgula decimal por ponto, remove separadores comuns
62
  tmp = s.astype(str).str.replace(r"[.\s]", "", regex=True).str.replace(",", ".", regex=False)
63
- coerced = pd.to_numeric(tmp, errors="coerce")
64
- return coerced
65
 
66
  def engineer_features(df: pd.DataFrame) -> pd.DataFrame:
67
- """Engenharia minimalista (compatível com o CSV padrão do Kaggle)."""
68
  out = df.copy()
69
 
70
  # Tenure (dias desde Dt_Customer)
@@ -191,27 +190,39 @@ df_eng = engineer_features(df_raw)
191
  with st.sidebar:
192
  st.markdown("**Alvo (variável dependente):**")
193
  all_cols = df_eng.columns.tolist()
194
- # Preferência: 'Response' se existir; senão binária; senão numérica
195
- default_target = "Response" if "Response" in all_cols else None
196
- if default_target is None:
197
- for c in all_cols:
198
- if is_binary_series(df_eng[c]):
199
- default_target = c; break
200
- if default_target is None:
201
  for c in all_cols:
202
- if np.issubdtype(df_eng[c].dtype, np.number):
203
- default_target = c; break
 
 
204
  target_col = st.selectbox("Alvo (y)", options=all_cols, index=all_cols.index(default_target) if default_target in all_cols else 0)
205
 
206
  # Variáveis explicativas
207
  exclude = [target_col]
208
  num_cols_all, cat_cols_all = split_num_cat(df_eng, exclude=exclude)
209
 
 
 
 
 
 
 
 
 
 
210
  with st.sidebar:
211
  st.markdown("**Variáveis explicativas (X):**")
212
- engineered_first = [c for c in ["TenureDays","TotalMnt","TotalPurchases","OnlineShare","PromoShare","AvgTicket","BasketDiversity"] if c in num_cols_all]
213
- base_defaults = engineered_first + [c for c in num_cols_all if c not in engineered_first][:5] + cat_cols_all[:3]
214
- selected_feats = st.multiselect("Selecione X", options=(num_cols_all + cat_cols_all), default=base_defaults)
 
 
 
215
 
216
  test_size = st.slider("Proporção de teste", 0.1, 0.4, 0.2, 0.05)
217
  random_state = st.number_input("Random seed", value=42, step=1)
@@ -233,11 +244,9 @@ is_bin = is_binary_series(y_raw)
233
  # 2) se não binário, tenta numérico (coerção segura)
234
  y_numeric_try = coerce_numeric_series(y_raw) if not is_bin else None
235
  is_numeric_ok = False
236
- if not is_bin:
237
- if y_numeric_try is not None:
238
- # considera "ok" se pelo menos 80% foram convertidos
239
- conv_rate = y_numeric_try.notna().mean()
240
- is_numeric_ok = conv_rate >= 0.8
241
 
242
  # 3) se não binário e não numérico, vira categórico multi-classe → one-vs-rest
243
  with st.sidebar:
@@ -250,20 +259,16 @@ with st.sidebar:
250
 
251
  # ---------- Montagem de y conforme os casos ----------
252
  if is_bin:
253
- # Se binário não-numérico → mapear para {0,1} em ordem alfabética
254
  if not np.issubdtype(y_raw.dtype, np.number):
255
  uniq = sorted(pd.unique(y_raw.dropna()).tolist(), key=lambda x: str(x))
256
  y = y_raw.replace({uniq[0]: 0, uniq[1]: 1}).astype(int)
257
  else:
258
  y = y_raw.astype(int)
259
  model_type = "logit"
260
-
261
  elif is_numeric_ok:
262
  y = y_numeric_try.astype(float)
263
  model_type = "ols"
264
-
265
  else:
266
- # one-vs-rest
267
  y = (y_raw == positive_class).astype(int)
268
  model_type = "logit"
269
 
 
5
  # Objetivo: permitir escolher ALVO e PREDITORES e produzir INFERÊNCIA (item e),
6
  # usando Logit (alvo binário) ou OLS (alvo contínuo).
7
  # Se o alvo for categórico com >2 classes, permite one-vs-rest.
8
+ # Alvo padrão: Response. X padrão alinhado ao Colab.
9
  # -------------------------------------------------------------------
10
  import os
11
  import numpy as np
 
59
  """Tenta converter strings numéricas para float (lida com vírgula decimal)."""
60
  if np.issubdtype(s.dtype, np.number):
61
  return s.astype(float)
 
62
  tmp = s.astype(str).str.replace(r"[.\s]", "", regex=True).str.replace(",", ".", regex=False)
63
+ return pd.to_numeric(tmp, errors="coerce")
 
64
 
65
  def engineer_features(df: pd.DataFrame) -> pd.DataFrame:
66
+ """Engenharia minimalista para o dataset padrão do Kaggle."""
67
  out = df.copy()
68
 
69
  # Tenure (dias desde Dt_Customer)
 
190
  with st.sidebar:
191
  st.markdown("**Alvo (variável dependente):**")
192
  all_cols = df_eng.columns.tolist()
193
+ # Alvo padrão fixo: Response (se existir). Caso contrário, mesma lógica de fallback.
194
+ if "Response" in all_cols:
195
+ default_target = "Response"
196
+ else:
197
+ default_target = None
 
 
198
  for c in all_cols:
199
+ if is_binary_series(df_eng[c]): default_target = c; break
200
+ if default_target is None:
201
+ for c in all_cols:
202
+ if np.issubdtype(df_eng[c].dtype, np.number): default_target = c; break
203
  target_col = st.selectbox("Alvo (y)", options=all_cols, index=all_cols.index(default_target) if default_target in all_cols else 0)
204
 
205
  # Variáveis explicativas
206
  exclude = [target_col]
207
  num_cols_all, cat_cols_all = split_num_cat(df_eng, exclude=exclude)
208
 
209
+ # X padrão alinhado ao Colab (só incluir se existir na base)
210
+ preferred_defaults = [
211
+ "Income", "Recency", "Education", "Marital_Status",
212
+ "TenureDays", "TotalMnt", "TotalPurchases",
213
+ "OnlineShare", "PromoShare", "AvgTicket", "BasketDiversity",
214
+ "NumWebVisitsMonth"
215
+ ]
216
+ default_X = [c for c in preferred_defaults if c in (num_cols_all + cat_cols_all)]
217
+
218
  with st.sidebar:
219
  st.markdown("**Variáveis explicativas (X):**")
220
+ # Se nada dos preferidos existir, cai no fallback antigo (algumas num + categ)
221
+ if not default_X:
222
+ engineered_first = [c for c in ["TenureDays","TotalMnt","TotalPurchases","OnlineShare","PromoShare","AvgTicket","BasketDiversity"] if c in num_cols_all]
223
+ default_X = engineered_first + [c for c in num_cols_all if c not in engineered_first][:5] + cat_cols_all[:3]
224
+
225
+ selected_feats = st.multiselect("Selecione X", options=(num_cols_all + cat_cols_all), default=default_X)
226
 
227
  test_size = st.slider("Proporção de teste", 0.1, 0.4, 0.2, 0.05)
228
  random_state = st.number_input("Random seed", value=42, step=1)
 
244
  # 2) se não binário, tenta numérico (coerção segura)
245
  y_numeric_try = coerce_numeric_series(y_raw) if not is_bin else None
246
  is_numeric_ok = False
247
+ if not is_bin and y_numeric_try is not None:
248
+ conv_rate = y_numeric_try.notna().mean()
249
+ is_numeric_ok = conv_rate >= 0.8
 
 
250
 
251
  # 3) se não binário e não numérico, vira categórico multi-classe → one-vs-rest
252
  with st.sidebar:
 
259
 
260
  # ---------- Montagem de y conforme os casos ----------
261
  if is_bin:
 
262
  if not np.issubdtype(y_raw.dtype, np.number):
263
  uniq = sorted(pd.unique(y_raw.dropna()).tolist(), key=lambda x: str(x))
264
  y = y_raw.replace({uniq[0]: 0, uniq[1]: 1}).astype(int)
265
  else:
266
  y = y_raw.astype(int)
267
  model_type = "logit"
 
268
  elif is_numeric_ok:
269
  y = y_numeric_try.astype(float)
270
  model_type = "ols"
 
271
  else:
 
272
  y = (y_raw == positive_class).astype(int)
273
  model_type = "logit"
274