FernandezUNB commited on
Commit
7eb35c8
·
verified ·
1 Parent(s): 90f9b9a

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +345 -231
app.py CHANGED
@@ -16,35 +16,75 @@ sns.set_style("whitegrid")
16
  plt.rcParams['figure.figsize'] = (12, 6)
17
  plt.rcParams['font.size'] = 10
18
 
19
- # Carregar dados diretamente (sem upload)
20
- def load_sample_data():
21
- """Carrega dados de exemplo ou do arquivo local"""
22
- try:
23
- # Tenta carregar de uma URL ou arquivo local
24
- url = "https://raw.githubusercontent.com/guipsamora/pandas_exercises/master/04_Apply/Students_Alcohol_Consumption/student-mat.csv"
25
- # Para o Hugging Face, vamos usar dados de exemplo se não encontrar o arquivo
26
- df = pd.read_csv('kc_house_data.csv')
27
- return df
28
- except:
29
- # Dados de exemplo se o arquivo não existir
30
- print("Arquivo não encontrado, usando dados de exemplo...")
31
- np.random.seed(42)
32
- n_samples = 1000
33
- data = {
34
- 'price': np.random.normal(500000, 200000, n_samples),
35
- 'sqft_living': np.random.normal(2000, 800, n_samples),
36
- 'bedrooms': np.random.randint(1, 6, n_samples),
37
- 'bathrooms': np.random.uniform(1, 4, n_samples),
38
- 'floors': np.random.choice([1, 1.5, 2, 2.5, 3], n_samples),
39
- 'waterfront': np.random.choice([0, 1], n_samples, p=[0.95, 0.05]),
40
- 'view': np.random.randint(0, 5, n_samples),
41
- 'condition': np.random.randint(1, 6, n_samples),
42
- 'grade': np.random.randint(1, 14, n_samples),
43
- 'yr_built': np.random.randint(1900, 2015, n_samples),
44
- 'lat': np.random.uniform(47.2, 47.8, n_samples),
45
- 'long': np.random.uniform(-122.5, -121.8, n_samples)
46
- }
47
- return pd.DataFrame(data)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
48
 
49
  class HousePricePredictor:
50
  def __init__(self):
@@ -56,17 +96,12 @@ class HousePricePredictor:
56
  self.selected_features = None
57
 
58
  def load_data(self):
59
- """Carrega os dados"""
60
- self.df = load_sample_data()
61
- # Remover colunas não numéricas e ID se existirem
62
- non_numeric_cols = self.df.select_dtypes(exclude=[np.number]).columns
63
- if len(non_numeric_cols) > 0:
64
- self.df = self.df.drop(columns=non_numeric_cols)
65
-
66
- if 'id' in self.df.columns:
67
- self.df = self.df.drop(columns=['id'])
68
-
69
- return f"✅ Dados carregados: {self.df.shape[0]} linhas × {self.df.shape[1]} colunas"
70
 
71
  def get_numeric_features(self):
72
  """Retorna lista de features numéricas (excluindo price)"""
@@ -174,106 +209,160 @@ def load_data_action():
174
  message = predictor.load_data()
175
  features = predictor.get_numeric_features()
176
 
 
177
  feature_checkboxes = []
178
- if features:
179
- # Selecionar automaticamente as 5 features mais correlacionadas
180
  correlations = predictor.df.corr()['price'].abs().sort_values(ascending=False)
181
- top_features = correlations[1:6].index.tolist() # Exclui 'price'
 
 
 
 
 
182
 
183
  for feature in features:
 
184
  feature_checkboxes.append(
185
  gr.Checkbox(
186
- label=feature,
187
  value=feature in top_features,
188
- info=f"Corr: {predictor.df.corr()['price'].get(feature, 0):.3f}"
189
  )
190
  )
191
 
192
- return message, feature_checkboxes, gr.update(visible=True)
193
 
194
- def train_model_action(selected_features):
 
 
 
 
 
 
 
 
 
195
  """Treina o modelo com features selecionadas"""
196
- # Converter lista de booleanos para lista de nomes de features
197
- all_features = predictor.get_numeric_features()
198
- selected_feature_names = [feature for feature, selected in zip(all_features, selected_features) if selected]
199
 
200
- success, result = predictor.train_model(selected_feature_names)
201
 
202
  if success:
203
  metrics_text = f"""
204
- 📊 **Métricas do Modelo:**
205
 
206
- Treino: {result['r2_train']:.4f}
207
- Teste: {result['r2_test']:.4f}
208
- RMSE Teste: ${result['rmse_test']:,.2f}
209
- MAE Teste: ${result['mae_test']:,.2f}
 
210
 
211
- 🎯 **Features Selecionadas (por importância):**
212
  """
213
 
214
  for i, feature in enumerate(result['top_features']):
215
- impact = "📈 Aumenta preço" if feature['Coeficiente'] > 0 else "📉 Diminui preço"
216
- metrics_text += f"\n{i+1}. {feature['Feature']}: {feature['Coeficiente']:.4f} ({impact})"
 
 
217
 
218
  return metrics_text, result, gr.update(visible=True)
219
  else:
220
  return result, None, gr.update(visible=False)
221
 
222
- def create_correlation_plot(selected_features):
223
- """Cria gráfico de correlação das features selecionadas"""
224
- if predictor.df is None or not selected_features:
225
  return None
226
 
227
  try:
228
- features_to_plot = selected_features + ['price']
229
- corr_matrix = predictor.df[features_to_plot].corr()
 
 
 
 
 
 
230
 
231
- fig, ax = plt.subplots(figsize=(10, 8))
232
- sns.heatmap(corr_matrix, annot=True, fmt='.2f', cmap='coolwarm', center=0,
233
- square=True, linewidths=1, cbar_kws={"shrink": 0.8}, ax=ax)
234
- ax.set_title('Matriz de Correlação - Features Selecionadas', fontsize=14, fontweight='bold')
 
 
 
235
  plt.tight_layout()
236
  return fig
