Tarefa3AEDI / app.py
FernandezUNB's picture
Update app.py
f75656a verified
# -*- coding: utf-8 -*-
"""Tarefa3_AEDI_Gradio.py
Aplicação Gradio para Análise ANOVA Dataset Ames Housing
"""
import gradio as gr
import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
import seaborn as sns
import warnings
from datetime import datetime
import io
import base64
from io import BytesIO
import tempfile
import os
# Bibliotecas estatísticas
from scipy import stats
from scipy.stats import f_oneway, levene, shapiro, kruskal
from statsmodels.stats.anova import anova_lm
from statsmodels.formula.api import ols
from statsmodels.stats.multicomp import pairwise_tukeyhsd
warnings.filterwarnings('ignore')
# Configurações de visualização
plt.style.use('default')
sns.set_palette("husl")
plt.rcParams['figure.figsize'] = (10, 6)
plt.rcParams['font.size'] = 10
# ========================================
# FUNÇÕES AUXILIARES
# ========================================
def save_plot_to_temp(fig):
"""Salvar gráfico em arquivo temporário"""
try:
# Criar arquivo temporário
temp_file = tempfile.NamedTemporaryFile(delete=False, suffix='.png')
temp_path = temp_file.name
temp_file.close()
# Salvar figura
fig.savefig(temp_path, dpi=100, bbox_inches='tight', facecolor='white')
plt.close(fig) # Fechar figura para liberar memória
return temp_path
except Exception as e:
print(f"Erro ao salvar gráfico: {e}")
return None
def criar_dataset_sintetico():
"""Criar dataset sintético"""
np.random.seed(42)
n_samples = 1000 # Reduzido para melhor performance
df = pd.DataFrame({
'SalePrice': np.random.normal(180000, 50000, n_samples).astype(int),
'GrLivArea': np.random.normal(1500, 400, n_samples).astype(int),
'BedroomAbvGr': np.random.choice([1, 2, 3, 4, 5], n_samples, p=[0.05, 0.15, 0.35, 0.35, 0.1]),
'YearBuilt': np.random.randint(1900, 2010, n_samples),
'YearRemodAdd': np.random.randint(1950, 2010, n_samples),
'GarageType': np.random.choice(['Attchd', 'Detchd', 'BuiltIn', None], n_samples, p=[0.6, 0.2, 0.1, 0.1]),
'Neighborhood': np.random.choice(['NAmes', 'Edwards', 'BrkSide', 'OldTown'], n_samples)
})
df['SalePrice'] = df['SalePrice'] + df['GrLivArea'] * 50 + df['BedroomAbvGr'] * 10000
df['SalePrice'] = np.clip(df['SalePrice'], 50000, 500000)
return df
def carregar_dados():
"""Carregar dados"""
try:
# Dataset menor para melhor performance
url = 'https://raw.githubusercontent.com/plotly/datasets/master/ames-housing.csv'
df = pd.read_csv(url)
return df, "Dataset Ames Housing carregado com sucesso!"
except Exception as e:
print(f"Erro ao carregar dataset: {e}")
df = criar_dataset_sintetico()
return df, "Dataset sintético criado para demonstração"
def preparar_caracteristicas(df):
"""Preparar características para análise"""
# 1. Presença de Garagem
if 'GarageType' in df.columns:
df['HasGarage'] = df['GarageType'].notna()
df['HasGarage'] = df['HasGarage'].map({True: 'Com Garagem', False: 'Sem Garagem'})
else:
df['HasGarage'] = np.random.choice(['Com Garagem', 'Sem Garagem'], len(df))
# 2. Categoria de Bairro
if 'Neighborhood' in df.columns:
neighborhood_prices = df.groupby('Neighborhood')['SalePrice'].mean()
if len(neighborhood_prices) >= 3:
low_price = neighborhood_prices.quantile(0.33)
high_price = neighborhood_prices.quantile(0.67)
def categorize_neighborhood(neighborhood):
if neighborhood in neighborhood_prices:
avg_price = neighborhood_prices[neighborhood]
if avg_price <= low_price:
return 'Econômico'
elif avg_price <= high_price:
return 'Intermediário'
else:
return 'Premium'
return 'Intermediário'
df['NeighborhoodCategory'] = df['Neighborhood'].apply(categorize_neighborhood)
else:
df['NeighborhoodCategory'] = 'Intermediário'
else:
df['NeighborhoodCategory'] = 'Intermediário'
# 3. Condição da Casa
if 'YearBuilt' in df.columns and 'YearRemodAdd' in df.columns:
df['YearsOld'] = 2010 - df['YearBuilt']
df['YearsSinceRemodel'] = 2010 - df['YearRemodAdd']
def categorize_house_condition(row):
years_old = row['YearsOld']
years_since_remodel = row.get('YearsSinceRemodel', years_old)
if years_old <= 20:
return 'Nova'
elif years_since_remodel <= 30:
return 'Reformada'
else:
return 'Antiga'
df['HouseCondition'] = df.apply(categorize_house_condition, axis=1)
else:
df['HouseCondition'] = np.random.choice(['Nova', 'Reformada', 'Antiga'], len(df))
# 4. Número de Quartos
if 'BedroomAbvGr' in df.columns:
def categorize_bedrooms(bedrooms):
if bedrooms <= 2:
return '1-2 Quartos'
elif bedrooms == 3:
return '3 Quartos'
elif bedrooms == 4:
return '4 Quartos'
else:
return '5+ Quartos'
df['BedroomCategory'] = df['BedroomAbvGr'].apply(categorize_bedrooms)
else:
df['BedroomCategory'] = np.random.choice(['1-2 Quartos', '3 Quartos', '4 Quartos', '5+ Quartos'], len(df))
# 5. Área da Casa
if 'GrLivArea' in df.columns:
df['AreaM2'] = df['GrLivArea'] * 0.092903
area_quartiles = df['AreaM2'].quantile([0.25, 0.5, 0.75])
def categorize_area(area_m2):
if area_m2 <= area_quartiles[0.25]:
return 'Pequena'
elif area_m2 <= area_quartiles[0.5]:
return 'Média-Pequena'
elif area_m2 <= area_quartiles[0.75]:
return 'Média-Grande'
else:
return 'Grande'
df['AreaCategory'] = df['AreaM2'].apply(categorize_area)
else:
df['AreaCategory'] = np.random.choice(['Pequena', 'Média-Pequena', 'Média-Grande', 'Grande'], len(df))
# Log do preço
df['LogSalePrice'] = np.log(df['SalePrice'])
return df
# ========================================
# FUNÇÕES DE ANÁLISE
# ========================================
def analise_exploratoria():
"""Análise exploratória dos dados"""
try:
df, mensagem = carregar_dados()
df = preparar_caracteristicas(df)
# Criar figura única mais simples
fig, ((ax1, ax2), (ax3, ax4)) = plt.subplots(2, 2, figsize=(12, 8))
fig.suptitle('Análise Exploratória - Preço de Venda', fontsize=14)
# Histograma
ax1.hist(df['SalePrice'], bins=20, alpha=0.7, color='skyblue', edgecolor='black')
ax1.set_title('Distribuição do Preço')
ax1.set_xlabel('Preço ($)')
ax1.set_ylabel('Frequência')
ax1.ticklabel_format(style='plain', axis='x')
# Boxplot
ax2.boxplot(df['SalePrice'], patch_artist=True)
ax2.set_title('Boxplot do Preço')
ax2.set_ylabel('Preço ($)')
# Scatter plot área vs preço
if 'GrLivArea' in df.columns:
ax3.scatter(df['GrLivArea'], df['SalePrice'], alpha=0.5)
ax3.set_title('Área vs Preço')
ax3.set_xlabel('Área (sqft)')
ax3.set_ylabel('Preço ($)')
else:
ax3.text(0.5, 0.5, 'Dados de área\nnão disponíveis',
ha='center', va='center', transform=ax3.transAxes)
# Log price
ax4.hist(df['LogSalePrice'], bins=20, alpha=0.7, color='lightcoral', edgecolor='black')
ax4.set_title('Distribuição Log(Preço)')
ax4.set_xlabel('Log(Preço)')
ax4.set_ylabel('Frequência')
plt.tight_layout()
# Salvar em arquivo temporário
temp_path = save_plot_to_temp(fig)
# Estatísticas
stats_desc = df['SalePrice'].describe()
stats_html = f"""
<div style="background: #f0f8ff; padding: 15px; border-radius: 8px; margin: 10px 0;">
<h3>📊 Estatísticas Descritivas</h3>
<p><strong>Média:</strong> ${stats_desc['mean']:,.0f}</p>
<p><strong>Mediana:</strong> ${stats_desc['50%']:,.0f}</p>
<p><strong>Desvio Padrão:</strong> ${stats_desc['std']:,.0f}</p>
<p><strong>Mínimo:</strong> ${stats_desc['min']:,.0f}</p>
<p><strong>Máximo:</strong> ${stats_desc['max']:,.0f}</p>
<p><strong>Amostras:</strong> {len(df):,}</p>
</div>
"""
return temp_path, stats_html, mensagem
except Exception as e:
error_msg = f"Erro na análise exploratória: {str(e)}"
return None, f"<div style='color: red; padding: 10px;'>{error_msg}</div>", error_msg
def visualizar_caracteristica(caracteristica):
"""Visualizar preços por característica"""
try:
df, _ = carregar_dados()
df = preparar_caracteristicas(df)
# Mapeamento de características
map_caracteristicas = {
'Presença de Garagem': 'HasGarage',
'Categoria de Bairro': 'NeighborhoodCategory',
'Condição da Casa': 'HouseCondition',
'Número de Quartos': 'BedroomCategory',
'Área da Casa': 'AreaCategory'
}
coluna = map_caracteristicas[caracteristica]
if coluna not in df.columns:
return None, f"<div style='color: red;'>Coluna {coluna} não encontrada</div>"
# Criar gráfico simples
fig, ax = plt.subplots(figsize=(10, 6))
# Boxplot
df_boxplot = df.dropna(subset=[coluna, 'SalePrice'])
if len(df_boxplot[coluna].unique()) > 0:
sns.boxplot(data=df_boxplot, x=coluna, y='SalePrice', ax=ax)
ax.set_title(f'Preço por {caracteristica}')
ax.tick_params(axis='x', rotation=45)
ax.ticklabel_format(style='plain', axis='y')
else:
ax.text(0.5, 0.5, 'Dados insuficientes', ha='center', va='center')
plt.tight_layout()
temp_path = save_plot_to_temp(fig)
# Estatísticas
stats_grupo = df.groupby(coluna)['SalePrice'].agg(['count', 'mean', 'std']).round(0)
stats_html = f"""
<div style="background: #f0fff0; padding: 15px; border-radius: 8px; margin: 10px 0;">
<h3>📈 Estatísticas por {caracteristica}</h3>
{stats_grupo.to_html(classes='table table-bordered')}
</div>
"""
return temp_path, stats_html
except Exception as e:
error_msg = f"Erro na visualização: {str(e)}"
return None, f"<div style='color: red; padding: 10px;'>{error_msg}</div>"
def executar_anova(caracteristica):
"""Executar análise ANOVA"""
try:
df, _ = carregar_dados()
df = preparar_caracteristicas(df)
map_caracteristicas = {
'Presença de Garagem': 'HasGarage',
'Categoria de Bairro': 'NeighborhoodCategory',
'Condição da Casa': 'HouseCondition',
'Número de Quartos': 'BedroomCategory',
'Área da Casa': 'AreaCategory'
}
coluna = map_caracteristicas[caracteristica]
if coluna not in df.columns:
return f"<div style='color: red;'>Coluna {coluna} não encontrada</div>"
# Preparar grupos
grupos = []
nomes_grupos = []
for nome, grupo in df.groupby(coluna):
valores = grupo['SalePrice'].dropna()
if len(valores) > 0:
grupos.append(valores)
nomes_grupos.append(nome)
if len(grupos) < 2:
return f"<div style='color: red;'>Necessário pelo menos 2 grupos (encontrados: {len(grupos)})</div>"
# ANOVA
f_stat, p_valor = f_oneway(*grupos)
significativo = p_valor < 0.05
# Resultado
cor = "#28a745" if significativo else "#dc3545"
resultado_html = f"""
<div style="border-left: 5px solid {cor}; padding: 15px; background: #f8f9fa; margin: 10px 0;">
<h3>🔬 Resultado ANOVA - {caracteristica}</h3>
<p><strong>F-statistic:</strong> {f_stat:.4f}</p>
<p><strong>p-value:</strong> {p_valor:.6f}</p>
<p><strong>Significativo (α=0.05):</strong> {'✅ SIM' if significativo else '❌ NÃO'}</p>
<p><strong>Grupos analisados:</strong> {len(grupos)}</p>
</div>
"""
return resultado_html
except Exception as e:
error_msg = f"Erro na ANOVA: {str(e)}"
return f"<div style='color: red; padding: 10px;'>{error_msg}</div>"
def analise_completa():
"""Análise completa"""
try:
df, mensagem = carregar_dados()
df = preparar_caracteristicas(df)
# Gráfico resumo
fig, axes = plt.subplots(2, 2, figsize=(12, 8))
caracteristicas = ['HasGarage', 'NeighborhoodCategory', 'HouseCondition', 'AreaCategory']
titulos = ['Garagem', 'Bairro', 'Condição', 'Área']
for i, (col, titulo) in enumerate(zip(caracteristicas, titulos)):
if i < 4 and col in df.columns:
ax = axes[i//2, i%2]
try:
df_plot = df.dropna(subset=[col, 'SalePrice'])
if len(df_plot[col].unique()) > 0:
sns.boxplot(data=df_plot, x=col, y='SalePrice', ax=ax)
ax.set_title(titulo, fontsize=10)
ax.tick_params(axis='x', rotation=45, labelsize=8)
ax.ticklabel_format(style='plain', axis='y')
else:
ax.text(0.5, 0.5, 'Sem dados', ha='center', va='center')
except:
ax.text(0.5, 0.5, 'Erro', ha='center', va='center')
plt.tight_layout()
temp_path = save_plot_to_temp(fig)
# Relatório
relatorio_html = f"""
<div style="background: linear-gradient(135deg, #667eea 0%, #764ba2 100%); color: white; padding: 20px; border-radius: 10px; text-align: center;">
<h2>📋 Relatório Completo</h2>
<p>Análise ANOVA - Ames Housing</p>
</div>
<div style="background: #f8f9fa; padding: 15px; border-radius: 8px; margin: 10px 0;">
<h3>📊 Resumo do Dataset</h3>
<p><strong>Amostras:</strong> {len(df):,} propriedades</p>
<p><strong>Preço médio:</strong> ${df['SalePrice'].mean():,.0f}</p>
<p><strong>Características analisadas:</strong> 5</p>
</div>
<div style="background: #e8f5e8; padding: 15px; border-radius: 8px; margin: 10px 0;">
<h3>🎯 Metodologia</h3>
<ul>
<li>Análise de Variância (ANOVA)</li>
<li>Teste de significância (α = 0.05)</li>
<li>5 características estratégicas</li>
<li>Dataset: Ames Housing</li>
</ul>
</div>
"""
return temp_path, relatorio_html, mensagem
except Exception as e:
error_msg = f"Erro na análise completa: {str(e)}"
return None, f"<div style='color: red; padding: 10px;'>{error_msg}</div>", error_msg
# ========================================
# INTERFACE GRADIO SIMPLIFICADA
# ========================================
with gr.Blocks(theme=gr.themes.Soft(), title="ANOVA - Ames Housing") as demo:
gr.Markdown("""
# 🏠 Análise ANOVA - Preços de Imóveis
**Análise Estatística do Dataset Ames Housing**
Explore como diferentes características influenciam os preços de venda de imóveis usando análise de variância (ANOVA).
""")
with gr.Tab("🔍 Análise Exploratória"):
gr.Markdown("### Explore os dados básicos do dataset")
btn_explorar = gr.Button("Executar Análise Exploratória", variant="primary")
with gr.Row():
img_exploratoria = gr.Image(label="Gráficos", height=300)
with gr.Row():
stats_exploratoria = gr.HTML()
status_exploratoria = gr.Textbox(label="Status", interactive=False)
btn_explorar.click(
analise_exploratoria,
outputs=[img_exploratoria, stats_exploratoria, status_exploratoria]
)
with gr.Tab("📊 Visualizar Características"):
gr.Markdown("### Compare preços por característica")
with gr.Row():
dropdown_caracteristica = gr.Dropdown(
choices=[
"Presença de Garagem",
"Categoria de Bairro",
"Condição da Casa",
"Número de Quartos",
"Área da Casa"
],
label="Selecione a característica",
value="Presença de Garagem"
)
btn_visualizar = gr.Button("Visualizar", variant="primary")
with gr.Row():
img_caracteristica = gr.Image(label="Gráfico", height=300)
with gr.Row():
stats_caracteristica = gr.HTML()
btn_visualizar.click(
visualizar_caracteristica,
inputs=dropdown_caracteristica,
outputs=[img_caracteristica, stats_caracteristica]
)
with gr.Tab("🔬 Teste ANOVA"):
gr.Markdown("### Execute análise ANOVA")
with gr.Row():
dropdown_anova = gr.Dropdown(
choices=[
"Presença de Garagem",
"Categoria de Bairro",
"Condição da Casa",
"Número de Quartos",
"Área da Casa"
],
label="Característica para ANOVA",
value="Presença de Garagem"
)
btn_anova = gr.Button("Executar ANOVA", variant="primary")
resultado_anova = gr.HTML(label="Resultado")
btn_anova.click(
executar_anova,
inputs=dropdown_anova,
outputs=resultado_anova
)
with gr.Tab("📋 Relatório"):
gr.Markdown("### Relatório completo da análise")
btn_relatorio = gr.Button("Gerar Relatório Completo", variant="primary")
with gr.Row():
img_relatorio = gr.Image(label="Gráficos do Relatório", height=300)
with gr.Row():
html_relatorio = gr.HTML()
status_relatorio = gr.Textbox(label="Status", interactive=False)
btn_relatorio.click(
analise_completa,
outputs=[img_relatorio, html_relatorio, status_relatorio]
)
with gr.Tab("ℹ️ Sobre"):
gr.Markdown("""
## Sobre esta Aplicação
**Objetivo:** Análise estatística de preços de imóveis usando ANOVA
**Dataset:** Ames Housing (preços de imóveis em Ames, Iowa)
**Características analisadas:**
- Presença de garagem
- Categoria do bairro
- Condição da casa
- Número de quartos
- Área construída
**Métodos:** ANOVA, análise exploratória, testes de hipótese
**Desenvolvido para:** Demonstração educacional
""")
# ========================================
# CONFIGURAÇÃO FINAL
# ========================================
if __name__ == "__main__":
# Configurações para Hugging Face Spaces
demo.launch(
server_name="0.0.0.0" if os.getenv('SPACE_ID') else "127.0.0.1",
server_port=7860,
share=False,
show_error=True
)