File size: 27,674 Bytes
e123789
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
f94c886
445c876
f94c886
 
 
 
 
e123789
90f9b9a
66d0fa3
7eb35c8
445c876
66d0fa3
 
 
 
 
 
 
 
f94c886
 
 
 
 
 
 
445c876
66d0fa3
445c876
 
 
66d0fa3
7eb35c8
66d0fa3
7eb35c8
e123789
66d0fa3
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
90f9b9a
 
 
 
 
 
 
 
 
b943177
 
 
 
 
 
 
 
 
 
 
 
 
 
 
58ac9af
90f9b9a
e123789
90f9b9a
 
 
 
 
 
 
 
66d0fa3
e123789
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
90f9b9a
 
 
 
e123789
 
 
 
 
 
90f9b9a
f94c886
e123789
 
 
90f9b9a
e123789
90f9b9a
e123789
 
90f9b9a
e123789
 
 
 
90f9b9a
 
e123789
 
 
 
 
 
 
 
 
 
 
 
 
90f9b9a
e123789
 
 
 
66d0fa3
445c876
 
 
b943177
445c876
 
90f9b9a
f94c886
 
 
90f9b9a
e123789
f94c886
 
7eb35c8
f94c886
90f9b9a
7eb35c8
f94c886
 
 
 
 
 
90f9b9a
7eb35c8
f94c886
90f9b9a
7eb35c8
f94c886
66d0fa3
90f9b9a
 
 
f94c886
e123789
f94c886
 
58ac9af
 
 
 
 
f94c886
0ba0496
a49b452
0ba0496
f94c886
58ac9af
 
e123789
 
 
7eb35c8
e123789
7eb35c8
 
 
 
 
e123789
f94c886
e123789
 
90f9b9a
7eb35c8
 
 
f94c886
 
 
e123789
f94c886
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
e123789
242e930
f94c886
242e930
e383184
e123789
 
242e930
90f9b9a
 
 
 
7eb35c8
 
 
 
66d0fa3
 
90f9b9a
 
 
445c876
90f9b9a
 
 
66d0fa3
 
90f9b9a
e123789
 
 
 
242e930
90f9b9a
a49b452
 
e123789
 
90f9b9a
242e930
 
 
 
 
 
e123789
90f9b9a
e123789
90f9b9a
 
 
 
 
 
445c876
90f9b9a
445c876
90f9b9a
445c876
90f9b9a
445c876
 
66d0fa3
90f9b9a
e123789
90f9b9a
e123789
 
90f9b9a
e123789
f94c886
 
 
 
 
 
 
 
 
 
 
 
 
e123789
 
f94c886
 
 
 
 
 
e123789
 
 
445c876
 
f94c886
90f9b9a
f94c886
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
a49b452
242e930
f94c886
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
a49b452
f94c886
 
 
 
 
a49b452
f94c886
 
 
90f9b9a
e123789
 
f94c886
 
 
e123789
 
f94c886
 
 
 
 
 
 
 
 
 
 
 
 
90f9b9a
f94c886
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
242e930
f94c886
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
e123789
e383184
66d0fa3
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
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()