237
  except Exception as e:
238
  print(f"Erro no gráfico de correlação: {e}")
239
  return None
240
 
241
- def create_distribution_plot(selected_feature):
242
- """Cria gráfico de distribuição e relação com preço"""
243
  if predictor.df is None or not selected_feature:
244
  return None, None
245
 
246
  try:
247
- # Gráfico de distribuição
248
- fig1, ax1 = plt.subplots(figsize=(10, 4))
249
- ax1.hist(predictor.df[selected_feature], bins=30, edgecolor='black', alpha=0.7, color='lightblue')
 
 
 
 
250
  ax1.set_xlabel(selected_feature)
251
  ax1.set_ylabel('Frequência')
252
- ax1.set_title(f'Distribuição de {selected_feature}')
 
253
  ax1.grid(True, alpha=0.3)
254
- plt.tight_layout()
255
 
256
- # Gráfico de dispersão vs preço
257
- fig2, ax2 = plt.subplots(figsize=(10, 4))
258
- ax2.scatter(predictor.df[selected_feature], predictor.df['price'], alpha=0.5, s=20, color='steelblue')
259
- ax2.set_xlabel(selected_feature)
260
- ax2.set_ylabel('Preço ($)')
261
- correlation = predictor.df[selected_feature].corr(predictor.df['price'])
262
- ax2.set_title(f'Relação com Preço (Corr: {correlation:.3f})')
263
  ax2.grid(True, alpha=0.3)
264
 
265
- # Linha de tendência
266
- if len(predictor.df[selected_feature].unique()) > 10: # Só para variáveis contínuas
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
267
  z = np.polyfit(predictor.df[selected_feature], predictor.df['price'], 1)
268
  p = np.poly1d(z)
269
  x_range = np.linspace(predictor.df[selected_feature].min(), predictor.df[selected_feature].max(), 100)
270
- ax2.plot(x_range, p(x_range), "r--", linewidth=2, alpha=0.8)
 
271
 
 
272
  plt.tight_layout()
 
273
  return fig1, fig2
274
 
275
  except Exception as e:
276
- print(f"Erro nos gráficos de distribuição: {e}")
277
  return None, None
278
 
279
  def create_price_distribution_plot():
@@ -281,35 +370,58 @@ def create_price_distribution_plot():
281
  if predictor.df is None:
282
  return None
283
 
284
- fig, ax = plt.subplots(figsize=(10, 5))
285
- ax.hist(predictor.df['price'], bins=50, edgecolor='black', alpha=0.7, color='steelblue')
286
- ax.axvline(predictor.df['price'].mean(), color='red', linestyle='--', linewidth=2,
 
 
287
  label=f'Média: ${predictor.df["price"].mean():,.0f}')
288
- ax.axvline(predictor.df['price'].median(), color='green', linestyle='--', linewidth=2,
289
  label=f'Mediana: ${predictor.df["price"].median():,.0f}')
290
- ax.set_xlabel('Preço ($)')
291
- ax.set_ylabel('Frequência')
292
- ax.set_title('Distribuição dos Preços dos Imóveis')
293
- ax.legend()
294
- ax.grid(True, alpha=0.3)
 
 
 
 
 
 
 
 
 
 
295
  plt.tight_layout()
296
  return fig
297
 
298
  def get_feature_stats(feature):
299
  """Retorna estatísticas de uma feature"""
300
  if predictor.df is None or feature not in predictor.df.columns:
301
- return "Selecione uma feature"
302
 
303
  stats = predictor.df[feature].describe()
 
 
304
  return f"""
305
- **Estatísticas de {feature}:**
 
 
306
  - Média: {stats['mean']:.2f}
307
  - Mediana: {stats['50%']:.2f}
308
  - Desvio Padrão: {stats['std']:.2f}
309
  - Mínimo: {stats['min']:.2f}
310
  - Máximo: {stats['max']:.2f}
 
 
311
  - 25º Percentil: {stats['25%']:.2f}
312
  - 75º Percentil: {stats['75%']:.2f}
 
 
 
 
 
313
  """
314
 
315
  def create_prediction_inputs(metrics_result):
@@ -323,16 +435,22 @@ def create_prediction_inputs(metrics_result):
323
  min_val = float(predictor.df[feature].min())
324
  max_val = float(predictor.df[feature].max())
325
  mean_val = float(predictor.df[feature].mean())
326
- std_val = float(predictor.df[feature].std())
 
 
 
 
 
 
327
 
328
  inputs.append(
329
  gr.Slider(
330
- label=f"{feature}",
331
  minimum=min_val,
332
  maximum=max_val,
333
  value=mean_val,
334
- step=(max_val - min_val) / 100,
335
- info=f"Média: {mean_val:.2f} ± {std_val:.2f}"
336
  )
337
  )
338
 
@@ -341,16 +459,17 @@ def create_prediction_inputs(metrics_result):
341
  def predict_price_action(*feature_values):
342
  """Faz previsão de preço"""
343
  if not predictor.is_trained:
344
- return "❌ Modelo não treinado. Treine o modelo primeiro.", None
345
 
346
  try:
347
  # Criar dicionário com os valores das features
348
  input_features = {}
349
  if hasattr(predictor, 'selected_features') and predictor.selected_features:
350
  for i, feature in enumerate(predictor.selected_features):
351
- input_features[feature] = feature_values[i]
 
352
  else:
353
- return "❌ Nenhuma feature selecionada", None
354
 
355
  pred_price, error = predictor.predict_price(input_features)
356
 
@@ -361,14 +480,15 @@ def predict_price_action(*feature_values):
361
  features_summary = "\n".join([f"- **{k}**: {v:.2f}" for k, v in input_features.items()])
362
 
363
  result_text = f"""
364
- 🏠 **Previsão de Preço do Imóvel**
365
 
366
- 💰 **Preço Estimado: ${pred_price:,.2f}**
367
 
368
- **Características informadas:**
369
  {features_summary}
370
 
371
- *Nota: Esta é uma estimativa baseada no modelo de regressão linear treinado.*
 
372
  """
