Spaces:
Sleeping
Sleeping
| #!/usr/bin/env python | |
| # coding: utf-8 | |
| # ===================================================== | |
| # Dashboard - AmesHousing (Tarefa 4) - versão compacta | |
| # ===================================================== | |
| import streamlit as st | |
| import pandas as pd | |
| import matplotlib.pyplot as plt | |
| import seaborn as sns | |
| import plotly.express as px | |
| import numpy as np | |
| from scipy.stats import shapiro, levene, kruskal | |
| from statsmodels.formula.api import ols | |
| import statsmodels.api as sm | |
| from statsmodels.stats.diagnostic import het_breuschpagan | |
| from statsmodels.stats.outliers_influence import variance_inflation_factor | |
| from sklearn.model_selection import train_test_split | |
| from sklearn.metrics import mean_squared_error, mean_absolute_error, r2_score | |
| # ----------------------------------------------------- | |
| # Configuração da Página | |
| # ----------------------------------------------------- | |
| st.set_page_config( | |
| page_title="Dashboard - AmesHousing (Tarefa 4)", | |
| layout="wide", | |
| initial_sidebar_state="expanded" | |
| ) | |
| st.query_params.clear() | |
| st.markdown("<h1 style='text-align:center;color:#003366;'>Análise do Dataset AmesHousing</h1>", unsafe_allow_html=True) | |
| st.markdown("---") | |
| # ----------------------------------------------------- | |
| # Leitura do CSV | |
| # ----------------------------------------------------- | |
| def carregar_dados(): | |
| paths_tentativa = [ | |
| "AmesHousing.csv", | |
| "/mnt/data/AmesHousing.csv", | |
| "../Dados/AmesHousing.csv", | |
| ] | |
| for p in paths_tentativa: | |
| try: | |
| df = pd.read_csv(p) | |
| return df | |
| except: | |
| continue | |
| return pd.DataFrame() | |
| casa_data = carregar_dados() | |
| casa_data.columns = casa_data.columns.str.strip().str.replace(" ", "_") | |
| # ----------------------------- | |
| # Sidebar — Filtros + Seleção de Variáveis (Regressão) | |
| # ----------------------------- | |
| st.sidebar.markdown("### Filtros AmesHousing") | |
| bairros = st.sidebar.multiselect( | |
| "Selecione bairros", | |
| options=sorted(casa_data["Neighborhood"].dropna().unique()), | |
| default=None | |
| ) | |
| dados_filtrados = casa_data.copy() | |
| if bairros: | |
| dados_filtrados = dados_filtrados[dados_filtrados["Neighborhood"].isin(bairros)] | |
| st.sidebar.markdown("---") | |
| st.sidebar.subheader("Regressão Linear — Tarefa 4 (PPCA/UnB)") | |
| st.sidebar.markdown("Selecione variáveis para modelagem do **SalePrice** (alvo).") | |
| candidatos_numericas = [ | |
| c for c in [ | |
| 'Bedroom_AbvGr','Full_Bath','Half_Bath','TotRms_AbvGrd','Gr_Liv_Area', | |
| 'Garage_Cars','Garage_Area','Overall_Qual','Overall_Cond','Year_Built','Lot_Area', | |
| 'Fireplaces' | |
| ] if c in dados_filtrados.columns | |
| ] | |
| candidatos_categoricas = [c for c in ['Neighborhood','House_Style','Bldg_Type','Garage_Type','Kitchen_Qual'] if c in dados_filtrados.columns] | |
| feats_num = st.sidebar.multiselect("Numéricas", options=candidatos_numericas, default=['Bedroom_AbvGr','Garage_Cars','Gr_Liv_Area'] if 'Garage_Cars' in candidatos_numericas else candidatos_numericas[:2]) | |
| feats_cat = st.sidebar.multiselect("Categóricas", options=candidatos_categoricas, default=['Neighborhood'] if 'Neighborhood' in candidatos_categoricas else []) | |
| interagir = st.sidebar.checkbox("Adicionar interação entre duas variáveis", value=False) | |
| inter_1 = inter_2 = None | |
| if interagir: | |
| inter_1 = st.sidebar.selectbox("Variável 1 (para interação)", options=feats_num + feats_cat, index=0 if feats_num else 0) | |
| inter_2 = st.sidebar.selectbox("Variável 2 (para interação)", options=[v for v in feats_num + feats_cat if v != inter_1], index=0) | |
| usar_logy = st.sidebar.checkbox("Aplicar transformação log(SalePrice) caso pressupostos sejam violados", value=False) | |
| teste_size = st.sidebar.slider("Proporção de teste (holdout)", 0.1, 0.5, 0.2, 0.05) | |
| alpha_reg = st.sidebar.slider("Nível de significância (α) — Regressão", 0.01, 0.10, 0.05, 0.01) | |
| # >>> botão agora na sidebar <<< | |
| ajustar = st.sidebar.button("Ajustar modelo") | |
| # ------------------------------------------------- | |
| # Conteúdo principal — Análises | |
| # ------------------------------------------------- | |
| # Distribuição de Preço de Venda | |
| st.subheader("Distribuição do Preço de Venda") | |
| if not dados_filtrados.empty: | |
| fig, ax = plt.subplots(figsize=(5,3.5)) | |
| sns.histplot(dados_filtrados['SalePrice'], kde=True, ax=ax) | |
| st.pyplot(fig, clear_figure=True, use_container_width=False) | |
| # Boxplots | |
| st.subheader("Boxplots das Variáveis Selecionadas") | |
| variavel = st.selectbox( | |
| "Escolha a variável categórica para comparar preços:", | |
| ["Neighborhood","Garage_Type","Fireplaces"] | |
| ) | |
| if not dados_filtrados.empty and len(dados_filtrados[variavel].dropna().unique()) > 1: | |
| fig2, ax2 = plt.subplots(figsize=(5,3.5)) | |
| sns.boxplot(x=variavel, y="SalePrice", data=dados_filtrados, ax=ax2) | |
| plt.xticks(rotation=60) | |
| st.pyplot(fig2, clear_figure=True, use_container_width=False) | |
| # Scatter interativo | |
| st.subheader("Preço Médio de Venda por Bairro") | |
| if not dados_filtrados.empty: | |
| bairro_grouped = dados_filtrados.groupby('Neighborhood').agg( | |
| count=('SalePrice','size'), | |
| mean_price=('SalePrice','mean') | |
| ).reset_index() | |
| bairro_filtered = bairro_grouped[bairro_grouped['count'] >= 5] | |
| if not bairro_filtered.empty: | |
| fig3 = px.scatter( | |
| bairro_filtered, | |
| x='mean_price', y='Neighborhood', | |
| size='count', color='Neighborhood', | |
| labels={'mean_price': 'Preço Médio de Venda', 'Neighborhood':'Bairro'} | |
| ) | |
| fig3.update_layout(width=550, height=320) | |
| st.plotly_chart(fig3, use_container_width=False) | |
| # ================================================= | |
| # Regressão Linear — Tarefa 4 | |
| # ================================================= | |
| def construir_formula(y, feats_num, feats_cat, inter_1=None, inter_2=None): | |
| termos = feats_num + [f"C({c})" for c in feats_cat] | |
| if inter_1 and inter_2: | |
| a = f"C({inter_1})" if inter_1 in feats_cat else inter_1 | |
| b = f"C({inter_2})" if inter_2 in feats_cat else inter_2 | |
| termos.append(f"{a}:{b}") | |
| rhs = " + ".join(termos) if termos else "1" | |
| return f"{y} ~ {rhs}" | |
| if ajustar: # botão na sidebar | |
| cols_necessarias = ['SalePrice'] + feats_num + feats_cat | |
| if interagir and inter_1 and inter_2: | |
| cols_necessarias += [inter_1, inter_2] | |
| df_modelo = dados_filtrados[cols_necessarias].dropna().copy() | |
| if not df_modelo.empty: | |
| y_col = 'SalePrice' | |
| if usar_logy: | |
| df_modelo['SalePrice'] = np.log(df_modelo['SalePrice']) | |
| formula = construir_formula(y_col, feats_num, feats_cat, inter_1 if interagir else None, inter_2 if interagir else None) | |
| df_treino, df_teste = train_test_split(df_modelo, test_size=teste_size, random_state=42) | |
| model = ols(formula, data=df_treino).fit() | |
| st.code(formula) | |
| st.dataframe(model.summary2().tables[1], use_container_width=False, height=200) | |
| y_true, y_pred = df_teste['SalePrice'], model.predict(df_teste) | |
| if usar_logy: | |
| y_true, y_pred = np.exp(y_true), np.exp(y_pred) | |
| metrics = pd.DataFrame({ | |
| 'Métrica': ['R²','RMSE','MAE'], | |
| 'Valor': [r2_score(y_true, y_pred), | |
| mean_squared_error(y_true, y_pred, squared=False), | |
| mean_absolute_error(y_true, y_pred)] | |
| }) | |
| st.dataframe(metrics, use_container_width=False, height=120) | |
| # Gráficos diagnósticos compactos | |
| residuos, fitted = model.resid, model.fittedvalues | |
| cols = st.columns(3) | |
| with cols[0]: | |
| fig_r, ax_r = plt.subplots(figsize=(3,3)) | |
| ax_r.scatter(fitted, residuos, alpha=0.5) | |
| ax_r.axhline(0, color='red', linestyle='--') | |
| st.pyplot(fig_r, clear_figure=True, use_container_width=False) | |
| with cols[1]: | |
| fig_q, ax_q = plt.subplots(figsize=(3,3)) | |
| sm.qqplot(residuos, line='45', fit=True, ax=ax_q) | |
| st.pyplot(fig_q, clear_figure=True, use_container_width=False) | |
| with cols[2]: | |
| fig_h, ax_h = plt.subplots(figsize=(3,3)) | |
| sns.histplot(residuos, kde=True, ax=ax_h) | |
| st.pyplot(fig_h, clear_figure=True, use_container_width=False) |