FernandezUNB commited on
Commit
e123789
·
verified ·
1 Parent(s): 4fdd446

Create app.py

Browse files
Files changed (1) hide show
  1. app.py +426 -0
app.py ADDED
@@ -0,0 +1,426 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import pandas as pd
2
+ import numpy as np
3
+ import matplotlib.pyplot as plt
4
+ import seaborn as sns
5
+ from scipy import stats
6
+ from sklearn.model_selection import train_test_split
7
+ from sklearn.linear_model import LinearRegression
8
+ from sklearn.preprocessing import StandardScaler
9
+ from sklearn.metrics import r2_score, mean_squared_error, mean_absolute_error
10
+ import gradio as gr
11
+ import warnings
12
+ warnings.filterwarnings('ignore')
13
+
14
+ # Configurações de visualização
15
+ sns.set_style("whitegrid")
16
+ plt.rcParams['figure.figsize'] = (12, 6)
17
+ plt.rcParams['font.size'] = 10
18
+
19
+ class HousePricePredictor:
20
+ def __init__(self):
21
+ self.model = None
22
+ self.scaler = None
23
+ self.feature_names = None
24
+ self.df = None
25
+ self.is_trained = False
26
+
27
+ def load_and_prepare_data(self, file_path):
28
+ """Carrega e prepara os dados"""
29
+ try:
30
+ self.df = pd.read_csv(file_path)
31
+ print(f"✅ Dados carregados: {self.df.shape[0]} linhas × {self.df.shape[1]} colunas")
32
+
33
+ # Preparar features
34
+ features_to_drop = ['id', 'date'] if 'date' in self.df.columns else ['id']
35
+ X = self.df.drop(columns=features_to_drop + ['price'], errors='ignore')
36
+ X = X.select_dtypes(include=[np.number])
37
+ y = self.df['price']
38
+
39
+ self.feature_names = X.columns.tolist()
40
+ return True, f"Dados carregados com sucesso! {len(self.feature_names)} features identificadas."
41
+
42
+ except Exception as e:
43
+ return False, f"Erro ao carregar dados: {str(e)}"
44
+
45
+ def train_model(self):
46
+ """Treina o modelo de regressão linear"""
47
+ try:
48
+ # Preparar dados
49
+ features_to_drop = ['id', 'date'] if 'date' in self.df.columns else ['id']
50
+ X = self.df.drop(columns=features_to_drop + ['price'], errors='ignore')
51
+ X = X.select_dtypes(include=[np.number])
52
+ y = np.log1p(self.df['price']) # Transformação logarítmica
53
+
54
+ # Dividir dados
55
+ X_train, X_test, y_train, y_test = train_test_split(
56
+ X, y, test_size=0.2, random_state=42
57
+ )
58
+
59
+ # Padronizar features
60
+ self.scaler = StandardScaler()
61
+ X_train_scaled = self.scaler.fit_transform(X_train)
62
+ X_test_scaled = self.scaler.transform(X_test)
63
+
64
+ # Treinar modelo
65
+ self.model = LinearRegression()
66
+ self.model.fit(X_train_scaled, y_train)
67
+
68
+ # Fazer previsões
69
+ y_pred_train = self.model.predict(X_train_scaled)
70
+ y_pred_test = self.model.predict(X_test_scaled)
71
+
72
+ # Calcular métricas
73
+ y_pred_train_orig = np.expm1(y_pred_train)
74
+ y_pred_test_orig = np.expm1(y_pred_test)
75
+ y_train_orig = np.expm1(y_train)
76
+ y_test_orig = np.expm1(y_test)
77
+
78
+ r2_train = r2_score(y_train_orig, y_pred_train_orig)
79
+ r2_test = r2_score(y_test_orig, y_pred_test_orig)
80
+ rmse_test = np.sqrt(mean_squared_error(y_test_orig, y_pred_test_orig))
81
+ mae_test = mean_absolute_error(y_test_orig, y_pred_test_orig)
82
+
83
+ self.is_trained = True
84
+
85
+ # Coeficientes
86
+ coeficientes = pd.DataFrame({
87
+ 'Feature': self.feature_names,
88
+ 'Coeficiente': self.model.coef_
89
+ }).sort_values('Coeficiente', key=abs, ascending=False)
90
+
91
+ return True, {
92
+ 'r2_train': r2_train,
93
+ 'r2_test': r2_test,
94
+ 'rmse_test': rmse_test,
95
+ 'mae_test': mae_test,
96
+ 'top_features': coeficientes.head(10).to_dict('records')
97
+ }
98
+
99
+ except Exception as e:
100
+ return False, f"Erro no treinamento: {str(e)}"
101
+
102
+ def predict_price(self, input_features):
103
+ """Faz previsão de preço para novas entradas"""
104
+ if not self.is_trained:
105
+ return None, "Modelo não treinado. Por favor, treine o modelo primeiro."
106
+
107
+ try:
108
+ # Criar array de features na ordem correta
109
+ input_array = []
110
+ for feature in self.feature_names:
111
+ input_array.append(float(input_features[feature]))
112
+
113
+ input_array = np.array(input_array).reshape(1, -1)
114
+
115
+ # Padronizar
116
+ input_scaled = self.scaler.transform(input_array)
117
+
118
+ # Fazer previsão
119
+ pred_log = self.model.predict(input_scaled)[0]
120
+ pred_original = np.expm1(pred_log)
121
+
122
+ return pred_original, None
123
+
124
+ except Exception as e:
125
+ return None, f"Erro na previsão: {str(e)}"
126
+
127
+ # Instanciar o predictor
128
+ predictor = HousePricePredictor()
129
+
130
+ def load_data(file):
131
+ """Função para carregar dados do arquivo"""
132
+ if file is None:
133
+ return "❌ Por favor, faça upload do arquivo CSV", None
134
+
135
+ success, message = predictor.load_and_prepare_data(file.name)
136
+ if success:
137
+ return message, gr.update(visible=True)
138
+ else:
139
+ return message, gr.update(visible=False)
140
+
141
+ def train_model_action():
142
+ """Função para treinar o modelo"""
143
+ if predictor.df is None:
144
+ return "❌ Por favor, carregue os dados primeiro.", None, gr.update(visible=False)
145
+
146
+ success, result = predictor.train_model()
147
+
148
+ if success:
149
+ metrics_text = f"""
150
+ 📊 **Métricas do Modelo:**
151
+
152
+ • R² Treino: {result['r2_train']:.4f}
153
+ • R² Teste: {result['r2_test']:.4f}
154
+ • RMSE Teste: ${result['rmse_test']:,.2f}
155
+ • MAE Teste: ${result['mae_test']:,.2f}
156
+
157
+ 🎯 **Top 5 Features Mais Importantes:**
158
+ """
159
+
160
+ for i, feature in enumerate(result['top_features'][:5]):
161
+ impact = "📈 Aumenta preço" if feature['Coeficiente'] > 0 else "📉 Diminui preço"
162
+ metrics_text += f"\n{i+1}. {feature['Feature']}: {feature['Coeficiente']:.4f} ({impact})"
163
+
164
+ return metrics_text, result, gr.update(visible=True)
165
+ else:
166
+ return result, None, gr.update(visible=False)
167
+
168
+ def create_prediction_interface(metrics_result):
169
+ """Cria a interface de previsão baseada nas features disponíveis"""
170
+ if predictor.feature_names is None:
171
+ return gr.update(visible=False)
172
+
173
+ # Criar inputs para cada feature
174
+ inputs = []
175
+ default_values = {}
176
+
177
+ # Calcular valores médios para preenchimento automático
178
+ if predictor.df is not None:
179
+ for feature in predictor.feature_names:
180
+ mean_val = predictor.df[feature].mean()
181
+ default_values[feature] = mean_val
182
+
183
+ for feature in predictor.feature_names:
184
+ min_val = float(predictor.df[feature].min())
185
+ max_val = float(predictor.df[feature].max())
186
+ mean_val = float(predictor.df[feature].mean())
187
+
188
+ inputs.append(
189
+ gr.Number(
190
+ label=feature,
191
+ value=mean_val,
192
+ minimum=min_val,
193
+ maximum=max_val,
194
+ info=f"Range: {min_val:.1f} - {max_val:.1f}"
195
+ )
196
+ )
197
+
198
+ return inputs
199
+
200
+ def predict_price_action(*feature_values):
201
+ """Função para fazer previsão de preço"""
202
+ if not predictor.is_trained:
203
+ return "❌ Modelo não treinado. Por favor, treine o modelo primeiro.", None
204
+
205
+ # Criar dicionário de features
206
+ input_features = {}
207
+ for i, feature in enumerate(predictor.feature_names):
208
+ input_features[feature] = feature_values[i]
209
+
210
+ pred_price, error = predictor.predict_price(input_features)
211
+
212
+ if error:
213
+ return f"❌ {error}", None
214
+
215
+ # Criar visualização dos inputs
216
+ features_text = "**Características do Imóvel:**\n"
217
+ for feature, value in input_features.items():
218
+ features_text += f"\n• {feature}: {value}"
219
+
220
+ result_text = f"""
221
+ 🏠 **Previsão de Preço**
222
+
223
+ 💰 **Preço Estimado: ${pred_price:,.2f}**
224
+
225
+ {features_text}
226
+
227
+ *Nota: Esta é uma estimativa baseada no modelo de regressão linear.*
228
+ """
229
+
230
+ return result_text, pred_price
231
+
232
+ def create_analysis_plots():
233
+ """Cria gráficos de análise exploratória"""
234
+ if predictor.df is None:
235
+ return None, None, None
236
+
237
+ try:
238
+ # Gráfico 1: Distribuição de preços
239
+ fig1, ax1 = plt.subplots(figsize=(10, 6))
240
+ ax1.hist(predictor.df['price'], bins=50, edgecolor='black', alpha=0.7, color='steelblue')
241
+ ax1.axvline(predictor.df['price'].mean(), color='red', linestyle='--',
242
+ label=f'Média: ${predictor.df["price"].mean():,.0f}')
243
+ ax1.axvline(predictor.df['price'].median(), color='green', linestyle='--',
244
+ label=f'Mediana: ${predictor.df["price"].median():,.0f}')
245
+ ax1.set_xlabel('Preço ($)')
246
+ ax1.set_ylabel('Frequência')
247
+ ax1.set_title('Distribuição dos Preços dos Imóveis')
248
+ ax1.legend()
249
+ ax1.grid(True, alpha=0.3)
250
+
251
+ # Gráfico 2: Correlações
252
+ numeric_cols = predictor.df.select_dtypes(include=[np.number]).columns.tolist()
253
+ correlations = predictor.df[numeric_cols].corr()['price'].sort_values(ascending=False)
254
+ top_corr = correlations[1:11]
255
+
256
+ fig2, ax2 = plt.subplots(figsize=(10, 6))
257
+ colors = ['green' if x > 0 else 'red' for x in top_corr.values]
258
+ bars = ax2.barh(range(len(top_corr)), top_corr.values, color=colors, alpha=0.7, edgecolor='black')
259
+ ax2.set_yticks(range(len(top_corr)))
260
+ ax2.set_yticklabels(top_corr.index)
261
+ ax2.set_xlabel('Coeficiente de Correlação')
262
+ ax2.set_title('Top 10 Variáveis Correlacionadas com Preço')
263
+ ax2.axvline(0, color='black', linewidth=0.8)
264
+ ax2.grid(True, alpha=0.3, axis='x')
265
+
266
+ for i, (bar, val) in enumerate(zip(bars, top_corr.values)):
267
+ ax2.text(val + 0.01 if val > 0 else val - 0.01, i, f'{val:.3f}',
268
+ va='center', ha='left' if val > 0 else 'right', fontsize=9, fontweight='bold')
269
+
270
+ # Gráfico 3: Scatter plot da feature mais correlacionada
271
+ most_correlated_feature = top_corr.index[0]
272
+ fig3, ax3 = plt.subplots(figsize=(10, 6))
273
+ ax3.scatter(predictor.df[most_correlated_feature], predictor.df['price'],
274
+ alpha=0.3, s=10, color='steelblue')
275
+ ax3.set_xlabel(most_correlated_feature)
276
+ ax3.set_ylabel('Preço ($)')
277
+ ax3.set_title(f'Preço vs {most_correlated_feature}\n(Corr: {top_corr.iloc[0]:.3f})')
278
+ ax3.grid(True, alpha=0.3)
279
+
280
+ # Adicionar linha de tendência
281
+ z = np.polyfit(predictor.df[most_correlated_feature], predictor.df['price'], 1)
282
+ p = np.poly1d(z)
283
+ ax3.plot(predictor.df[most_correlated_feature], p(predictor.df[most_correlated_feature]),
284
+ "r--", linewidth=2, alpha=0.8)
285
+
286
+ return fig1, fig2, fig3
287
+
288
+ except Exception as e:
289
+ print(f"Erro ao criar gráficos: {e}")
290
+ return None, None, None
291
+
292
+ # Interface Gradio
293
+ with gr.Blocks(theme=gr.themes.Soft(), title="Previsão de Preços de Imóveis") as demo:
294
+ gr.Markdown(
295
+ """
296
+ # 🏠 Previsão de Preços de Imóveis - King County
297
+
298
+ Este aplicativo utiliza machine learning para prever preços de imóveis baseado em suas características.
299
+ O modelo é treinado com dados reais de King County, Washington, USA.
300
+ """
301
+ )
302
+
303
+ with gr.Tab("📊 Carregar Dados e Treinar"):
304
+ with gr.Row():
305
+ with gr.Column():
306
+ file_input = gr.File(
307
+ label="Upload do CSV",
308
+ file_types=[".csv"],
309
+ type="filepath"
310
+ )
311
+ load_btn = gr.Button("📂 Carregar Dados", variant="primary")
312
+
313
+ with gr.Column():
314
+ load_status = gr.Markdown("Aguardando upload do arquivo CSV...")
315
+ train_btn = gr.Button("🤖 Treinar Modelo", variant="primary", visible=False)
316
+
317
+ with gr.Row():
318
+ train_output = gr.Markdown("")
319
+ metrics_json = gr.JSON(label="Métricas Detalhadas", visible=False)
320
+
321
+ # Conectar eventos
322
+ load_btn.click(
323
+ load_data,
324
+ inputs=[file_input],
325
+ outputs=[load_status, train_btn]
326
+ )
327
+
328
+ train_btn.click(
329
+ train_model_action,
330
+ outputs=[train_output, metrics_json, metrics_json]
331
+ )
332
+
333
+ with gr.Tab("🔍 Análise Exploratória"):
334
+ gr.Markdown("### Análise Exploratória dos Dados")
335
+ analysis_btn = gr.Button("📈 Gerar Análises", variant="primary")
336
+
337
+ with gr.Row():
338
+ plot1 = gr.Plot(label="Distribuição de Preços")
339
+ plot2 = gr.Plot(label="Features Mais Correlacionadas")
340
+
341
+ plot3 = gr.Plot(label="Relação com Feature Mais Importante")
342
+
343
+ analysis_btn.click(
344
+ create_analysis_plots,
345
+ outputs=[plot1, plot2, plot3]
346
+ )
347
+
348
+ with gr.Tab("🎯 Fazer Previsão"):
349
+ gr.Markdown("### Faça uma Previsão de Preço")
350
+
351
+ with gr.Row():
352
+ with gr.Column():
353
+ prediction_inputs = gr.Column(visible=False)
354
+ predict_btn = gr.Button("💰 Prever Preço", variant="primary")
355
+
356
+ with gr.Column():
357
+ prediction_output = gr.Markdown("Preencha os valores das características e clique em 'Prever Preço'")
358
+ price_display = gr.Number(
359
+ label="Preço Previsto ($)",
360
+ visible=False
361
+ )
362
+
363
+ # Atualizar interface de previsão quando o modelo for treinado
364
+ def update_prediction_interface(metrics_result):
365
+ inputs = create_prediction_interface(metrics_result)
366
+ return gr.update(visible=True, value=inputs) if inputs else gr.update(visible=False)
367
+
368
+ metrics_json.change(
369
+ update_prediction_interface,
370
+ inputs=[metrics_json],
371
+ outputs=[prediction_inputs]
372
+ )
373
+
374
+ # Conectar botão de previsão
375
+ predict_btn.click(
376
+ predict_price_action,
377
+ inputs=[prediction_inputs],
378
+ outputs=[prediction_output, price_display]
379
+ ).then(
380
+ lambda: gr.update(visible=True),
381
+ outputs=[price_display]
382
+ )
383
+
384
+ with gr.Tab("ℹ️ Sobre"):
385
+ gr.Markdown(
386
+ """
387
+ ## Sobre este Projeto
388
+
389
+ ### 📋 Descrição
390
+ Este aplicativo implementa um modelo de Regressão Linear para prever preços de imóveis
391
+ baseado no dataset "House Sales in King County, USA".
392
+
393
+ ### 🛠️ Tecnologias Utilizadas
394
+ - **Python** com scikit-learn para machine learning
395
+ - **Gradio** para interface web
396
+ - **Pandas** e **NumPy** para manipulação de dados
397
+ - **Matplotlib** e **Seaborn** para visualizações
398
+
399
+ ### 📊 Características do Modelo
400
+ - **Algoritmo**: Regressão Linear Múltipla
401
+ - **Pré-processamento**: Padronização de features e transformação logarítmica do target
402
+ - **Validação**: Divisão 80/20 treino/teste
403
+ - **Métricas**: R², RMSE, MAE
404
+
405
+ ### 🎯 Features Utilizadas
406
+ O modelo considera diversas características dos imóveis como:
407
+ - Área construída
408
+ - Número de quartos e banheiros
409
+ - Localização (latitude/longitude)
410
+ - Condição e qualidade do imóvel
411
+ - Ano de construção e renovação
412
+ - E outras características relevantes
413
+
414
+ ### 📝 Notas Importantes
415
+ - Os resultados são estimativas baseadas em padrões históricos
416
+ - Fatores externos não capturados pelo modelo podem influenciar os preços reais
417
+ - Recomenda-se usar como ferramenta auxiliar na tomada de decisão
418
+ """
419
+ )
420
+
421
+ # Para executar localmente (descomente se quiser testar)
422
+ # if __name__ == "__main__":
423
+ # demo.launch(share=True)
424
+
425
+ # Para Hugging Face Spaces
426
+ demo.launch()