373
 
374
  return result_text, pred_price
@@ -377,119 +497,94 @@ def predict_price_action(*feature_values):
377
  return f"❌ Erro na previsão: {str(e)}", None
378
 
379
  # Interface Gradio
380
- with gr.Blocks(title="Análise e Previsão de Preços de Imóveis") as demo:
381
  gr.Markdown(
382
  """
383
  # 🏠 Análise e Previsão de Preços de Imóveis
 
384
 
385
- ## 📋 Sobre este Projeto
386
-
387
- Este aplicativo realiza análise exploratória e previsão de preços de imóveis usando:
388
- - **Regressão Linear Múltipla** para modelagem
389
- - **Análise estatística** das características dos imóveis
390
- - **Visualizações interativas** para compreensão dos dados
391
 
392
- ### 🎯 Objetivos dos Gráficos:
393
-
394
- 1. **Distribuição de Preços**: Mostra como os preços estão distribuídos (normal, assimétrica)
395
- 2. **Matriz de Correlação**: Revela relações entre features e preço
396
- 3. **Distribuição por Feature**: Analisa cada característica individualmente
397
- 4. **Relação com Preço**: Mostra como cada feature influencia o preço
398
 
399
  """
400
  )
401
 
402
- with gr.Tab("🚀 Inicialização"):
403
- gr.Markdown("### Passo 1: Carregar Dados")
404
- load_btn = gr.Button("📂 Carregar Dados do Dataset", variant="primary")
405
- load_status = gr.Markdown("Clique no botão para carregar os dados...")
406
-
407
- gr.Markdown("### Passo 2: Selecionar Features para o Modelo")
408
- feature_selection = gr.Column()
409
- train_btn = gr.Button("🤖 Treinar Modelo com Features Selecionadas", variant="primary", visible=False)
410
 
411
  load_btn.click(
412
  load_data_action,
413
  outputs=[load_status, feature_selection, train_btn]
414
  )
415
 
416
- with gr.Tab("📈 Análise Exploratória"):
417
- gr.Markdown("### Análise Completa dos Dados")
418
 
419
  with gr.Row():
420
  with gr.Column():
421
- gr.Markdown("#### Distribuição Geral de Preços")
422
- price_plot_btn = gr.Button("📊 Gerar Gráfico de Preços")
423
  price_plot = gr.Plot()
424
 
425
  with gr.Column():
426
- gr.Markdown("#### Estatísticas por Feature")
 
 
 
 
 
 
 
 
427
  feature_selector = gr.Dropdown(
428
- label="Selecione uma feature para análise detalhada",
429
  choices=[],
430
  interactive=True
431
  )
432
- feature_stats = gr.Markdown("Selecione uma feature...")
 
 
 
433
 
434
  with gr.Row():
435
- dist_plot1 = gr.Plot(label="Distribuição da Feature")
436
- dist_plot2 = gr.Plot(label="Relação com Preço")
437
-
438
- price_plot_btn.click(
439
- create_price_distribution_plot,
440
- outputs=[price_plot]
441
- )
442
-
443
- # Atualizar dropdown quando dados forem carregados
444
- def update_feature_selector():
445
- if predictor.df is not None:
446
- features = predictor.get_numeric_features()
447
- return gr.update(choices=features, value=features[0] if features else None)
448
- return gr.update(choices=[])
449
-
450
- load_btn.click(
451
- update_feature_selector,
452
- outputs=[feature_selector]
453
  )
454
-
455
  feature_selector.change(
456
  get_feature_stats,
457
  inputs=[feature_selector],
458
  outputs=[feature_stats]
459
- ).then(
460
- create_distribution_plot,
461
- inputs=[feature_selector],
462
- outputs=[dist_plot1, dist_plot2]
463
- )
464
-
465
- with gr.Tab("🔍 Correlações"):
466
- gr.Markdown("### Análise de Correlações entre Features")
467
- gr.Markdown("""
468
- **📊 Sobre a Matriz de Correlação:**
469
- - **Cores vermelhas**: Correlação positiva (quanto maior a feature, maior o preço)
470
- - **Cores azuis**: Correlação negativa (quanto maior a feature, menor o preço)
471
- - **Valores próximos de 1 ou -1**: Forte correlação
472
- - **Valores próximos de 0**: Fraca ou nenhuma correlação
473
- """)
474
-
475
- correlation_btn = gr.Button("🔄 Gerar Matriz de Correlação", variant="primary")
476
- correlation_plot = gr.Plot()
477
-
478
- correlation_btn.click(
479
- lambda: create_correlation_plot(predictor.get_numeric_features()),
480
- outputs=[correlation_plot]
481
  )
482
 
483
- with gr.Tab("🎯 Treinar Modelo"):
484
- gr.Markdown("### Treinamento do Modelo de Previsão")
485
  gr.Markdown("""
486
- **📝 Como funciona:**
487
- 1. Selecione as features que deseja usar no modelo
488
- 2. Features com alta correlação com preço geralmente são melhores preditoras
489
- 3. O modelo será treinado e avaliado automaticamente
490
  """)
491
 
492
- train_output = gr.Markdown("Selecione as features e clique em 'Treinar Modelo'")
 
 
493
  metrics_display = gr.JSON(label="Métricas Detalhadas", visible=False)
494
 
495
  train_btn.click(
@@ -498,18 +593,19 @@ with gr.Blocks(title="Análise e Previsão de Preços de Imóveis") as demo:
498
  outputs=[train_output, metrics_display, metrics_display]
499
  )
500
 
501
- with gr.Tab("💰 Fazer Previsão"):
502
  gr.Markdown("### Faça uma Previsão de Preço")
503
- gr.Markdown("Ajuste os valores das características para obter uma previsão de preço:")
504
 
505
  prediction_inputs = gr.Column()
506
- predict_btn = gr.Button("🎯 Calcular Preço do Imóvel", variant="primary")
507
 
