Spaces:
Sleeping
Sleeping
Update src/streamlit_app.py
Browse files- src/streamlit_app.py +35 -67
src/streamlit_app.py
CHANGED
|
@@ -2,7 +2,7 @@
|
|
| 2 |
# coding: utf-8
|
| 3 |
|
| 4 |
# =====================================================
|
| 5 |
-
# Dashboard - AmesHousing (Tarefa 4) - versão
|
| 6 |
# =====================================================
|
| 7 |
|
| 8 |
import streamlit as st
|
|
@@ -28,8 +28,6 @@ st.set_page_config(
|
|
| 28 |
layout="wide",
|
| 29 |
initial_sidebar_state="expanded"
|
| 30 |
)
|
| 31 |
-
|
| 32 |
-
# Nova API para manter posição estável
|
| 33 |
st.query_params.clear()
|
| 34 |
|
| 35 |
st.markdown("<h1 style='text-align:center;color:#003366;'>Análise do Dataset AmesHousing</h1>", unsafe_allow_html=True)
|
|
@@ -45,15 +43,13 @@ def carregar_dados():
|
|
| 45 |
"/mnt/data/AmesHousing.csv",
|
| 46 |
"../Dados/AmesHousing.csv",
|
| 47 |
]
|
| 48 |
-
last_err = None
|
| 49 |
for p in paths_tentativa:
|
| 50 |
try:
|
| 51 |
df = pd.read_csv(p)
|
| 52 |
return df
|
| 53 |
-
except
|
| 54 |
-
last_err = e
|
| 55 |
continue
|
| 56 |
-
|
| 57 |
|
| 58 |
casa_data = carregar_dados()
|
| 59 |
casa_data.columns = casa_data.columns.str.strip().str.replace(" ", "_")
|
|
@@ -106,12 +102,9 @@ alpha_reg = st.sidebar.slider("Nível de significância (α) — Regressão", 0.
|
|
| 106 |
# Distribuição de Preço de Venda
|
| 107 |
st.subheader("Distribuição do Preço de Venda")
|
| 108 |
if not dados_filtrados.empty:
|
| 109 |
-
fig, ax = plt.subplots(figsize=(
|
| 110 |
sns.histplot(dados_filtrados['SalePrice'], kde=True, ax=ax)
|
| 111 |
-
ax.set_title("Distribuição do Preço de Venda")
|
| 112 |
st.pyplot(fig, clear_figure=True, use_container_width=False)
|
| 113 |
-
else:
|
| 114 |
-
st.warning("Nenhum dado disponível com os filtros aplicados.")
|
| 115 |
|
| 116 |
# Boxplots
|
| 117 |
st.subheader("Boxplots das Variáveis Selecionadas")
|
|
@@ -119,53 +112,35 @@ variavel = st.selectbox(
|
|
| 119 |
"Escolha a variável categórica para comparar preços:",
|
| 120 |
["Neighborhood","Garage_Type","Fireplaces"]
|
| 121 |
)
|
| 122 |
-
|
| 123 |
-
|
| 124 |
-
|
| 125 |
-
|
| 126 |
-
|
| 127 |
-
plt.xticks(rotation=90)
|
| 128 |
-
ax2.set_title(f"Preço de Venda por {variavel}")
|
| 129 |
-
st.pyplot(fig2, clear_figure=True, use_container_width=False)
|
| 130 |
-
else:
|
| 131 |
-
st.warning(f"Não é possível gerar boxplot: apenas uma categoria em {variavel} após os filtros.")
|
| 132 |
|
| 133 |
# Scatter interativo
|
| 134 |
st.subheader("Preço Médio de Venda por Bairro")
|
| 135 |
-
|
| 136 |
-
|
| 137 |
-
return df.groupby('Neighborhood').agg(
|
| 138 |
count=('SalePrice','size'),
|
| 139 |
mean_price=('SalePrice','mean')
|
| 140 |
).reset_index()
|
| 141 |
-
|
| 142 |
-
if not dados_filtrados.empty:
|
| 143 |
-
bairro_grouped = agrupar_bairros(dados_filtrados)
|
| 144 |
bairro_filtered = bairro_grouped[bairro_grouped['count'] >= 5]
|
| 145 |
if not bairro_filtered.empty:
|
| 146 |
fig3 = px.scatter(
|
| 147 |
bairro_filtered,
|
| 148 |
-
x='mean_price',
|
| 149 |
-
|
| 150 |
-
|
| 151 |
-
color='Neighborhood',
|
| 152 |
-
title='Preço Médio de Venda vs Bairro (Ames, Iowa)',
|
| 153 |
-
labels={'mean_price': 'Preço Médio de Venda', 'Neighborhood':'Bairro'},
|
| 154 |
-
opacity=0.8
|
| 155 |
)
|
| 156 |
-
fig3.update_layout(
|
| 157 |
st.plotly_chart(fig3, use_container_width=False)
|
| 158 |
-
else:
|
| 159 |
-
st.warning("Não há bairros suficientes após filtros para gerar o gráfico.")
|
| 160 |
|
| 161 |
# =================================================
|
| 162 |
# Regressão Linear — Tarefa 4
|
| 163 |
# =================================================
|
| 164 |
-
|
| 165 |
def construir_formula(y, feats_num, feats_cat, inter_1=None, inter_2=None):
|
| 166 |
-
termos = []
|
| 167 |
-
termos += feats_num
|
| 168 |
-
termos += [f"C({c})" for c in feats_cat]
|
| 169 |
if inter_1 and inter_2:
|
| 170 |
a = f"C({inter_1})" if inter_1 in feats_cat else inter_1
|
| 171 |
b = f"C({inter_2})" if inter_2 in feats_cat else inter_2
|
|
@@ -178,48 +153,41 @@ if st.button("Ajustar modelo"):
|
|
| 178 |
if interagir and inter_1 and inter_2:
|
| 179 |
cols_necessarias += [inter_1, inter_2]
|
| 180 |
df_modelo = dados_filtrados[cols_necessarias].dropna().copy()
|
| 181 |
-
if df_modelo.empty:
|
| 182 |
-
st.error("Sem dados suficientes após remoção de NAs nas variáveis selecionadas.")
|
| 183 |
-
else:
|
| 184 |
y_col = 'SalePrice'
|
| 185 |
if usar_logy:
|
| 186 |
-
df_modelo['SalePrice'] = np.log(df_modelo['SalePrice']
|
| 187 |
-
y_col = 'SalePrice'
|
| 188 |
-
|
| 189 |
formula = construir_formula(y_col, feats_num, feats_cat, inter_1 if interagir else None, inter_2 if interagir else None)
|
| 190 |
df_treino, df_teste = train_test_split(df_modelo, test_size=teste_size, random_state=42)
|
| 191 |
model = ols(formula, data=df_treino).fit()
|
| 192 |
|
| 193 |
-
st.markdown("#### Especificação do Modelo")
|
| 194 |
st.code(formula)
|
| 195 |
-
st.
|
| 196 |
-
st.dataframe(model.summary2().tables[1])
|
| 197 |
|
| 198 |
-
|
| 199 |
-
y_true = df_teste['SalePrice']
|
| 200 |
-
y_pred = model.predict(df_teste)
|
| 201 |
if usar_logy:
|
| 202 |
-
y_true = np.exp(y_true)
|
| 203 |
-
|
| 204 |
-
|
| 205 |
-
|
| 206 |
-
|
| 207 |
-
|
| 208 |
-
|
| 209 |
-
|
| 210 |
-
|
| 211 |
-
|
|
|
|
| 212 |
cols = st.columns(3)
|
| 213 |
with cols[0]:
|
| 214 |
-
fig_r, ax_r = plt.subplots(figsize=(3
|
| 215 |
ax_r.scatter(fitted, residuos, alpha=0.5)
|
| 216 |
ax_r.axhline(0, color='red', linestyle='--')
|
| 217 |
st.pyplot(fig_r, clear_figure=True, use_container_width=False)
|
| 218 |
with cols[1]:
|
| 219 |
-
fig_q =
|
| 220 |
-
|
| 221 |
st.pyplot(fig_q, clear_figure=True, use_container_width=False)
|
| 222 |
with cols[2]:
|
| 223 |
-
fig_h, ax_h = plt.subplots(figsize=(3
|
| 224 |
sns.histplot(residuos, kde=True, ax=ax_h)
|
| 225 |
st.pyplot(fig_h, clear_figure=True, use_container_width=False)
|
|
|
|
| 2 |
# coding: utf-8
|
| 3 |
|
| 4 |
# =====================================================
|
| 5 |
+
# Dashboard - AmesHousing (Tarefa 4) - versão compacta
|
| 6 |
# =====================================================
|
| 7 |
|
| 8 |
import streamlit as st
|
|
|
|
| 28 |
layout="wide",
|
| 29 |
initial_sidebar_state="expanded"
|
| 30 |
)
|
|
|
|
|
|
|
| 31 |
st.query_params.clear()
|
| 32 |
|
| 33 |
st.markdown("<h1 style='text-align:center;color:#003366;'>Análise do Dataset AmesHousing</h1>", unsafe_allow_html=True)
|
|
|
|
| 43 |
"/mnt/data/AmesHousing.csv",
|
| 44 |
"../Dados/AmesHousing.csv",
|
| 45 |
]
|
|
|
|
| 46 |
for p in paths_tentativa:
|
| 47 |
try:
|
| 48 |
df = pd.read_csv(p)
|
| 49 |
return df
|
| 50 |
+
except:
|
|
|
|
| 51 |
continue
|
| 52 |
+
return pd.DataFrame()
|
| 53 |
|
| 54 |
casa_data = carregar_dados()
|
| 55 |
casa_data.columns = casa_data.columns.str.strip().str.replace(" ", "_")
|
|
|
|
| 102 |
# Distribuição de Preço de Venda
|
| 103 |
st.subheader("Distribuição do Preço de Venda")
|
| 104 |
if not dados_filtrados.empty:
|
| 105 |
+
fig, ax = plt.subplots(figsize=(5,3.5))
|
| 106 |
sns.histplot(dados_filtrados['SalePrice'], kde=True, ax=ax)
|
|
|
|
| 107 |
st.pyplot(fig, clear_figure=True, use_container_width=False)
|
|
|
|
|
|
|
| 108 |
|
| 109 |
# Boxplots
|
| 110 |
st.subheader("Boxplots das Variáveis Selecionadas")
|
|
|
|
| 112 |
"Escolha a variável categórica para comparar preços:",
|
| 113 |
["Neighborhood","Garage_Type","Fireplaces"]
|
| 114 |
)
|
| 115 |
+
if not dados_filtrados.empty and len(dados_filtrados[variavel].dropna().unique()) > 1:
|
| 116 |
+
fig2, ax2 = plt.subplots(figsize=(6,4))
|
| 117 |
+
sns.boxplot(x=variavel, y="SalePrice", data=dados_filtrados, ax=ax2)
|
| 118 |
+
plt.xticks(rotation=90)
|
| 119 |
+
st.pyplot(fig2, clear_figure=True, use_container_width=False)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 120 |
|
| 121 |
# Scatter interativo
|
| 122 |
st.subheader("Preço Médio de Venda por Bairro")
|
| 123 |
+
if not dados_filtrados.empty:
|
| 124 |
+
bairro_grouped = dados_filtrados.groupby('Neighborhood').agg(
|
|
|
|
| 125 |
count=('SalePrice','size'),
|
| 126 |
mean_price=('SalePrice','mean')
|
| 127 |
).reset_index()
|
|
|
|
|
|
|
|
|
|
| 128 |
bairro_filtered = bairro_grouped[bairro_grouped['count'] >= 5]
|
| 129 |
if not bairro_filtered.empty:
|
| 130 |
fig3 = px.scatter(
|
| 131 |
bairro_filtered,
|
| 132 |
+
x='mean_price', y='Neighborhood',
|
| 133 |
+
size='count', color='Neighborhood',
|
| 134 |
+
labels={'mean_price': 'Preço Médio de Venda', 'Neighborhood':'Bairro'}
|
|
|
|
|
|
|
|
|
|
|
|
|
| 135 |
)
|
| 136 |
+
fig3.update_layout(width=600, height=350)
|
| 137 |
st.plotly_chart(fig3, use_container_width=False)
|
|
|
|
|
|
|
| 138 |
|
| 139 |
# =================================================
|
| 140 |
# Regressão Linear — Tarefa 4
|
| 141 |
# =================================================
|
|
|
|
| 142 |
def construir_formula(y, feats_num, feats_cat, inter_1=None, inter_2=None):
|
| 143 |
+
termos = feats_num + [f"C({c})" for c in feats_cat]
|
|
|
|
|
|
|
| 144 |
if inter_1 and inter_2:
|
| 145 |
a = f"C({inter_1})" if inter_1 in feats_cat else inter_1
|
| 146 |
b = f"C({inter_2})" if inter_2 in feats_cat else inter_2
|
|
|
|
| 153 |
if interagir and inter_1 and inter_2:
|
| 154 |
cols_necessarias += [inter_1, inter_2]
|
| 155 |
df_modelo = dados_filtrados[cols_necessarias].dropna().copy()
|
| 156 |
+
if not df_modelo.empty:
|
|
|
|
|
|
|
| 157 |
y_col = 'SalePrice'
|
| 158 |
if usar_logy:
|
| 159 |
+
df_modelo['SalePrice'] = np.log(df_modelo['SalePrice'])
|
|
|
|
|
|
|
| 160 |
formula = construir_formula(y_col, feats_num, feats_cat, inter_1 if interagir else None, inter_2 if interagir else None)
|
| 161 |
df_treino, df_teste = train_test_split(df_modelo, test_size=teste_size, random_state=42)
|
| 162 |
model = ols(formula, data=df_treino).fit()
|
| 163 |
|
|
|
|
| 164 |
st.code(formula)
|
| 165 |
+
st.dataframe(model.summary2().tables[1], use_container_width=False, height=200)
|
|
|
|
| 166 |
|
| 167 |
+
y_true, y_pred = df_teste['SalePrice'], model.predict(df_teste)
|
|
|
|
|
|
|
| 168 |
if usar_logy:
|
| 169 |
+
y_true, y_pred = np.exp(y_true), np.exp(y_pred)
|
| 170 |
+
metrics = pd.DataFrame({
|
| 171 |
+
'Métrica': ['R²','RMSE','MAE'],
|
| 172 |
+
'Valor': [r2_score(y_true, y_pred),
|
| 173 |
+
mean_squared_error(y_true, y_pred, squared=False),
|
| 174 |
+
mean_absolute_error(y_true, y_pred)]
|
| 175 |
+
})
|
| 176 |
+
st.dataframe(metrics, use_container_width=False, height=120)
|
| 177 |
+
|
| 178 |
+
# Gráficos diagnósticos
|
| 179 |
+
residuos, fitted = model.resid, model.fittedvalues
|
| 180 |
cols = st.columns(3)
|
| 181 |
with cols[0]:
|
| 182 |
+
fig_r, ax_r = plt.subplots(figsize=(3,3))
|
| 183 |
ax_r.scatter(fitted, residuos, alpha=0.5)
|
| 184 |
ax_r.axhline(0, color='red', linestyle='--')
|
| 185 |
st.pyplot(fig_r, clear_figure=True, use_container_width=False)
|
| 186 |
with cols[1]:
|
| 187 |
+
fig_q, ax_q = plt.subplots(figsize=(3,3))
|
| 188 |
+
sm.qqplot(residuos, line='45', fit=True, ax=ax_q)
|
| 189 |
st.pyplot(fig_q, clear_figure=True, use_container_width=False)
|
| 190 |
with cols[2]:
|
| 191 |
+
fig_h, ax_h = plt.subplots(figsize=(3,3))
|
| 192 |
sns.histplot(residuos, kde=True, ax=ax_h)
|
| 193 |
st.pyplot(fig_h, clear_figure=True, use_container_width=False)
|