ProvaEADI / app.py
FernandezUNB's picture
Update app.py
f94c886 verified
import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
import seaborn as sns
from scipy import stats
from sklearn.model_selection import train_test_split
from sklearn.linear_model import LinearRegression
from sklearn.preprocessing import StandardScaler
from sklearn.metrics import r2_score, mean_squared_error, mean_absolute_error
import gradio as gr
import warnings
warnings.filterwarnings('ignore')
# Configurações de visualização
sns.set_style("whitegrid")
plt.rcParams['figure.figsize'] = (12, 6)
plt.rcParams['font.size'] = 10
class HousePricePredictor:
def __init__(self):
self.model = None
self.scaler = None
self.df = None
self.is_trained = False
self.selected_features = None
self._data_loaded = False
self.stats = {
'n_imoveis': 0,
'n_caracteristicas': 0,
'preco_medio': 0
}
def load_data(self):
"""Carrega dados do arquivo kc_house_data.csv"""
try:
if not self._data_loaded:
print("📂 Tentando carregar kc_house_data.csv...")
# Tentar carregar do arquivo local (Hugging Face Files)
self.df = pd.read_csv('kc_house_data.csv')
# Limpeza básica dos dados
self._clean_data()
# Atualizar estatísticas
self.stats = {
'n_imoveis': self.df.shape[0],
'n_caracteristicas': self.df.shape[1],
'preco_medio': self.df['price'].mean()
}
self._data_loaded = True
print(f"✅ Dados carregados: {self.df.shape[0]} imóveis × {self.df.shape[1]} características")
return f"✅ Dados carregados: {self.df.shape[0]} imóveis × {self.df.shape[1]} características"
else:
return f"✅ Dados já carregados: {self.df.shape[0]} imóveis × {self.df.shape[1]} características"
except Exception as e:
print(f"❌ Erro ao carregar arquivo: {e}")
return f"❌ Erro ao carregar dados: {str(e)}"
def _clean_data(self):
"""Faz limpeza básica dos dados"""
# Remover colunas não numéricas problemáticas
if 'date' in self.df.columns:
self.df = self.df.drop(columns=['date'])
if 'id' in self.df.columns:
self.df = self.df.drop(columns=['id'])
# Remover linhas com valores missing
self.df = self.df.dropna()
# Remover outliers extremos no preço
Q1 = self.df['price'].quantile(0.01)
Q3 = self.df['price'].quantile(0.99)
self.df = self.df[(self.df['price'] >= Q1) & (self.df['price'] <= Q3)]
print(f"📊 Dados limpos: {self.df.shape[0]} imóveis")
print(f"💰 Preço médio: ${self.df['price'].mean():,.2f}")
def get_numeric_features(self):
"""Retorna lista de features numéricas (excluindo price)"""
if self.df is None:
return []
numeric_features = self.df.select_dtypes(include=[np.number]).columns.tolist()
if 'price' in numeric_features:
numeric_features.remove('price')
return numeric_features
def get_dataset_stats(self):
"""Retorna estatísticas do dataset para display seguro"""
if self.df is not None:
return {
'n_imoveis': self.df.shape[0],
'n_caracteristicas': self.df.shape[1],
'preco_medio': self.df['price'].mean()
}
else:
return {
'n_imoveis': 0,
'n_caracteristicas': 0,
'preco_medio': 0
}
def train_model(self, selected_features):
"""Treina o modelo com as features selecionadas"""
try:
if self.df is None:
return False, "❌ Dados não carregados"
if not selected_features:
return False, "❌ Selecione pelo menos uma feature"
self.selected_features = selected_features
X = self.df[selected_features]
y = np.log1p(self.df['price']) # Transformação logarítmica
# Dividir dados
X_train, X_test, y_train, y_test = train_test_split(
X, y, test_size=0.2, random_state=42
)
# Padronizar features
self.scaler = StandardScaler()
X_train_scaled = self.scaler.fit_transform(X_train)
X_test_scaled = self.scaler.transform(X_test)
# Treinar modelo
self.model = LinearRegression()
self.model.fit(X_train_scaled, y_train)
# Fazer previsões
y_pred_train = self.model.predict(X_train_scaled)
y_pred_test = self.model.predict(X_test_scaled)
# Calcular métricas
y_pred_train_orig = np.expm1(y_pred_train)
y_pred_test_orig = np.expm1(y_pred_test)
y_train_orig = np.expm1(y_train)
y_test_orig = np.expm1(y_test)
r2_train = r2_score(y_train_orig, y_pred_train_orig)
r2_test = r2_score(y_test_orig, y_pred_test_orig)
rmse_test = np.sqrt(mean_squared_error(y_test_orig, y_pred_test_orig))
mae_test = mean_absolute_error(y_test_orig, y_pred_test_orig)
self.is_trained = True
# Coeficientes
coeficientes = pd.DataFrame({
'Feature': selected_features,
'Coeficiente': self.model.coef_,
'Impacto_Absoluto': np.abs(self.model.coef_)
}).sort_values('Impacto_Absoluto', ascending=False)
return True, {
'r2_train': r2_train,
'r2_test': r2_test,
'rmse_test': rmse_test,
'mae_test': mae_test,
'top_features': coeficientes.to_dict('records'),
'selected_features': selected_features
}
except Exception as e:
return False, f"❌ Erro no treinamento: {str(e)}"
def predict_price(self, input_features_dict):
"""Faz previsão de preço para novas entradas"""
if not self.is_trained:
return None, "❌ Modelo não treinado"
try:
# Criar array de features na ordem correta
input_array = []
for feature in self.selected_features:
input_array.append(float(input_features_dict[feature]))
input_array = np.array(input_array).reshape(1, -1)
# Padronizar
input_scaled = self.scaler.transform(input_array)
# Fazer previsão
pred_log = self.model.predict(input_scaled)[0]
pred_original = np.expm1(pred_log)
return pred_original, None
except Exception as e:
return None, f"❌ Erro na previsão: {str(e)}"
# Instanciar o predictor
predictor = HousePricePredictor()
# Carregar dados automaticamente ao iniciar
print("🚀 Iniciando aplicação...")
initial_message = predictor.load_data()
initial_features = predictor.get_numeric_features()
dataset_stats = predictor.get_dataset_stats()
print(f"📊 Features disponíveis: {initial_features}")
# Funções para a interface Gradio
def load_data_action():
"""Carrega os dados - função para o botão"""
message = predictor.load_data()
features = predictor.get_numeric_features()
# Criar checkboxes para features
feature_checkboxes = []
if features and predictor.df is not None:
# Calcular correlações com preço
correlations = predictor.df.corr()['price'].abs().sort_values(ascending=False)
# Selecionar automaticamente as 6 features mais correlacionadas
top_features = []
for feature in correlations.index:
if feature != 'price' and len(top_features) < 6:
top_features.append(feature)
for feature in features:
corr_value = correlations.get(feature, 0)
feature_checkboxes.append(
gr.Checkbox(
label=f"{feature} (corr: {corr_value:.3f})",
value=feature in top_features,
info=f"Média: {predictor.df[feature].mean():.1f}"
)
)
return message, gr.Column(feature_checkboxes), gr.update(visible=True)
def get_selected_features_from_checkboxes(*checkbox_values):
"""Converte valores dos checkboxes para lista de features selecionadas"""
features = predictor.get_numeric_features()
selected = []
for i, is_checked in enumerate(checkbox_values):
if is_checked and i < len(features):
selected.append(features[i])
return selected
def train_model_action(*checkbox_values):
"""Treina o modelo com features selecionadas"""
selected_features = get_selected_features_from_checkboxes(*checkbox_values)
success, result = predictor.train_model(selected_features)
if success:
metrics_text = f"""
## 📊 Resultados do Modelo
### Métricas de Desempenho:
- **R² Treino**: {result['r2_train']:.4f}
- **R² Teste**: {result['r2_test']:.4f}
- **RMSE Teste**: ${result['rmse_test']:,.0f}
- **MAE Teste**: ${result['mae_test']:,.0f}
### 🎯 Features por Importância:
"""
for i, feature in enumerate(result['top_features']):
direction = "📈 Aumenta preço" if feature['Coeficiente'] > 0 else "📉 Diminui preço"
metrics_text += f"\n{i+1}. **{feature['Feature']}**: {feature['Coeficiente']:.4f} ({direction})"
metrics_text += f"\n\n**Total de features usadas**: {len(selected_features)}"
return metrics_text, result, gr.update(visible=True)
else:
return result, None, gr.update(visible=False)
def create_correlation_plot():
"""Cria gráfico de correlação"""
if predictor.df is None:
return None
try:
# Selecionar features mais importantes
numeric_cols = predictor.df.select_dtypes(include=[np.number]).columns.tolist()
if len(numeric_cols) > 8:
correlations = predictor.df.corr()['price'].abs().sort_values(ascending=False)
top_features = correlations.index[:8].tolist()
else:
top_features = numeric_cols
corr_matrix = predictor.df[top_features].corr()
fig, ax = plt.subplots(figsize=(12, 10))
sns.heatmap(corr_matrix, annot=True, fmt='.2f', cmap='RdYlBu', center=0,
square=True, linewidths=0.5, cbar_kws={"shrink": 0.8}, ax=ax)
ax.set_title('🔗 Matriz de Correlação - Dataset Real King County', fontsize=14, fontweight='bold')
plt.tight_layout()
return fig
except Exception as e:
print(f"Erro no gráfico de correlação: {e}")
return None
def create_feature_analysis_plot(selected_feature):
"""Cria gráfico de análise para uma feature específica"""
if predictor.df is None or not selected_feature:
return None, None
try:
# Gráfico 1: Distribuição
fig1, ax1 = plt.subplots(figsize=(10, 5))
ax1.hist(predictor.df[selected_feature], bins=30, edgecolor='black', alpha=0.7, color='skyblue')
ax1.axvline(predictor.df[selected_feature].mean(), color='red', linestyle='--', linewidth=2,
label=f'Média: {predictor.df[selected_feature].mean():.2f}')
ax1.set_xlabel(selected_feature)
ax1.set_ylabel('Frequência')
ax1.set_title(f'📊 Distribuição de {selected_feature}')
ax1.legend()
ax1.grid(True, alpha=0.3)
plt.tight_layout()
# Gráfico 2: Relação com preço
fig2, ax2 = plt.subplots(figsize=(10, 5))
if predictor.df[selected_feature].nunique() < 10:
# Boxplot para variáveis categóricas
data_to_plot = []
categories = sorted(predictor.df[selected_feature].unique())
for cat in categories:
data_to_plot.append(predictor.df[predictor.df[selected_feature] == cat]['price'])
ax2.boxplot(data_to_plot, labels=categories)
else:
# Scatter plot para variáveis contínuas
ax2.scatter(predictor.df[selected_feature], predictor.df['price'], alpha=0.3, s=20, color='steelblue')
# Linha de tendência
z = np.polyfit(predictor.df[selected_feature], predictor.df['price'], 1)
p = np.poly1d(z)
x_range = np.linspace(predictor.df[selected_feature].min(), predictor.df[selected_feature].max(), 100)
ax2.plot(x_range, p(x_range), "r--", linewidth=2, alpha=0.8, label='Tendência linear')
ax2.legend()
ax2.set_xlabel(selected_feature)
ax2.set_ylabel('Preço ($)')
correlation = predictor.df[selected_feature].corr(predictor.df['price'])
ax2.set_title(f'💰 Preço vs {selected_feature} (Corr: {correlation:.3f})')
ax2.grid(True, alpha=0.3)
# Formatar eixo y para dólares
ax2.yaxis.set_major_formatter(plt.FuncFormatter(lambda x, p: f'${x:,.0f}'))
plt.tight_layout()
return fig1, fig2
except Exception as e:
print(f"Erro nos gráficos de análise: {e}")
return None, None
def create_price_distribution_plot():
"""Cria gráfico da distribuição de preços"""
if predictor.df is None:
return None
fig, ax = plt.subplots(figsize=(12, 6))
ax.hist(predictor.df['price'], bins=50, edgecolor='black', alpha=0.7, color='steelblue')
ax.axvline(predictor.df['price'].mean(), color='red', linestyle='--', linewidth=2,
label=f'Média: ${predictor.df["price"].mean():,.0f}')
ax.axvline(predictor.df['price'].median(), color='green', linestyle='--', linewidth=2,
label=f'Mediana: ${predictor.df["price"].median():,.0f}')
ax.set_xlabel('Preço ($)')
ax.set_ylabel('Número de Imóveis')
ax.set_title('🏠 Distribuição dos Preços - Dataset Real King County')
ax.legend()
ax.grid(True, alpha=0.3)
# Formatar eixo x para dólares
ax.xaxis.set_major_formatter(plt.FuncFormatter(lambda x, p: f'${x:,.0f}'))
plt.tight_layout()
return fig
def get_feature_stats(feature):
"""Retorna estatísticas de uma feature"""
if predictor.df is None or feature not in predictor.df.columns:
return "Selecione uma feature para ver estatísticas"
stats = predictor.df[feature].describe()
correlation = predictor.df[feature].corr(predictor.df['price'])
return f"""
## 📈 Estatísticas de **{feature}**
**Valores:**
- Média: {stats['mean']:.2f}
- Mediana: {stats['50%']:.2f}
- Desvio Padrão: {stats['std']:.2f}
- Mínimo: {stats['min']:.2f}
- Máximo: {stats['max']:.2f}
**Distribuição:**
- 25º Percentil: {stats['25%']:.2f}
- 75º Percentil: {stats['75%']:.2f}
- Valores Únicos: {predictor.df[feature].nunique()}
**Relação com Preço:**
- Correlação: {correlation:.3f}
- Interpretação: {'Forte' if abs(correlation) > 0.5 else 'Moderada' if abs(correlation) > 0.3 else 'Fraca'} relação
"""
def create_prediction_inputs(metrics_result):
"""Cria inputs para previsão"""
if metrics_result is None or 'selected_features' not in metrics_result:
return []
inputs = []
for feature in metrics_result['selected_features']:
if feature in predictor.df.columns:
min_val = float(predictor.df[feature].min())
max_val = float(predictor.df[feature].max())
mean_val = float(predictor.df[feature].mean())
step = (max_val - min_val) / 100
if step < 0.1:
step = 0.1
elif step > 100:
step = 10
inputs.append(
gr.Slider(
label=f"🏠 {feature}",
minimum=min_val,
maximum=max_val,
value=mean_val,
step=step,
info=f"Range: {min_val:.1f} - {max_val:.1f}"
)
)
return inputs
def predict_price_action(*feature_values):
"""Faz previsão de preço"""
if not predictor.is_trained:
return "❌ Modelo não treinado. Treine o modelo primeiro.", None
try:
input_features = {}
if hasattr(predictor, 'selected_features') and predictor.selected_features:
for i, feature in enumerate(predictor.selected_features):
if i < len(feature_values):
input_features[feature] = feature_values[i]
else:
return "❌ Nenhuma feature selecionada", None
pred_price, error = predictor.predict_price(input_features)
if error:
return f"❌ {error}", None
features_summary = "\n".join([f"- **{k}**: {v:.2f}" for k, v in input_features.items()])
result_text = f"""
## 🏠 Previsão de Preço do Imóvel
### 💰 **Preço Estimado: ${pred_price:,.2f}**
### 📋 Características Informadas:
{features_summary}
---
*💡 Nota: Previsão baseada no modelo treinado com dados reais do King County.*
"""
return result_text, pred_price
except Exception as e:
return f"❌ Erro na previsão: {str(e)}", None
def get_dataset_info():
"""Retorna informações do dataset para display seguro"""
stats = predictor.get_dataset_stats()
return f"""
**📊 Dataset Carregado:**
- **Arquivo**: kc_house_data.csv
- **Imóveis**: {stats['n_imoveis']:,}
- **Características**: {stats['n_caracteristicas']}
- **Preço Médio**: ${stats['preco_medio']:,.2f}
"""
# Interface Gradio
with gr.Blocks(title="🏠 Análise e Previsão - King County Dataset Real") as demo:
gr.Markdown(
"""
# 🏠 Análise e Previsão de Preços de Imóveis
## 📊 Dataset Real - King County, Washington
### ℹ️ Sobre os Dados:
Este aplicativo utiliza o **dataset real** `kc_house_data.csv` do mercado imobiliário de King County.
Dados reais de vendas de imóveis com diversas características.
"""
)
# Status inicial
initial_status = gr.Markdown(f"**Status:** {initial_message}")
dataset_info = gr.Markdown(get_dataset_info())
with gr.Tab("🚀 Iniciar"):
gr.Markdown("### Bem-vindo ao Analisador de Dados Reais do King County!")
gr.Markdown(get_dataset_info())
gr.Markdown("""
**🎯 Funcionalidades:**
1. **📊 Análise Exploratória** - Gráficos com dados reais
2. **🤖 Treinar Modelo** - Machine Learning com features selecionadas
3. **💰 Fazer Previsão** - Estime preços baseado no modelo
4. **📚 Explicações** - Entenda as análises
""")
load_btn = gr.Button("🔄 Recarregar Dados", variant="secondary")
load_status = gr.Markdown()
feature_selection = gr.Column()
train_btn = gr.Button("🚀 Treinar Modelo", variant="primary", visible=False)
def update_after_load():
message = predictor.load_data()
info = get_dataset_info()
features = predictor.get_numeric_features()
# Criar checkboxes
feature_checkboxes = []
if features and predictor.df is not None:
correlations = predictor.df.corr()['price'].abs().sort_values(ascending=False)
top_features = []
for feature in correlations.index:
if feature != 'price' and len(top_features) < 6:
top_features.append(feature)
for feature in features:
corr_value = correlations.get(feature, 0)
feature_checkboxes.append(
gr.Checkbox(
label=f"{feature} (corr: {corr_value:.3f})",
value=feature in top_features
)
)
return message, info, gr.Column(feature_checkboxes), gr.update(visible=True)
load_btn.click(
update_after_load,
outputs=[load_status, dataset_info, feature_selection, train_btn]
)
with gr.Tab("📊 Análise Exploratória"):
gr.Markdown("### Explore os Dados Reais do King County")
with gr.Row():
with gr.Column():
gr.Markdown("#### 📈 Distribuição de Preços Reais")
price_plot_btn = gr.Button("🎨 Gerar Gráfico de Preços", variant="primary")
price_plot = gr.Plot()
with gr.Column():
gr.Markdown("#### 🔗 Correlações entre Variáveis")
correlation_btn = gr.Button("🔄 Gerar Matriz de Correlação", variant="primary")
correlation_plot = gr.Plot()
gr.Markdown("---")
gr.Markdown("#### 🔍 Análise Detalhada por Feature")
with gr.Row():
with gr.Column():
feature_selector = gr.Dropdown(
label="Selecione uma característica para análise detalhada",
choices=initial_features,
value=initial_features[0] if initial_features else None
)
feature_stats = gr.Markdown()
with gr.Column():
feature_analysis_btn = gr.Button("📈 Analisar Feature", variant="primary")
with gr.Row():
feature_dist_plot = gr.Plot(label="Distribuição da Feature")
feature_price_plot = gr.Plot(label="Relação com Preço")
# Eventos
price_plot_btn.click(create_price_distribution_plot, outputs=[price_plot])
correlation_btn.click(create_correlation_plot, outputs=[correlation_plot])
feature_analysis_btn.click(
create_feature_analysis_plot,
inputs=[feature_selector],
outputs=[feature_dist_plot, feature_price_plot]
)
# Inicializar estatísticas da primeira feature
if initial_features:
feature_stats.value = get_feature_stats(initial_features[0])
# Atualizar estatísticas quando feature mudar
feature_selector.change(
get_feature_stats,
inputs=[feature_selector],
outputs=[feature_stats]
)
with gr.Tab("🤖 Treinar Modelo"):
gr.Markdown("### Treine o Modelo com Dados Reais")
gr.Markdown("""
**🎯 Como Funciona:**
- Selecione as características para prever preços
- Features com alta correlação são melhores preditoras
- Modelo: **Regressão Linear** com dados reais
- **Dataset**: kc_house_data.csv (dados reais)
""")
train_output = gr.Markdown("Selecione as features e clique em 'Treinar Modelo'")
metrics_display = gr.JSON(label="Métricas Detalhadas", visible=False)
train_btn.click(
train_model_action,
inputs=[feature_selection],
outputs=[train_output, metrics_display, metrics_display]
)
with gr.Tab("💰 Fazer Previsão"):
gr.Markdown("### Faça Previsões com o Modelo Treinado")
prediction_inputs = gr.Column()
predict_btn = gr.Button("🎯 Calcular Preço do Imóvel", variant="primary", size="lg")
with gr.Row():
prediction_output = gr.Markdown("Preencha os valores e clique em 'Calcular Preço'")
price_result = gr.Number(
label="💵 Preço Previsto",
visible=False
)
# Atualizar inputs quando modelo for treinado
metrics_display.change(
create_prediction_inputs,
inputs=[metrics_display],
outputs=[prediction_inputs]
)
predict_btn.click(
predict_price_action,
inputs=[prediction_inputs],
outputs=[prediction_output, price_result]
).then(
lambda: gr.update(visible=True),
outputs=[price_result]
)
with gr.Tab("📚 Explicações"):
gr.Markdown(
"""
## 📊 Guia do Dataset Real King County
### 🏠 Sobre os Dados Reais
**King County** inclui Seattle e áreas metropolitanas. O dataset contém:
- **Vendas reais** de imóveis
- **Período**: Maio 2014 - Maio 2015
- **Características**: 21 colunas incluindo localização, tamanho, qualidade
- **Preços**: Variam de dezenas de milhares a milhões de dólares
### 📈 Variáveis Principais:
- **price**: Preço de venda (target)
- **sqft_living**: Área habitável (pés quadrados)
- **bedrooms**: Número de quartos
- **bathrooms**: Número de banheiros
- **floors**: Número de andares
- **waterfront**: Vista para água (0/1)
- **view**: Qualidade da vista (0-4)
- **condition**: Condição do imóvel (1-5)
- **grade**: Grau de construção (1-13)
- **yr_built**: Ano de construção
- **lat/long**: Coordenadas geográficas
### 🎯 Interpretação dos Gráficos
#### Distribuição de Preços
- Mostra a realidade do mercado imobiliário
- Geralmente assimétrica positiva (mais imóveis baratos)
- Presença de outliers (imóveis de luxo)
#### Matriz de Correlação
- Baseada em **dados reais**
- Relações observadas no mercado real
- Padrões que o modelo aprenderá
#### Análise por Feature
- Distribuições reais das características
- Relações observadas com preços de venda
- Insights do mercado real
### 🤖 Modelo de Machine Learning
- **Algoritmo**: Regressão Linear Múltipla
- **Base**: Dados reais de vendas
- **Aplicação**: Previsão de preços baseada em padrões históricos
### 💡 Insights do Mercado Real
- Features como **sqft_living** e **grade** têm alta correlação
- Localização (**lat/long**) é crucial para preços
- Características de qualidade impactam significativamente
- O modelo captura relações observadas no mercado real
"""
)
if __name__ == "__main__":
demo.launch()