508
- prediction_output = gr.Markdown("Preencha os valores e clique em 'Calcular Preço'")
509
- price_result = gr.Number(
510
- label="Preço Previsto ($)",
511
- visible=False
512
- )
 
513
 
514
  # Atualizar inputs quando modelo for treinado
515
  metrics_display.change(
@@ -527,49 +623,67 @@ with gr.Blocks(title="Análise e Previsão de Preços de Imóveis") as demo:
527
  outputs=[price_result]
528
  )
529
 
530
- with gr.Tab("📚 Explicações"):
531
  gr.Markdown(
532
  """
533
- ## 📊 Explicação dos Gráficos e Análises
534
-
535
- ### 1. Distribuição de Preços
536
- **Objetivo**: Entender a distribuição dos preços no mercado
537
- - **Histograma**: Mostra a frequência de imóveis em cada faixa de preço
538
- - **Linhas verticais**: Média (vermelha) e Mediana (verde)
539
- - **Interpretação**:
540
- - Distribuição normal: curva simétrica
541
- - Assimetria positiva: mais imóveis baratos, alguns muito caros
542
- - Assimetria negativa: mais imóveis caros, alguns muito baratos
543
-
544
- ### 2. Matriz de Correlação
545
- **Objetivo**: Identificar relações entre variáveis
546
- - **Correlação positiva (vermelho)**: Ambas variáveis aumentam juntas
547
- - **Correlação negativa (azul)**: Uma aumenta enquanto a outra diminui
548
- - **Valores**: -1 (correlação negativa perfeita) a +1 (correlação positiva perfeita)
549
- - **Para previsão**: Buscamos features com alta correlação com 'price'
550
-
551
- ### 3. Análise por Feature Individual
552
- **Dois gráficos para cada feature:**
553
- - **Distribuição**: Como os valores da feature se distribuem
554
- - **Relação com Preço**: Como a feature influencia o preço
555
- - **Linha de tendência**: Mostra a direção da relação
556
-
557
- ### 4. Métricas do Modelo
558
- - **R² (R-quadrado)**: Proporção da variância explicada (0-1, quanto maior melhor)
559
- - **RMSE**: Erro médio em dólares (penaliza erros grandes)
560
- - **MAE**: Erro absoluto médio em dólares
561
- - **Coeficientes**: Impacto de cada feature no preço
562
-
563
- ### 🎯 Dicas para Melhor Previsão:
564
- 1. Use features com alta correlação com preço
565
- 2. Evite features muito correlacionadas entre si
566
- 3. Verifique se as relações são lineares nos gráficos
567
- 4. Considere transformações para features com distribuições assimétricas
 
 
 
 
 
 
 
568
  """
569
  )
570
 
571
- # Inicializar dados ao carregar
572
- predictor.load_data()
 
 
 
 
 
 
 
 
 
 
 
573
 
574
  if __name__ == "__main__":
575
- demo.launch()
 
16
  plt.rcParams['figure.figsize'] = (12, 6)
17
  plt.rcParams['font.size'] = 10
18
 
19
+ # Gerar dados realísticos do King County
20
+ def generate_king_county_data(n_samples=2000):
21
+ """Gera dados realísticos simulando o dataset King County"""
22
+ np.random.seed(42)
23
+
24
+ # Gerar características básicas com relações realísticas
25
+ sqft_living = np.random.normal(2080, 920, n_samples)
26
+ sqft_living = np.clip(sqft_living, 370, 13540) # Valores reais do dataset
27
+
28
+ bedrooms = np.random.choice([1, 2, 3, 4, 5, 6, 7, 8, 9, 10], n_samples, p=[0.01, 0.05, 0.15, 0.3, 0.25, 0.15, 0.05, 0.02, 0.01, 0.01])
29
+ bathrooms = np.random.choice([0.5, 0.75, 1, 1.25, 1.5, 1.75, 2, 2.25, 2.5, 2.75, 3, 3.25, 3.5, 3.75, 4, 4.25, 4.5, 4.75, 5, 5.25, 5.5, 5.75, 6, 6.25, 6.5, 6.75, 7, 7.25, 7.5, 7.75, 8],
30
+ n_samples, p=[0.01, 0.02, 0.05, 0.08, 0.1, 0.12, 0.15, 0.1, 0.08, 0.06, 0.05, 0.04, 0.03, 0.02, 0.02, 0.01, 0.01, 0.01, 0.01, 0.005, 0.005, 0.005, 0.005, 0.005, 0.005, 0.005, 0.005, 0.005, 0.005, 0.005, 0.005])
31
+
32
+ floors = np.random.choice([1, 1.5, 2, 2.5, 3, 3.5], n_samples, p=[0.4, 0.25, 0.2, 0.1, 0.04, 0.01])
33
+ waterfront = np.random.choice([0, 1], n_samples, p=[0.99, 0.01])
34
+ view = np.random.choice([0, 1, 2, 3, 4], n_samples, p=[0.9, 0.05, 0.03, 0.015, 0.005])
35
+ condition = np.random.choice([1, 2, 3, 4, 5], n_samples, p=[0.05, 0.2, 0.5, 0.2, 0.05])
36
+ grade = np.random.choice([1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13], n_samples, p=[0.001, 0.005, 0.01, 0.05, 0.1, 0.15, 0.2, 0.15, 0.1, 0.08, 0.06, 0.03, 0.01])
37
+
38
+ # Ano de construção (de 1900 a 2015)
39
+ yr_built = np.random.randint(1900, 2016, n_samples)
40
+
41
+ # Localização (coordenadas de King County)
42
+ lat = np.random.uniform(47.2, 47.8, n_samples)
43
+ long = np.random.uniform(-122.5, -121.8, n_samples)
44
+
45
+ # Calcular preço base com relações realísticas
46
+ base_price = (
47
+ sqft_living * 300 + # Preço por sqft
48
+ bedrooms * 50000 + # Valor por quarto
49
+ bathrooms * 40000 + # Valor por banheiro
50
+ floors * 25000 + # Valor por andar
51
+ waterfront * 500000 + # Água frente
52
+ view * 25000 + # Vista
53
+ condition * 15000 + # Condição
54
+ grade * 30000 + # Grau de qualidade
55
+ (2024 - yr_built) * -500 + # Depreciação por ano
56
+ (lat - 47.5) * 100000 + # Localização norte/sul
57
+ (long + 122.2) * 80000 # Localização leste/oeste
58
+ )
59
+
60
+ # Adicionar variação aleatória
61
+ noise = np.random.normal(0, 150000, n_samples)
62
+ price = base_price + noise
63
+ price = np.clip(price, 75000, 5000000) # Range realístico
64
+
65
+ # Criar DataFrame
66
+ data = {
67
+ 'price': price,
68
+ 'sqft_living': sqft_living,
69
+ 'bedrooms': bedrooms,
70
+ 'bathrooms': bathrooms,
71
+ 'floors': floors,
72
+ 'waterfront': waterfront,
73
+ 'view': view,
74
+ 'condition': condition,
75
+ 'grade': grade,
76
+ 'yr_built': yr_built,
77
+ 'lat': lat,
78
+ 'long': long,
79
+ 'sqft_lot': np.random.normal(15000, 10000, n_samples), # Área do terreno
80
+ 'sqft_above': sqft_living * 0.8, # Área acima do solo
81
+ 'sqft_basement': sqft_living * 0.2, # Porão
82
+ 'yr_renovated': np.where(np.random.random(n_samples) > 0.8,
83
+ np.random.randint(1950, 2016, n_samples), 0) # Ano renovação
84
+ }
85
+
86
+ df = pd.DataFrame(data)
87
+ return df
88
 
