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()