Spaces:
Sleeping
Sleeping
| import gradio as gr | |
| import numpy as np | |
| import pandas as pd | |
| import matplotlib.pyplot as plt | |
| import seaborn as sns | |
| from scipy import stats | |
| import warnings | |
| warnings.filterwarnings('ignore') | |
| # Bibliotecas para modelagem | |
| from sklearn.model_selection import train_test_split | |
| from sklearn.linear_model import LinearRegression | |
| from sklearn.preprocessing import StandardScaler | |
| from sklearn.metrics import mean_squared_error, mean_absolute_error, r2_score | |
| from sklearn.ensemble import RandomForestRegressor | |
| # Configuração visual | |
| plt.style.use('default') | |
| sns.set_palette("husl") | |
| plt.rcParams['figure.figsize'] = (10, 6) | |
| plt.rcParams['font.size'] = 10 | |
| # Cache manual para melhor performance | |
| _data_cache = {} | |
| def load_data(): | |
| """Carrega o dataset Ames Housing com cache manual""" | |
| if 'data' in _data_cache: | |
| return _data_cache['data'] | |
| try: | |
| url = 'http://jse.amstat.org/v19n3/decock/AmesHousing.txt' | |
| df = pd.read_csv(url, sep='\t') | |
| _data_cache['data'] = df | |
| return df | |
| except Exception as e: | |
| print(f"Erro ao carregar dados: {e}") | |
| return None | |
| # Todas as variáveis disponíveis | |
| all_features = [ | |
| 'Gr Liv Area', # Área habitável | |
| 'Overall Qual', # Qualidade geral | |
| 'Year Built', # Ano de construção | |
| 'Total Bsmt SF', # Área do porão | |
| 'Garage Cars', # Capacidade da garagem | |
| 'Full Bath', # Banheiros completos | |
| 'TotRms AbvGrd', # Total de cômodos | |
| 'Fireplaces', # Lareiras | |
| 'Overall Cond', # Condição geral | |
| '1st Flr SF', # Área do primeiro andar | |
| '2nd Flr SF', # Área do segundo andar | |
| 'Bedroom AbvGr', # Quartos acima do solo | |
| 'Kitchen Qual', # Qualidade da cozinha | |
| 'Garage Area', # Área da garagem | |
| 'Wood Deck SF', # Área do deck de madeira | |
| 'Open Porch SF', # Área da varanda aberta | |
| 'Lot Area', # Área do lote | |
| 'Year Remod/Add', # Ano de reforma | |
| 'Mas Vnr Area', # Área de revestimento | |
| 'BsmtFin SF 1' # Área acabada do porão 1 | |
| ] | |
| def preparar_dados(variaveis_selecionadas): | |
| """Prepara os dados para análise com as variáveis selecionadas""" | |
| cache_key = f'prepared_data_{str(sorted(variaveis_selecionadas))}' | |
| if cache_key in _data_cache: | |
| return _data_cache[cache_key] | |
| df = load_data() | |
| if df is None: | |
| return None | |
| # Verifica se todas as variáveis selecionadas existem no dataset | |
| variaveis_validas = [var for var in variaveis_selecionadas if var in df.columns] | |
| variaveis_validas.append('SalePrice') # Sempre inclui o target | |
| df_model = df[variaveis_validas].copy() | |
| # Tratamento de valores ausentes | |
| for col in variaveis_validas: | |
| if col != 'SalePrice' and df_model[col].isnull().sum() > 0: | |
| if df_model[col].dtype in ['float64', 'int64']: | |
| df_model[col].fillna(df_model[col].median(), inplace=True) | |
| else: | |
| df_model[col].fillna(df_model[col].mode()[0] if len(df_model[col].mode()) > 0 else 0, inplace=True) | |
| _data_cache[cache_key] = df_model | |
| return df_model | |
| def plot_distribuicao_preco(df): | |
| """Plota a distribuição do preço de venda""" | |
| fig, axes = plt.subplots(1, 2, figsize=(12, 4)) | |
| # Histograma | |
| axes[0].hist(df['SalePrice'], bins=50, edgecolor='black', alpha=0.7, color='skyblue') | |
| axes[0].set_xlabel('Preço de Venda ($)') | |
| axes[0].set_ylabel('Frequência') | |
| axes[0].set_title('Distribuição do Preço de Venda') | |
| axes[0].axvline(df['SalePrice'].mean(), color='red', linestyle='--', | |
| label=f'Média: ${df["SalePrice"].mean():,.0f}') | |
| axes[0].axvline(df['SalePrice'].median(), color='green', linestyle='--', | |
| label=f'Mediana: ${df["SalePrice"].median():,.0f}') | |
| axes[0].legend(fontsize=8) | |
| # Boxplot | |
| axes[1].boxplot(df['SalePrice'], vert=True) | |
| axes[1].set_ylabel('Preço de Venda ($)') | |
| axes[1].set_title('Boxplot do Preço de Venda') | |
| axes[1].grid(True, alpha=0.3) | |
| plt.tight_layout() | |
| return fig | |
| def plot_correlacao(df): | |
| """Plota a matriz de correlação""" | |
| # Calcula apenas correlações numéricas | |
| numeric_df = df.select_dtypes(include=[np.number]) | |
| correlation_matrix = numeric_df.corr() | |
| fig, ax = plt.subplots(figsize=(10, 8)) | |
| sns.heatmap(correlation_matrix, annot=True, fmt='.2f', cmap='coolwarm', | |
| center=0, square=True, linewidths=0.5, cbar_kws={"shrink": 0.8}, ax=ax) | |
| ax.set_title('Matriz de Correlação - Variáveis Selecionadas', fontsize=12, fontweight='bold') | |
| plt.tight_layout() | |
| return fig | |
| def plot_relacoes_bivariadas(df, variaveis_selecionadas): | |
| """Plota as relações bivariadas com SalePrice""" | |
| # Filtra apenas variáveis numéricas para os scatter plots | |
| numeric_vars = [var for var in variaveis_selecionadas if var in df.select_dtypes(include=[np.number]).columns] | |
| n_vars = len(numeric_vars) | |
| if n_vars == 0: | |
| # Gráfico vazio se não há variáveis numéricas | |
| fig, ax = plt.subplots(figsize=(8, 6)) | |
| ax.text(0.5, 0.5, 'Nenhuma variável numérica selecionada', | |
| ha='center', va='center', transform=ax.transAxes, fontsize=12) | |
| ax.set_title('Relações com Preço de Venda') | |
| return fig | |
| n_cols = 3 | |
| n_rows = (n_vars + n_cols - 1) // n_cols | |
| fig, axes = plt.subplots(n_rows, n_cols, figsize=(15, 4 * n_rows)) | |
| # Garante que axes seja sempre uma lista 2D | |
| if n_rows == 1 and n_cols == 1: | |
| axes = np.array([axes]) | |
| elif n_rows == 1: | |
| axes = axes | |
| else: | |
| axes = axes.ravel() | |
| for idx, feature in enumerate(numeric_vars): | |
| if idx >= len(axes): | |
| break | |
| axes[idx].scatter(df[feature], df['SalePrice'], alpha=0.5, s=8, color='steelblue') | |
| axes[idx].set_xlabel(feature) | |
| axes[idx].set_ylabel('SalePrice ($)') | |
| axes[idx].set_title(f'SalePrice vs {feature}', fontsize=10) | |
| # Adicionar linha de tendência apenas se há dados suficientes | |
| if len(df[feature].dropna()) > 1: | |
| valid_data = df[[feature, 'SalePrice']].dropna() | |
| if len(valid_data) > 1: | |
| z = np.polyfit(valid_data[feature], valid_data['SalePrice'], 1) | |
| p = np.poly1d(z) | |
| axes[idx].plot(valid_data[feature], p(valid_data[feature]), "r--", alpha=0.8, linewidth=1.5) | |
| # Correlação | |
| corr = valid_data[feature].corr(valid_data['SalePrice']) | |
| axes[idx].text(0.05, 0.95, f'r = {corr:.3f}', transform=axes[idx].transAxes, | |
| verticalalignment='top', fontsize=9, | |
| bbox=dict(boxstyle='round', facecolor='white', alpha=0.8)) | |
| # Remove eixos extras | |
| for idx in range(len(numeric_vars), len(axes)): | |
| if idx < len(axes): | |
| fig.delaxes(axes[idx]) | |
| plt.tight_layout() | |
| return fig | |
| def plot_residuos(y_test, y_pred, modelo_tipo): | |
| """Plota análise de resíduos""" | |
| residuos = y_test - y_pred | |
| fig, axes = plt.subplots(1, 2, figsize=(12, 4)) | |
| # Resíduos vs Preditos | |
| axes[0].scatter(y_pred, residuos, alpha=0.5, color='steelblue', s=20) | |
| axes[0].axhline(y=0, color='red', linestyle='--', linewidth=1) | |
| axes[0].set_xlabel('Valores Preditos') | |
| axes[0].set_ylabel('Resíduos') | |
| axes[0].set_title(f'Resíduos vs Preditos - {modelo_tipo}') | |
| axes[0].grid(True, alpha=0.3) | |
| # QQ-Plot dos resíduos | |
| stats.probplot(residuos, dist="norm", plot=axes[1]) | |
| axes[1].set_title(f'QQ-Plot dos Resíduos - {modelo_tipo}') | |
| plt.tight_layout() | |
| return fig | |
| def treinar_modelo(variaveis_selecionadas, usar_modelo_robusto=False): | |
| """Treina o modelo de regressão com as variáveis selecionadas""" | |
| cache_key = f'model_{str(sorted(variaveis_selecionadas))}_{usar_modelo_robusto}' | |
| if cache_key in _data_cache: | |
| return _data_cache[cache_key] | |
| df_model = preparar_dados(variaveis_selecionadas) | |
| if df_model is None: | |
| return None | |
| # Remove colunas não numéricas para o modelo | |
| X = df_model[variaveis_selecionadas].select_dtypes(include=[np.number]) | |
| y = df_model['SalePrice'] | |
| # Verifica se há dados suficientes | |
| if len(X.columns) == 0 or len(X) < 10: | |
| return None | |
| # Divisão treino/teste | |
| X_train, X_test, y_train, y_test = train_test_split( | |
| X, y, test_size=0.2, random_state=42 | |
| ) | |
| # Escalonamento | |
| scaler = StandardScaler() | |
| X_train_scaled = scaler.fit_transform(X_train) | |
| X_test_scaled = scaler.transform(X_test) | |
| if usar_modelo_robusto: | |
| # Random Forest (modelo robusto) | |
| modelo = RandomForestRegressor(n_estimators=100, random_state=42) | |
| modelo_tipo = "Random Forest" | |
| else: | |
| # Regressão Linear | |
| modelo = LinearRegression() | |
| modelo_tipo = "Regressão Linear" | |
| modelo.fit(X_train_scaled, y_train) | |
| y_pred = modelo.predict(X_test_scaled) | |
| # Métricas | |
| mse = mean_squared_error(y_test, y_pred) | |
| mae = mean_absolute_error(y_test, y_pred) | |
| r2 = r2_score(y_test, y_pred) | |
| # Feature importance para Random Forest | |
| if usar_modelo_robusto: | |
| importances = modelo.feature_importances_ | |
| feature_importance_df = pd.DataFrame({ | |
| 'Feature': X.columns, | |
| 'Importance': importances | |
| }).sort_values('Importance', ascending=False) | |
| else: | |
| feature_importance_df = None | |
| resultados = { | |
| 'modelo': modelo, | |
| 'scaler': scaler, | |
| 'X_columns': X.columns.tolist(), | |
| 'X_test': X_test, | |
| 'y_test': y_test, | |
| 'y_pred': y_pred, | |
| 'metricas': { | |
| 'MSE': f"{mse:,.2f}", | |
| 'RMSE': f"${np.sqrt(mse):,.2f}", | |
| 'MAE': f"${mae:,.2f}", | |
| 'R²': f"{r2:.4f}" | |
| }, | |
| 'modelo_tipo': modelo_tipo, | |
| 'feature_importance': feature_importance_df | |
| } | |
| _data_cache[cache_key] = resultados | |
| return resultados | |
| def plot_importance_features(feature_importance_df): | |
| """Plota a importância das features para Random Forest""" | |
| if feature_importance_df is None or len(feature_importance_df) == 0: | |
| fig, ax = plt.subplots(figsize=(8, 4)) | |
| ax.text(0.5, 0.5, 'Nenhuma importância disponível', | |
| ha='center', va='center', transform=ax.transAxes, fontsize=12) | |
| ax.set_title('Importância das Variáveis') | |
| return fig | |
| fig, ax = plt.subplots(figsize=(10, 5)) | |
| sns.barplot(data=feature_importance_df, x='Importance', y='Feature', ax=ax) | |
| ax.set_title('Importância das Variáveis - Random Forest', fontsize=12) | |
| ax.set_xlabel('Importância') | |
| plt.tight_layout() | |
| return fig | |
| def analise_completa(variaveis_selecionadas, usar_modelo_robusto=False): | |
| """Executa análise completa e retorna todos os gráficos e métricas""" | |
| if not variaveis_selecionadas: | |
| raise gr.Error("❌ Selecione pelo menos uma variável para análise.") | |
| df_model = preparar_dados(variaveis_selecionadas) | |
| if df_model is None: | |
| raise gr.Error("❌ Erro ao carregar os dados. Verifique sua conexão com a internet.") | |
| # Gráficos de análise exploratória | |
| fig_distribuicao = plot_distribuicao_preco(df_model) | |
| fig_correlacao = plot_correlacao(df_model) | |
| fig_relacoes = plot_relacoes_bivariadas(df_model, variaveis_selecionadas) | |
| # Treinar modelo | |
| resultados = treinar_modelo(variaveis_selecionadas, usar_modelo_robusto) | |
| if resultados is None: | |
| raise gr.Error("❌ Erro ao treinar o modelo. Verifique as variáveis selecionadas.") | |
| # Gráfico de resíduos | |
| fig_residuos = plot_residuos( | |
| resultados['y_test'], | |
| resultados['y_pred'], | |
| resultados['modelo_tipo'] | |
| ) | |
| # Feature importance (apenas para Random Forest) | |
| if usar_modelo_robusto and resultados['feature_importance'] is not None: | |
| fig_importance = plot_importance_features(resultados['feature_importance']) | |
| else: | |
| # Cria um gráfico vazio quando não há feature importance | |
| fig_importance, ax = plt.subplots(figsize=(8, 4)) | |
| ax.text(0.5, 0.5, 'Feature Importance disponível apenas para Random Forest', | |
| ha='center', va='center', transform=ax.transAxes, fontsize=12) | |
| ax.set_title('Importância das Variáveis') | |
| plt.tight_layout() | |
| # Estatísticas descritivas | |
| stats_descritivas = df_model['SalePrice'].describe().reset_index() | |
| stats_descritivas.columns = ['Estatística', 'Valor'] | |
| stats_descritivas['Valor'] = stats_descritivas['Valor'].apply( | |
| lambda x: f"${x:,.2f}" if isinstance(x, (int, float)) else str(x) | |
| ) | |
| # Correlações com SalePrice (apenas numéricas) | |
| numeric_df = df_model.select_dtypes(include=[np.number]) | |
| correlacoes = numeric_df.corr()['SalePrice'].sort_values(ascending=False).reset_index() | |
| correlacoes.columns = ['Variável', 'Correlação'] | |
| correlacoes['Correlação'] = correlacoes['Correlação'].round(4) | |
| return (fig_distribuicao, fig_correlacao, fig_relacoes, fig_residuos, | |
| fig_importance, resultados['metricas'], stats_descritivas, correlacoes, | |
| resultados['modelo_tipo']) | |
| def predizer_preco(variaveis_selecionadas, inputs_dict, usar_modelo_robusto=False): | |
| """Prediz o preço baseado nas variáveis selecionadas e inputs""" | |
| try: | |
| if not variaveis_selecionadas: | |
| return 0 | |
| resultados = treinar_modelo(variaveis_selecionadas, usar_modelo_robusto) | |
| if resultados is None: | |
| return 0 | |
| # Prepara os dados de entrada na ordem correta | |
| input_data = [] | |
| for col in resultados['X_columns']: | |
| input_data.append(inputs_dict.get(col, 0)) | |
| input_array = np.array([input_data]) | |
| # Escalonar e prever | |
| input_scaled = resultados['scaler'].transform(input_array) | |
| preco_predito = resultados['modelo'].predict(input_scaled)[0] | |
| return max(0, preco_predito) | |
| except Exception as e: | |
| print(f"Erro na predição: {e}") | |
| return 0 | |
| # Interface Gradio | |
| with gr.Blocks( | |
| title="Análise de Dados Imobiliários - Ames Housing", | |
| css=""" | |
| .gradio-container { | |
| max-width: 1400px !important; | |
| } | |
| .container { | |
| max-width: 1400px; | |
| margin: auto; | |
| } | |
| """ | |
| ) as demo: | |
| gr.Markdown(""" | |
| # 🏠 Análise de Dados Imobiliários - Ames Housing | |
| **Análise completa utilizando Machine Learning para prever preços de propriedades em Ames, Iowa** | |
| *Dataset: Ames Housing (2930 propriedades, 82 variáveis)* | |
| """) | |
| with gr.Row(): | |
| with gr.Column(scale=1): | |
| gr.Markdown("### 🎯 Seleção de Variáveis") | |
| # Checkboxes para seleção de variáveis | |
| variaveis_checkboxes = [] | |
| with gr.Group(): | |
| gr.Markdown("**Selecione as variáveis para análise:**") | |
| for i, feature in enumerate(all_features): | |
| checkbox = gr.Checkbox( | |
| label=feature, | |
| value=i < 9, # Primeiras 9 selecionadas por padrão | |
| elem_id=f"checkbox_{feature}" | |
| ) | |
| variaveis_checkboxes.append(checkbox) | |
| with gr.Row(): | |
| usar_robusto = gr.Checkbox( | |
| label="Usar Modelo Robusto (Random Forest)", | |
| value=False, | |
| info="Random Forest geralmente tem melhor performance" | |
| ) | |
| btn_analisar = gr.Button( | |
| "🔍 Executar Análise Completa", | |
| variant="primary", | |
| size="lg" | |
| ) | |
| with gr.Tabs(): | |
| with gr.TabItem("📊 Análise Exploratória"): | |
| with gr.Row(): | |
| plot_dist = gr.Plot(label="Distribuição do Preço") | |
| with gr.Row(): | |
| plot_corr = gr.Plot(label="Matriz de Correlação") | |
| with gr.Row(): | |
| plot_rel = gr.Plot(label="Relações com Preço") | |
| with gr.TabItem("🤖 Modelo & Métricas"): | |
| with gr.Row(): | |
| tipo_modelo = gr.Textbox( | |
| label="🎯 Tipo de Modelo", | |
| interactive=False, | |
| value="Clique em 'Executar Análise' para começar" | |
| ) | |
| with gr.Row(): | |
| metricas = gr.JSON( | |
| label="📈 Métricas de Desempenho", | |
| value={"Status": "Aguardando análise..."} | |
| ) | |
| with gr.Row(): | |
| with gr.Column(): | |
| stats_desc = gr.DataFrame( | |
| label="📋 Estatísticas Descritivas - SalePrice", | |
| value=[["Aguardando...", "Aguardando..."]], | |
| headers=["Estatística", "Valor"] | |
| ) | |
| with gr.Column(): | |
| correlacoes = gr.DataFrame( | |
| label="🔗 Correlações com SalePrice", | |
| value=[["Aguardando...", "Aguardando..."]], | |
| headers=["Variável", "Correlação"] | |
| ) | |
| with gr.TabItem("📉 Validação do Modelo"): | |
| with gr.Row(): | |
| plot_res = gr.Plot(label="Análise de Resíduos") | |
| with gr.Row(): | |
| plot_imp = gr.Plot(label="Importância das Variáveis") | |
| with gr.TabItem("🎲 Simular Predição"): | |
| gr.Markdown(""" | |
| ### 💰 Simule o Preço de uma Propriedade | |
| Ajuste as características abaixo para prever o preço de venda: | |
| """) | |
| # Inputs dinâmicos para predição | |
| input_components = {} | |
| with gr.Row(): | |
| columns = [gr.Column() for _ in range(3)] | |
| for i, feature in enumerate(all_features): | |
| col_idx = i % 3 | |
| with columns[col_idx]: | |
| if feature in ['Overall Qual', 'Overall Cond']: | |
| input_comp = gr.Slider(1, 10, value=5, step=1, label=feature) | |
| elif feature in ['Garage Cars', 'Full Bath', 'Fireplaces', 'Bedroom AbvGr', 'TotRms AbvGrd']: | |
| input_comp = gr.Slider(0, 10, value=2, step=1, label=feature) | |
| elif 'Year' in feature: | |
| input_comp = gr.Slider(1800, 2020, value=1990, label=feature) | |
| elif 'Area' in feature or 'SF' in feature: | |
| input_comp = gr.Slider(0, 5000, value=1000, label=f"{feature} (sq ft)") | |
| else: | |
| input_comp = gr.Slider(0, 5000, value=1000, label=feature) | |
| input_components[feature] = input_comp | |
| with gr.Row(): | |
| btn_predizer = gr.Button("💰 Calcular Preço Predito", variant="secondary", size="lg") | |
| predicao = gr.Number( | |
| label="🏷️ Preço Predito ($)", | |
| interactive=False, | |
| value=0 | |
| ) | |
| # Funções de callback | |
| def get_selected_variables(*checkbox_values): | |
| """Retorna a lista de variáveis selecionadas""" | |
| selected = [] | |
| for i, is_selected in enumerate(checkbox_values): | |
| if is_selected: | |
| selected.append(all_features[i]) | |
| return selected | |
| def executar_analise(*args): | |
| try: | |
| # Os primeiros len(all_features) argumentos são os checkboxes | |
| checkbox_values = args[:len(all_features)] | |
| usar_robusto = args[len(all_features)] | |
| variaveis_selecionadas = get_selected_variables(*checkbox_values) | |
| if not variaveis_selecionadas: | |
| # Retorna valores padrão se nenhuma variável selecionada | |
| empty_fig, ax = plt.subplots(figsize=(8, 4)) | |
| ax.text(0.5, 0.5, 'Selecione variáveis para ver a análise', | |
| ha='center', va='center', transform=ax.transAxes, fontsize=12) | |
| ax.set_title('Aguardando seleção') | |
| plt.tight_layout() | |
| empty_data = [["Selecione variáveis", "Selecione variáveis"]] | |
| empty_json = {"Status": "Selecione variáveis para análise"} | |
| return (empty_fig, empty_fig, empty_fig, empty_fig, empty_fig, | |
| empty_json, empty_data, empty_data, "Nenhum modelo") | |
| resultados = analise_completa(variaveis_selecionadas, usar_robusto) | |
| # Garante que retornamos exatamente 9 valores | |
| output = list(resultados) | |
| return output | |
| except Exception as e: | |
| # Retorna gráficos de erro em caso de falha | |
| error_fig, ax = plt.subplots(figsize=(8, 4)) | |
| ax.text(0.5, 0.5, f'Erro: {str(e)}', | |
| ha='center', va='center', transform=ax.transAxes, fontsize=10) | |
| ax.set_title('Erro na análise') | |
| plt.tight_layout() | |
| error_data = [["Erro", str(e)]] | |
| error_json = {"Erro": str(e)} | |
| return (error_fig, error_fig, error_fig, error_fig, error_fig, | |
| error_json, error_data, error_data, "Erro") | |
| def predizer_preco_wrapper(*args): | |
| try: | |
| # Os primeiros len(all_features) argumentos são os checkboxes | |
| checkbox_values = args[:len(all_features)] | |
| # Os próximos len(all_features) argumentos são os valores dos inputs | |
| input_values = args[len(all_features):2*len(all_features)] | |
| usar_robusto = args[2*len(all_features)] | |
| variaveis_selecionadas = get_selected_variables(*checkbox_values) | |
| if not variaveis_selecionadas: | |
| return 0 | |
| # Cria dicionário com os inputs | |
| inputs_dict = {} | |
| for i, feature in enumerate(all_features): | |
| inputs_dict[feature] = input_values[i] | |
| return predizer_preco(variaveis_selecionadas, inputs_dict, usar_robusto) | |
| except Exception as e: | |
| print(f"Erro na predição: {e}") | |
| return 0 | |
| # Conectando os callbacks | |
| btn_analisar.click( | |
| fn=executar_analise, | |
| inputs=variaveis_checkboxes + [usar_robusto], | |
| outputs=[plot_dist, plot_corr, plot_rel, plot_res, plot_imp, | |
| metricas, stats_desc, correlacoes, tipo_modelo] | |
| ) | |
| btn_predizer.click( | |
| fn=predizer_preco_wrapper, | |
| inputs=variaveis_checkboxes + list(input_components.values()) + [usar_robusto], | |
| outputs=[predicao] | |
| ) | |
| # Informações adicionais | |
| gr.Markdown(""" | |
| ## 📝 Sobre o Projeto | |
| Desenvolvido como parte do Programa de Pós-graduação em Computação Aplicada - PPCA/UnB | |
| **Dataset:** [Ames Housing](http://jse.amstat.org/v19n3/decock/AmesHousing.txt) | |
| **Interpretação das Métricas:** | |
| - **R²**: Proporção da variância explicada (0-1, quanto maior melhor) | |
| - **RMSE**: Raiz do erro quadrático médio (em $, quanto menor melhor) | |
| - **MAE**: Erro absoluto médio (em $, quanto menor melhor) | |
| **💡 Dica:** Comece selecionando 3-5 variáveis para uma análise mais rápida. | |
| """) | |
| # Configuração para Spaces | |
| if __name__ == "__main__": | |
| demo.launch(debug=True) |