89
  class HousePricePredictor:
90
  def __init__(self):
 
96
  self.selected_features = None
97
 
98
  def load_data(self):
99
+ """Carrega dados gerados"""
100
+ try:
101
+ self.df = generate_king_county_data()
102
+ return f"✅ Dados carregados: {self.df.shape[0]} imóveis × {self.df.shape[1]} características"
103
+ except Exception as e:
104
+ return f"❌ Erro ao carregar dados: {str(e)}"
 
 
 
 
 
105
 
106
  def get_numeric_features(self):
107
  """Retorna lista de features numéricas (excluindo price)"""
 
209
  message = predictor.load_data()
210
  features = predictor.get_numeric_features()
211
 
212
+ # Criar checkboxes para features
213
  feature_checkboxes = []
214
+ if features and predictor.df is not None:
215
+ # Calcular correlações com preço
216
  correlations = predictor.df.corr()['price'].abs().sort_values(ascending=False)
217
+
218
+ # Selecionar automaticamente as 6 features mais correlacionadas (excluindo price)
219
+ top_features = []
220
+ for feature in correlations.index:
221
+ if feature != 'price' and len(top_features) < 6:
222
+ top_features.append(feature)
223
 
224
  for feature in features:
225
+ corr_value = correlations.get(feature, 0)
226
  feature_checkboxes.append(
227
  gr.Checkbox(
228
+ label=f"{feature} (corr: {corr_value:.3f})",
229
  value=feature in top_features,
230
+ info=f"Média: {predictor.df[feature].mean():.1f}"
231
  )
232
  )
233
 
234
+ return message, gr.Column(feature_checkboxes), gr.update(visible=True)
235
 
236
+ def get_selected_features_from_checkboxes(*checkbox_values):
237
+ """Converte valores dos checkboxes para lista de features selecionadas"""
238
+ features = predictor.get_numeric_features()
239
+ selected = []
240
+ for i, is_checked in enumerate(checkbox_values):
241
+ if is_checked and i < len(features):
242
+ selected.append(features[i])
243
+ return selected
244
+
245
+ def train_model_action(*checkbox_values):
246
  """Treina o modelo com features selecionadas"""
247
+ selected_features = get_selected_features_from_checkboxes(*checkbox_values)
 
 
248
 
249
+ success, result = predictor.train_model(selected_features)
250
 
251
  if success:
252
  metrics_text = f"""
253
+ ## 📊 Resultados do Modelo
254
 
255
+ ### Métricas de Desempenho:
256
+ - **Treino**: {result['r2_train']:.4f}
257
+ - **R² Teste**: {result['r2_test']:.4f}
258
+ - **RMSE Teste**: ${result['rmse_test']:,.0f}
259
+ - **MAE Teste**: ${result['mae_test']:,.0f}
260
 
261
+ ### 🎯 Features por Importância:
262
  """
263
 
264
  for i, feature in enumerate(result['top_features']):
265
+ direction = "📈 Aumenta preço" if feature['Coeficiente'] > 0 else "📉 Diminui preço"
266
+ metrics_text += f"\n{i+1}. **{feature['Feature']}**: {feature['Coeficiente']:.4f} ({direction})"
267
+
268
+ metrics_text += f"\n\n**Total de features usadas**: {len(selected_features)}"
269
 
270
  return metrics_text, result, gr.update(visible=True)
271
  else:
272
  return result, None, gr.update(visible=False)
273
 
274
+ def create_correlation_plot():
275
+ """Cria gráfico de correlação das features numéricas"""
276
+ if predictor.df is None:
277
  return None
278
 
279
  try:
280
+ # Selecionar apenas algumas features para não sobrecarregar o gráfico
281
+ numeric_cols = predictor.df.select_dtypes(include=[np.number]).columns.tolist()
282
+ if len(numeric_cols) > 8: # Limitar para visualização
283
+ # Pegar as mais correlacionadas com price
284
+ correlations = predictor.df.corr()['price'].abs().sort_values(ascending=False)
285
+ top_features = correlations.index[:8].tolist()
286
+ else:
287
+ top_features = numeric_cols
288
 
289
+ corr_matrix = predictor.df[top_features].corr()
290
+
291
+ fig, ax = plt.subplots(figsize=(12, 10))
292
+ sns.heatmap(corr_matrix, annot=True, fmt='.2f', cmap='RdYlBu', center=0,
293
+ square=True, linewidths=0.5, cbar_kws={"shrink": 0.8}, ax=ax)
294
+ ax.set_title('🔗 Matriz de Correlação entre Variáveis\n(Valores próximos de 1 ou -1 indicam forte correlação)',
295
+ fontsize=14, fontweight='bold', pad=20)
296
  plt.tight_layout()
297
  return fig
298
  except Exception as e:
299
  print(f"Erro no gráfico de correlação: {e}")
300
  return None
301
 
302
+ def create_feature_analysis_plot(selected_feature):
303
+ """Cria gráfico de análise para uma feature específica"""
304
  if predictor.df is None or not selected_feature:
305
  return None, None
306
 
307
  try:
308
+ # Gráfico 1: Distribuição da feature
309
+ fig1, (ax1, ax2) = plt.subplots(1, 2, figsize=(15, 5))
310
+
311
+ # Histograma da distribuição
312
+ ax1.hist(predictor.df[selected_feature], bins=30, edgecolor='black', alpha=0.7, color='skyblue')
313
+ ax1.axvline(predictor.df[selected_feature].mean(), color='red', linestyle='--', linewidth=2,
314
+ label=f'Média: {predictor.df[selected_feature].mean():.2f}')
315
  ax1.set_xlabel(selected_feature)
316
  ax1.set_ylabel('Frequência')
317
+ ax1.set_title(f'📊 Distribuição de {selected_feature}')
318
+ ax1.legend()
319
  ax1.grid(True, alpha=0.3)
 
320
 
321
+ # Boxplot
322
+ ax2.boxplot(predictor.df[selected_feature])
323
+ ax2.set_ylabel(selected_feature)
324
+ ax2.set_title(f'📦 Boxplot - {selected_feature}')
 
 
 
325
  ax2.grid(True, alpha=0.3)
326
 
327
+ plt.tight_layout()
328
+
329
+ # Gráfico 2: Relação com preço
330
+ fig2, ax = plt.subplots(figsize=(10, 6))
331
+
332
+ if predictor.df[selected_feature].nunique() < 10: # Variável categórica
333
+ # Boxplot por categoria
334
+ data_to_plot = []
335
+ categories = sorted(predictor.df[selected_feature].unique())
336
+ for cat in categories:
337
+ data_to_plot.append(predictor.df[predictor.df[selected_feature] == cat]['price'])
338
+
339
+ ax.boxplot(data_to_plot, labels=categories)
340
+ ax.set_xlabel(selected_feature)
341
+ ax.set_ylabel('Preço ($)')
342
+ ax.set_title(f'💰 Preço vs {selected_feature}\n(Corr: {predictor.df[selected_feature].corr(predictor.df["price"]):.3f})')
343
+
344
+ else: # Variável contínua
345
+ # Scatter plot
346
+ ax.scatter(predictor.df[selected_feature], predictor.df['price'], alpha=0.5, s=20, color='steelblue')
347
+ ax.set_xlabel(selected_feature)
348
+ ax.set_ylabel('Preço ($)')
349
+ correlation = predictor.df[selected_feature].corr(predictor.df['price'])
350
+ ax.set_title(f'💰 Preço vs {selected_feature}\n(Corr: {correlation:.3f})')
351
+
352
+ # Linha de tendência
353
  z = np.polyfit(predictor.df[selected_feature], predictor.df['price'], 1)
354
  p = np.poly1d(z)
355
  x_range = np.linspace(predictor.df[selected_feature].min(), predictor.df[selected_feature].max(), 100)
356
+ ax.plot(x_range, p(x_range), "r--", linewidth=2, alpha=0.8, label='Tendência')
357
+ ax.legend()
358
 
359
+ ax.grid(True, alpha=0.3)
360
  plt.tight_layout()
361
+
362
  return fig1, fig2
363
 
364
  except Exception as e:
365
+ print(f"Erro nos gráficos de análise: {e}")
366
  return None, None
367
 
368
  def create_price_distribution_plot():
 
370
  if predictor.df is None:
371
  return None
372
 
373
+ fig, (ax1, ax2) = plt.subplots(1, 2, figsize=(15, 5))
374
+
375
+ # Histograma
376
+ ax1.hist(predictor.df['price'], bins=50, edgecolor='black', alpha=0.7, color='steelblue')
377
+ ax1.axvline(predictor.df['price'].mean(), color='red', linestyle='--', linewidth=2,
378
  label=f'Média: ${predictor.df["price"].mean():,.0f}')
379
+ ax1.axvline(predictor.df['price'].median(), color='green', linestyle='--', linewidth=2,
380
  label=f'Mediana: ${predictor.df["price"].median():,.0f}')
381
+ ax1.set_xlabel('Preço ($)')
382
+ ax1.set_ylabel('Número de Imóveis')
383
+ ax1.set_title('🏠 Distribuição dos Preços dos Imóveis')
384
+ ax1.legend()
385
+ ax1.grid(True, alpha=0.3)
386
+
387
+ # Boxplot
388
+ ax2.boxplot(predictor.df['price'])
389
+ ax2.set_ylabel('Preço ($)')
390
+ ax2.set_title('📦 Distribuição - Boxplot')
391
+ ax2.grid(True, alpha=0.3)
392
+
393
+ # Formatar eixos
394
+ ax2.yaxis.set_major_formatter(plt.FuncFormatter(lambda x, p: f'${x:,.0f}'))
395
+
396
  plt.tight_layout()
397
  return fig
398
 
399
  def get_feature_stats(feature):
400
  """Retorna estatísticas de uma feature"""
401
  if predictor.df is None or feature not in predictor.df.columns:
402
+ return "Selecione uma feature para ver estatísticas"
403
 
404
  stats = predictor.df[feature].describe()
405
+ correlation = predictor.df[feature].corr(predictor.df['price'])
406
+
407
  return f"""
408
+ ## 📈 Estatísticas de **{feature}**
409
+
410
+ **Valores Básicos:**
411
  - Média: {stats['mean']:.2f}
412
  - Mediana: {stats['50%']:.2f}
413
  - Desvio Padrão: {stats['std']:.2f}
414
  - Mínimo: {stats['min']:.2f}
415
  - Máximo: {stats['max']:.2f}
416
+
417
+ **Distribuição:**
418
  - 25º Percentil: {stats['25%']:.2f}
419
  - 75º Percentil: {stats['75%']:.2f}
420
+ - Número de Valores Únicos: {predictor.df[feature].nunique()}
421
+
422
+ **Relação com Preço:**
423
+ - Correlação: {correlation:.3f}
424
+ - Interpretação: {'Forte' if abs(correlation) > 0.5 else 'Moderada' if abs(correlation) > 0.3 else 'Fraca'} relação com preço
425
  """
426
 
427
  def create_prediction_inputs(metrics_result):
 
435
  min_val = float(predictor.df[feature].min())
436
  max_val = float(predictor.df[feature].max())
437
  mean_val = float(predictor.df[feature].mean())
438
+
439
+ # Definir step apropriado baseado no range
440
+ step = (max_val - min_val) / 100
441
+ if step < 0.1:
442
+ step = 0.1
443
+ elif step > 100:
444
+ step = 10
445
 
446
  inputs.append(
447
  gr.Slider(
448
+ label=f"🏠 {feature}",
449
  minimum=min_val,
450
  maximum=max_val,
451
  value=mean_val,
452
+ step=step,
453
+ info=f"Range: {min_val:.1f} - {max_val:.1f}"
454
  )
455
  )
456
 
 
459
  def predict_price_action(*feature_values):
460
  """Faz previsão de preço"""
461
  if not predictor.is_trained:
462
+ return "❌ Modelo não treinado. Por favor, treine o modelo primeiro.", None
463
 
464
  try:
465
  # Criar dicionário com os valores das features
466
  input_features = {}
467
  if hasattr(predictor, 'selected_features') and predictor.selected_features:
468
  for i, feature in enumerate(predictor.selected_features):
469
+ if i < len(feature_values):
470
+ input_features[feature] = feature_values[i]
471
  else:
472
+ return "❌ Nenhuma feature selecionada no modelo", None
473
 
474
  pred_price, error = predictor.predict_price(input_features)
475
 
 
480
  features_summary = "\n".join([f"- **{k}**: {v:.2f}" for k, v in input_features.items()])
481
 
482
  result_text = f"""
483
+ ## 🏠 Previsão de Preço do Imóvel
484
 
485
+ ### 💰 **Preço Estimado: ${pred_price:,.2f}**
486
 
487
+ ### 📋 Características Informadas:
488
  {features_summary}
489
 
490
+ ---
491
+ *💡 Nota: Esta é uma estimativa baseada no modelo de regressão linear treinado com dados de King County.*
492
  """
493
 
494
  return result_text, pred_price
 
497
  return f"❌ Erro na previsão: {str(e)}", None
498
 
499
  # Interface Gradio
500
+ with gr.Blocks(title="🏠 Análise e Previsão de Preços de Imóveis - King County") as demo:
501
  gr.Markdown(
502
  """
503
  # 🏠 Análise e Previsão de Preços de Imóveis
504
+ ## King County, Washington - USA
505
 
506
+ ### 📊 Sobre os Dados:
507
+ Este aplicativo utiliza dados **realísticos** simulando o mercado imobiliário de King County (Seattle).
508
+ Os dados incluem características como área construída, quartos, banheiros, localização e muito mais.
 
 
 
509
 
510
+ ### 🎯 Funcionalidades:
511
+ - **Análise Exploratória**: Gráficos interativos dos dados
512
+ - **Seleção de Features**: Escolha quais características usar no modelo
513
+ - **Treinamento**: Modelo de Machine Learning para prever preços
514
+ - **Previsão**: Estime o preço de um imóvel com características específicas
 
515
 
516
  """
517
  )
518
 
519
+ with gr.Tab("🚀 1. Carregar Dados"):
520
+ gr.Markdown("### Primeiro Passo: Carregar os Dados")
521
+ load_btn = gr.Button("📂 Carregar Dados do King County", variant="primary", size="lg")
522
+ load_status = gr.Markdown("Clique no botão para carregar os dados de imóveis...")
 
 
 
 
523
 
524
  load_btn.click(
525
  load_data_action,
526
  outputs=[load_status, feature_selection, train_btn]
527
  )
528
 
529
+ with gr.Tab("📈 2. Análise Exploratória"):
530
+ gr.Markdown("### Explore os Dados e Visualize Relações")
531
 
532
  with gr.Row():
533
  with gr.Column():
534
+ gr.Markdown("#### 📊 Distribuição de Preços")
535
+ price_plot_btn = gr.Button("🎨 Gerar Gráfico de Preços", variant="primary")
536
  price_plot = gr.Plot()
537
 
538
  with gr.Column():
539
+ gr.Markdown("#### 🔗 Correlações entre Variáveis")
540
+ correlation_btn = gr.Button("🔄 Gerar Matriz de Correlação", variant="primary")
541
+ correlation_plot = gr.Plot()
542
+
543
+ gr.Markdown("---")
544
+ gr.Markdown("#### 🔍 Análise Detalhada por Feature")
545
+
546
+ with gr.Row():
547
+ with gr.Column():
548
  feature_selector = gr.Dropdown(
549
+ label="Selecione uma característica para análise detalhada",
550
  choices=[],
551
  interactive=True
552
  )
553
+ feature_stats = gr.Markdown("Selecione uma feature acima...")
554
+
555
+ with gr.Column():
556
+ feature_analysis_btn = gr.Button("📈 Analisar Feature", variant="primary")
557
 
558
  with gr.Row():
559
+ feature_dist_plot = gr.Plot(label="Distribuição da Feature")
560
+ feature_price_plot = gr.Plot(label="Relação com Preço")
561
+
562
+ # Conectar eventos
563
+ price_plot_btn.click(create_price_distribution_plot, outputs=[price_plot])
564
+ correlation_btn.click(create_correlation_plot, outputs=[correlation_plot])
565
+ feature_analysis_btn.click(
566
+ create_feature_analysis_plot,
567
+ inputs=[feature_selector],
568
+ outputs=[feature_dist_plot, feature_price_plot]
 
 
 
 
 
 
 
 
569
  )
 
570
  feature_selector.change(
571
  get_feature_stats,
572
  inputs=[feature_selector],
573
  outputs=[feature_stats]
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
574
  )
575
 
576
+ with gr.Tab("🤖 3. Treinar Modelo"):
577
+ gr.Markdown("### Configure e Treine o Modelo de Previsão")
578
  gr.Markdown("""
579
+ **🎯 Como Funciona:**
580
+ - Selecione as características que deseja usar para prever preços
581
+ - Features com alta correlação (próximas de 1 ou -1) geralmente são melhores preditoras
582
+ - O modelo usará Regressão Linear para aprender os padrões
583
  """)
584
 
585
+ feature_selection = gr.Column()
586
+ train_btn = gr.Button("🚀 Treinar Modelo de Previsão", variant="primary", size="lg", visible=False)
587
+ train_output = gr.Markdown("Selecione as features acima e clique em 'Treinar Modelo'")
588
  metrics_display = gr.JSON(label="Métricas Detalhadas", visible=False)
589
 
590
  train_btn.click(
 
593
  outputs=[train_output, metrics_display, metrics_display]
594
  )
595
 
596
+ with gr.Tab("💰 4. Fazer Previsão"):
597
  gr.Markdown("### Faça uma Previsão de Preço")
598
+ gr.Markdown("Ajuste os valores das características para estimar o preço de um imóvel:")
599
 
600
  prediction_inputs = gr.Column()
601
+ predict_btn = gr.Button("🎯 Calcular Preço do Imóvel", variant="primary", size="lg")
602
 
603
+ with gr.Row():
604
+ prediction_output = gr.Markdown("Preencha os valores acima e clique em 'Calcular Preço'")
605
+ price_result = gr.Number(
606
+ label="💵 Preço Previsto",
607
+ visible=False
608
+ )
609
 
610
  # Atualizar inputs quando modelo for treinado
611
  metrics_display.change(
 
623
  outputs=[price_result]
624
  )
625
 
626
+ with gr.Tab("📚 5. Explicações"):
627
  gr.Markdown(
628
  """
629
+ ## 📊 Guia Completo de Análise
630
+
631
+ ### 🏠 Sobre os Dados
632
+ **King County** inclui Seattle e é um mercado imobiliário dinâmico. Os dados simulados incluem:
633
+ - **Preços**: De $75,000 a $5,000,000
634
+ - **Características**: Área, quartos, banheiros, localização, qualidade, etc.
635
+ - **Período**: Imóveis de 1900 até 2015
636
+
637
+ ### 📈 Interpretação dos Gráficos
638
+
639
+ #### 1. Distribuição de Preços
640
+ - **Histograma**: Mostra quantos imóveis existem em cada faixa de preço
641
+ - **Média vs Mediana**: Se a média > mediana, há imóveis muito caros puxando a média
642
+ - **Assimetria**: Mercados reais geralmente têm assimetria positiva (mais imóveis baratos)
643
+
644
+ #### 2. Matriz de Correlação
645
+ - **🔴 Vermelho**: Correlação positiva (ex: área maior preço maior)
646
+ - **🔵 Azul**: Correlação negativa (ex: ano mais antigo → preço menor)
647
+ - **Valores**: -1 (perfeita negativa) a +1 (perfeita positiva)
648
+ - **Para modelo**: Busque features com |correlação| > 0.3 com preço
649
+
650
+ #### 3. Análise por Feature
651
+ - **Distribuição**: Como os valores se espalham (normal, assimétrica)
652
+ - **Relação com Preço**: Padrão linear? Há outliers?
653
+ - **Boxplot**: Mostra mediana, quartis e valores extremos
654
+
655
+ ### 🤖 Sobre o Modelo
656
+ - **Algoritmo**: Regressão Linear Múltipla
657
+ - **Transformação**: Logarítmica nos preços para normalizar
658
+ - **Avaliação**: R² mostra % da variância explicada (0-100%)
659
+ - **Coeficientes**: Impacto de cada feature no preço final
660
+
661
+ ### 💡 Dicas para Boas Previsões
662
+ 1. **Selecione features relevantes**: Área, quartos, localização
663
+ 2. **Evite multicolinearidade**: Não use features muito correlacionadas entre si
664
+ 3. **Verifique relações lineares**: Features com relação clara com preço funcionam melhor
665
+ 4. **Considere o contexto**: Características únicas podem afetar preços reais
666
+
667
+ ### 🎯 Métricas do Modelo
668
+ - **R²**: 0.7-0.9 = Excelente, 0.5-0.7 = Bom, <0.5 = Precisa melhorar
669
+ - **RMSE**: Erro médio em dólares (ideal: <20% do preço médio)
670
+ - **Coeficientes**: Mostram quanto cada feature impacta no preço
671
  """
672
  )
673
 
674
+ # Inicializar dados ao carregar a interface
675
+ def initialize_app():
676
+ """Inicializa o aplicativo carregando dados e atualizando interfaces"""
677
+ load_status = predictor.load_data()
678
+ features = predictor.get_numeric_features()
679
+
680
+ # Atualizar dropdown de features
681
+ feature_choices = gr.update(choices=features, value=features[0] if features else None)
682
+
683
+ return load_status, feature_choices
684
+
685
+ # Inicializar quando o app carregar
686
+ demo.load(initialize_app, outputs=[load_status, feature_selector])
687
 
688
  if __name__ == "__main__":
689
+ demo.launch(share=True)