Spaces:
Sleeping
Sleeping
Update app.py
Browse files
app.py
CHANGED
|
@@ -3,224 +3,136 @@ import pandas as pd
|
|
| 3 |
import numpy as np
|
| 4 |
import matplotlib.pyplot as plt
|
| 5 |
import seaborn as sns
|
| 6 |
-
|
| 7 |
-
|
| 8 |
-
from plotly.subplots import make_subplots
|
| 9 |
-
from sklearn.linear_model import LinearRegression, Ridge
|
| 10 |
-
from sklearn.ensemble import RandomForestRegressor
|
| 11 |
-
from sklearn.model_selection import train_test_split, cross_val_score
|
| 12 |
from sklearn.metrics import r2_score, mean_squared_error, mean_absolute_error
|
| 13 |
-
from
|
| 14 |
-
from sklearn.inspection import permutation_importance
|
| 15 |
-
from scipy import stats
|
| 16 |
-
from scipy.stats import kruskal, shapiro, levene, norm
|
| 17 |
import io
|
| 18 |
import base64
|
| 19 |
-
import warnings
|
| 20 |
-
warnings.filterwarnings('ignore')
|
| 21 |
|
| 22 |
-
#
|
| 23 |
-
plt.style.use('
|
| 24 |
-
sns.
|
| 25 |
-
plt.rcParams['figure.figsize'] = (14, 10)
|
| 26 |
-
plt.rcParams['font.size'] = 12
|
| 27 |
|
| 28 |
-
|
| 29 |
-
|
| 30 |
-
'primaria': '#2E86AB',
|
| 31 |
-
'secundaria': '#A23B72',
|
| 32 |
-
'sucesso': '#18A558',
|
| 33 |
-
'alerta': '#F18F01',
|
| 34 |
-
'perigo': '#C73E1D',
|
| 35 |
-
'neutro': '#6C757D',
|
| 36 |
-
'fundo': '#F8F9FA'
|
| 37 |
-
}
|
| 38 |
-
|
| 39 |
-
def criar_dashboard_profissional():
|
| 40 |
-
"""Função principal que cria o dashboard profissional"""
|
| 41 |
-
|
| 42 |
-
with gr.Blocks(theme=gr.themes.Soft(primary_hue="blue", neutral_hue="slate")) as dashboard:
|
| 43 |
|
| 44 |
-
# HEADER
|
| 45 |
-
|
| 46 |
-
|
| 47 |
-
|
| 48 |
-
|
| 49 |
-
|
| 50 |
-
|
| 51 |
-
|
| 52 |
-
|
| 53 |
-
"""
|
| 54 |
-
)
|
| 55 |
|
| 56 |
-
# MENU
|
| 57 |
with gr.Row():
|
| 58 |
-
|
| 59 |
-
|
| 60 |
-
|
| 61 |
-
|
| 62 |
-
|
| 63 |
-
info="Selecione a análise desejada"
|
| 64 |
-
)
|
| 65 |
|
| 66 |
gr.Markdown("---")
|
| 67 |
|
| 68 |
-
#
|
| 69 |
with gr.Row():
|
| 70 |
with gr.Column(scale=1):
|
| 71 |
-
|
| 72 |
-
|
| 73 |
-
|
| 74 |
-
|
| 75 |
-
|
| 76 |
-
|
| 77 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 78 |
)
|
| 79 |
-
|
| 80 |
-
|
| 81 |
-
|
| 82 |
-
|
| 83 |
-
|
| 84 |
-
|
| 85 |
-
|
| 86 |
-
|
| 87 |
-
|
| 88 |
-
|
| 89 |
-
|
| 90 |
-
|
| 91 |
-
|
| 92 |
-
|
| 93 |
-
|
| 94 |
-
with gr.Row():
|
| 95 |
-
var_cont_reg_1 = gr.Dropdown(
|
| 96 |
-
label="Variável Contínua 1 (Regressão)",
|
| 97 |
-
choices=[],
|
| 98 |
-
interactive=True
|
| 99 |
-
)
|
| 100 |
-
var_cont_reg_2 = gr.Dropdown(
|
| 101 |
-
label="Variável Contínua 2 (Regressão)",
|
| 102 |
-
choices=[],
|
| 103 |
-
interactive=True
|
| 104 |
-
)
|
| 105 |
-
|
| 106 |
-
var_cat_reg = gr.Dropdown(
|
| 107 |
-
label="Variável Categórica (Regressão)",
|
| 108 |
choices=[],
|
| 109 |
interactive=True
|
| 110 |
)
|
| 111 |
-
|
| 112 |
-
with gr.Row():
|
| 113 |
-
aplicar_log = gr.Checkbox(
|
| 114 |
-
label="Aplicar Transformação Logarítmica",
|
| 115 |
-
value=True,
|
| 116 |
-
interactive=True
|
| 117 |
-
)
|
| 118 |
-
testar_pressupostos = gr.Checkbox(
|
| 119 |
-
label="Testar Pressupostos Estatísticos",
|
| 120 |
-
value=True,
|
| 121 |
-
interactive=True
|
| 122 |
-
)
|
| 123 |
|
| 124 |
btn_analisar = gr.Button(
|
| 125 |
-
"🚀
|
| 126 |
variant="primary",
|
| 127 |
size="lg"
|
| 128 |
)
|
| 129 |
|
| 130 |
-
#
|
| 131 |
with gr.Column(scale=2):
|
| 132 |
-
|
| 133 |
-
label="Resultados da Análise",
|
| 134 |
value="""
|
| 135 |
-
<div style="text-align: center; padding:
|
| 136 |
<h3 style="color: #6c757d;">📊 Área de Resultados</h3>
|
| 137 |
-
<p style="color: #6c757d;">Configure os parâmetros e execute a análise
|
| 138 |
</div>
|
| 139 |
"""
|
| 140 |
)
|
| 141 |
|
| 142 |
-
# FUNÇÕES
|
| 143 |
def atualizar_variaveis(arquivo):
|
| 144 |
-
"""Atualiza dropdowns com variáveis do dataset"""
|
| 145 |
if arquivo is None:
|
| 146 |
-
|
| 147 |
-
|
| 148 |
-
return [gr.Dropdown(choices=variaveis)] * 5
|
| 149 |
else:
|
| 150 |
try:
|
| 151 |
df = pd.read_csv(arquivo.name)
|
| 152 |
variaveis = df.columns.tolist()
|
| 153 |
-
return [gr.Dropdown(choices=variaveis)] *
|
| 154 |
except:
|
| 155 |
-
return [gr.Dropdown(choices=[])] *
|
| 156 |
|
| 157 |
-
|
| 158 |
-
def executar_analise_completa(arquivo, var_cat1, var_cat2, var_cont1, var_cont2, var_cat_reg, aplicar_log, testar_pressupostos, tab_selecionada):
|
| 159 |
-
"""Executa análise estatística completa"""
|
| 160 |
-
|
| 161 |
try:
|
| 162 |
-
# CARREGAR DADOS
|
| 163 |
if arquivo is None:
|
| 164 |
df = gerar_dados_exemplo()
|
| 165 |
else:
|
| 166 |
df = pd.read_csv(arquivo.name)
|
| 167 |
|
| 168 |
-
|
| 169 |
-
|
| 170 |
-
|
| 171 |
-
|
| 172 |
-
# ANÁLISE ANOVA
|
| 173 |
-
elif tab_selecionada == "📈 ANOVA Estatística":
|
| 174 |
-
return gerar_analise_anova(df, var_cat1, var_cat2, testar_pressupostos)
|
| 175 |
-
|
| 176 |
-
# MODELO PREDITIVO
|
| 177 |
-
elif tab_selecionada == "🤖 Modelo Preditivo":
|
| 178 |
-
return gerar_modelo_preditivo(df, var_cont1, var_cont2, var_cat_reg, aplicar_log, testar_pressupostos)
|
| 179 |
-
|
| 180 |
-
# RELATÓRIO COMPLETO
|
| 181 |
else:
|
| 182 |
-
return
|
| 183 |
|
| 184 |
except Exception as e:
|
| 185 |
return f"""
|
| 186 |
-
<div style="background: #f8d7da; color: #721c24; padding: 20px; border-radius: 10px;
|
| 187 |
-
<h3>❌ Erro
|
| 188 |
-
<p>
|
| 189 |
-
<p>Verifique se as variáveis selecionadas existem no dataset.</p>
|
| 190 |
</div>
|
| 191 |
"""
|
| 192 |
|
| 193 |
-
#
|
| 194 |
-
|
| 195 |
-
|
| 196 |
-
inputs=[arquivo_csv],
|
| 197 |
-
outputs=[var_cat_anova_1, var_cat_anova_2, var_cont_reg_1, var_cont_reg_2, var_cat_reg]
|
| 198 |
-
)
|
| 199 |
-
|
| 200 |
-
btn_analisar.click(
|
| 201 |
-
fn=executar_analise_completa,
|
| 202 |
-
inputs=[arquivo_csv, var_cat_anova_1, var_cat_anova_2, var_cont_reg_1, var_cont_reg_2, var_cat_reg, aplicar_log, testar_pressupostos, selected_tab],
|
| 203 |
-
outputs=[output_analise]
|
| 204 |
-
)
|
| 205 |
-
|
| 206 |
-
selected_tab.change(
|
| 207 |
-
fn=lambda tab: f"""
|
| 208 |
-
<div style="text-align: center; padding: 40px; background: #e9ecef; border-radius: 10px;">
|
| 209 |
-
<h3>Pronto para análise: {tab}</h3>
|
| 210 |
-
<p>Configure os parâmetros e clique em EXECUTAR ANÁLISE COMPLETA</p>
|
| 211 |
-
</div>
|
| 212 |
-
""",
|
| 213 |
-
inputs=[selected_tab],
|
| 214 |
-
outputs=[output_analise]
|
| 215 |
-
)
|
| 216 |
|
| 217 |
# FOOTER
|
| 218 |
gr.Markdown("---")
|
| 219 |
gr.Markdown(
|
| 220 |
"""
|
| 221 |
-
<div style="text-align: center; color: #6c757d;
|
| 222 |
-
<p><strong>
|
| 223 |
-
<p><strong>Tecnologias:</strong> Python • Scikit-learn • Plotly • Gradio • Estatística Avançada</p>
|
| 224 |
</div>
|
| 225 |
"""
|
| 226 |
)
|
|
@@ -228,237 +140,154 @@ def criar_dashboard_profissional():
|
|
| 228 |
return dashboard
|
| 229 |
|
| 230 |
def gerar_dados_exemplo():
|
| 231 |
-
"""Gera dados de exemplo realistas"""
|
| 232 |
np.random.seed(42)
|
| 233 |
-
n_imoveis =
|
| 234 |
|
| 235 |
dados = pd.DataFrame({
|
| 236 |
-
'SalePrice': np.random.
|
| 237 |
-
'GrLivArea': np.random.normal(
|
| 238 |
-
'OverallQual': np.random.
|
| 239 |
-
'Neighborhood': np.random.choice(['Centro', 'Norte', 'Sul', 'Leste'
|
| 240 |
-
'FullBath': np.random.
|
| 241 |
-
'GarageCars': np.random.
|
| 242 |
-
'BedroomAbvGr': np.random.
|
| 243 |
-
'YearBuilt': np.random.randint(1950, 2020, n_imoveis),
|
| 244 |
-
'Fireplaces': np.random.choice([0,1,2], n_imoveis, p=[0.3,0.6,0.1]),
|
| 245 |
-
'LotArea': np.random.lognormal(8.5, 0.6, n_imoveis)
|
| 246 |
})
|
| 247 |
|
| 248 |
-
|
| 249 |
-
|
| 250 |
-
dados['
|
| 251 |
-
dados['OverallQual'] * 25000 +
|
| 252 |
-
dados['FullBath'] * 18000 +
|
| 253 |
-
dados['GarageCars'] * 15000 +
|
| 254 |
-
(dados['YearBuilt'] - 1950) * 800 +
|
| 255 |
-
dados['Fireplaces'] * 12000)
|
| 256 |
-
|
| 257 |
dados['SalePrice'] = dados['SalePrice'].astype(int)
|
| 258 |
-
dados['GrLivArea'] = dados['GrLivArea'].astype(int)
|
| 259 |
-
dados['LotArea'] = dados['LotArea'].astype(int)
|
| 260 |
|
| 261 |
return dados
|
| 262 |
|
| 263 |
-
def
|
| 264 |
-
|
|
|
|
| 265 |
|
| 266 |
-
#
|
| 267 |
-
|
| 268 |
-
|
| 269 |
-
|
| 270 |
-
|
| 271 |
-
|
| 272 |
|
| 273 |
-
# Gráfico
|
| 274 |
-
|
| 275 |
-
|
|
|
|
|
|
|
|
|
|
| 276 |
|
| 277 |
-
|
| 278 |
-
|
| 279 |
-
|
| 280 |
-
|
|
|
|
|
|
|
|
|
|
| 281 |
|
| 282 |
-
#
|
| 283 |
-
if '
|
| 284 |
-
|
| 285 |
-
|
| 286 |
-
|
| 287 |
-
color_discrete_sequence=['#A23B72'])
|
| 288 |
-
else:
|
| 289 |
-
fig_scatter = go.Figure()
|
| 290 |
-
fig_scatter.add_annotation(text="Variáveis não disponíveis para scatter plot")
|
| 291 |
|
| 292 |
-
|
| 293 |
-
stats_html = gerar_estatisticas_descritivas(df)
|
| 294 |
|
| 295 |
-
# Converter
|
| 296 |
-
|
| 297 |
-
|
| 298 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 299 |
|
| 300 |
return f"""
|
| 301 |
-
<div style="font-family:
|
| 302 |
-
<h2 style="color: #2E86AB;
|
| 303 |
-
|
| 304 |
-
<div style="background: #f8f9fa; padding: 20px; border-radius: 10px; margin-bottom: 20px;">
|
| 305 |
-
<h3 style="color: #495057;">📈 Estatísticas Descritivas</h3>
|
| 306 |
-
{stats_html}
|
| 307 |
-
</div>
|
| 308 |
|
| 309 |
-
<div style="
|
| 310 |
-
<
|
| 311 |
-
|
| 312 |
-
|
| 313 |
-
|
| 314 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 315 |
</div>
|
| 316 |
</div>
|
| 317 |
|
| 318 |
-
<div style="
|
| 319 |
-
{
|
| 320 |
</div>
|
| 321 |
</div>
|
| 322 |
"""
|
| 323 |
|
| 324 |
-
def
|
| 325 |
-
"""Gera tabela de estatísticas descritivas"""
|
| 326 |
-
if 'SalePrice' in df.columns:
|
| 327 |
-
stats = df['SalePrice'].describe()
|
| 328 |
-
return f"""
|
| 329 |
-
<div style="display: grid; grid-template-columns: repeat(4, 1fr); gap: 10px;">
|
| 330 |
-
<div style="background: #e7f3ff; padding: 10px; border-radius: 5px; text-align: center;">
|
| 331 |
-
<strong>Média</strong><br>R$ {stats['mean']:,.0f}
|
| 332 |
-
</div>
|
| 333 |
-
<div style="background: #e7f3ff; padding: 10px; border-radius: 5px; text-align: center;">
|
| 334 |
-
<strong>Mediana</strong><br>R$ {stats['50%']:,.0f}
|
| 335 |
-
</div>
|
| 336 |
-
<div style="background: #e7f3ff; padding: 10px; border-radius: 5px; text-align: center;">
|
| 337 |
-
<strong>Desvio Padrão</strong><br>R$ {stats['std']:,.0f}
|
| 338 |
-
</div>
|
| 339 |
-
<div style="background: #e7f3ff; padding: 10px; border-radius: 5px; text-align: center;">
|
| 340 |
-
<strong>Total</strong><br>{len(df)} imóveis
|
| 341 |
-
</div>
|
| 342 |
-
</div>
|
| 343 |
-
"""
|
| 344 |
-
return "<p>Estatísticas não disponíveis</p>"
|
| 345 |
-
|
| 346 |
-
def gerar_analise_anova(df, var_cat1, var_cat2, testar_pressupostos):
|
| 347 |
-
"""Gera análise ANOVA/Kruskal-Wallis"""
|
| 348 |
-
|
| 349 |
resultados = []
|
| 350 |
|
| 351 |
-
for
|
| 352 |
-
if
|
| 353 |
-
|
| 354 |
-
|
| 355 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 356 |
|
| 357 |
if resultados:
|
| 358 |
resultados_html = "\n".join(resultados)
|
| 359 |
else:
|
| 360 |
-
resultados_html = "<p>Selecione variáveis categóricas para análise
|
| 361 |
-
|
| 362 |
-
return f"""
|
| 363 |
-
<div style="font-family: 'Segoe UI', Arial, sans-serif;">
|
| 364 |
-
<h2 style="color: #A23B72; border-bottom: 2px solid #A23B72; padding-bottom: 10px;">📈 ANÁLISE ANOVA - DIFERENÇAS ENTRE CATEGORIAS</h2>
|
| 365 |
-
{resultados_html}
|
| 366 |
-
</div>
|
| 367 |
-
"""
|
| 368 |
-
|
| 369 |
-
def executar_teste_anova(df, variavel, testar_pressupostos):
|
| 370 |
-
"""Executa teste ANOVA ou Kruskal-Wallis"""
|
| 371 |
-
|
| 372 |
-
grupos = [grupo['SalePrice'].values for nome, grupo in df.groupby(variavel)]
|
| 373 |
-
|
| 374 |
-
# Teste de normalidade
|
| 375 |
-
if testar_pressupostos:
|
| 376 |
-
p_valores_normalidade = [shapiro(grupo)[1] for grupo in grupos if len(grupo) > 3 and len(grupo) < 5000]
|
| 377 |
-
normalidade_ok = all(p > 0.05 for p in p_valores_normalidade) if p_valores_normalidade else False
|
| 378 |
-
else:
|
| 379 |
-
normalidade_ok = False
|
| 380 |
-
|
| 381 |
-
# Escolher teste
|
| 382 |
-
if normalidade_ok:
|
| 383 |
-
from scipy.stats import f_oneway
|
| 384 |
-
estatistica, p_valor = f_oneway(*grupos)
|
| 385 |
-
teste_usado = "ANOVA One-Way"
|
| 386 |
-
else:
|
| 387 |
-
estatistica, p_valor = kruskal(*grupos)
|
| 388 |
-
teste_usado = "Kruskal-Wallis (não-paramétrico)"
|
| 389 |
-
|
| 390 |
-
# Interpretação
|
| 391 |
-
significativo = p_valor < 0.05
|
| 392 |
-
icone = "✅" if significativo else "❌"
|
| 393 |
-
conclusao = "IMPACTA SIGNIFICATIVAMENTE" if significativo else "NÃO IMPACTA SIGNIFICATIVAMENTE"
|
| 394 |
|
| 395 |
return f"""
|
| 396 |
-
<div style="
|
| 397 |
-
<
|
| 398 |
-
<
|
| 399 |
-
|
| 400 |
-
|
| 401 |
-
<
|
| 402 |
-
<p><strong>Interpretação:</strong> Há {'diferenças significativas' if significativo else 'diferenças não significativas'} nos preços médios entre as categorias de {variavel}.</p>
|
| 403 |
</div>
|
| 404 |
"""
|
| 405 |
|
| 406 |
-
def
|
| 407 |
-
"""Gera modelo preditivo de regressão"""
|
| 408 |
-
|
| 409 |
return """
|
| 410 |
-
<div style="font-family:
|
| 411 |
-
<h2 style="color: #18A558;
|
| 412 |
<div style="background: #d1ecf1; padding: 20px; border-radius: 10px;">
|
| 413 |
-
<h3>
|
| 414 |
-
<p>
|
|
|
|
| 415 |
<ul>
|
| 416 |
-
<li>
|
| 417 |
-
<li>
|
| 418 |
-
<li>📈
|
| 419 |
-
<li>🎯
|
| 420 |
</ul>
|
| 421 |
-
<p><strong>Status:</strong> Implementação em andamento</p>
|
| 422 |
-
</div>
|
| 423 |
-
</div>
|
| 424 |
-
"""
|
| 425 |
-
|
| 426 |
-
def gerar_relatorio_completo(df, var_cat1, var_cat2, var_cont1, var_cont2, var_cat_reg, aplicar_log, testar_pressupostos):
|
| 427 |
-
"""Gera relatório completo da análise"""
|
| 428 |
-
|
| 429 |
-
return """
|
| 430 |
-
<div style="font-family: 'Segoe UI', Arial, sans-serif;">
|
| 431 |
-
<h2 style="color: #F18F01; border-bottom: 2px solid #F18F01; padding-bottom: 10px;">📋 RELATÓRIO COMPLETO DA ANÁLISE</h2>
|
| 432 |
-
<div style="background: #fff3cd; padding: 20px; border-radius: 10px;">
|
| 433 |
-
<h3>📖 Sumário Executivo</h3>
|
| 434 |
-
<p>Este dashboard oferece análise estatística completa para precificação imobiliária, incluindo:</p>
|
| 435 |
-
|
| 436 |
-
<div style="display: grid; grid-template-columns: 1fr 1fr; gap: 15px; margin: 20px 0;">
|
| 437 |
-
<div style="background: white; padding: 15px; border-radius: 8px; border-left: 4px solid #2E86AB;">
|
| 438 |
-
<h4>📊 Análise Exploratória</h4>
|
| 439 |
-
<p>Visualização interativa dos dados e estatísticas descritivas</p>
|
| 440 |
-
</div>
|
| 441 |
-
<div style="background: white; padding: 15px; border-radius: 8px; border-left: 4px solid #A23B72;">
|
| 442 |
-
<h4>📈 Testes ANOVA</h4>
|
| 443 |
-
<p>Análise de diferenças entre categorias com diagnósticos</p>
|
| 444 |
-
</div>
|
| 445 |
-
<div style="background: white; padding: 15px; border-radius: 8px; border-left: 4px solid #18A558;">
|
| 446 |
-
<h4>🤖 Modelagem Preditiva</h4>
|
| 447 |
-
<p>Regressão linear múltipla com transformações</p>
|
| 448 |
-
</div>
|
| 449 |
-
<div style="background: white; padding: 15px; border-radius: 8px; border-left: 4px solid #F18F01;">
|
| 450 |
-
<h4>📋 Relatórios Automáticos</h4>
|
| 451 |
-
<p>Documentação completa dos resultados</p>
|
| 452 |
-
</div>
|
| 453 |
-
</div>
|
| 454 |
-
|
| 455 |
-
<h3>🎯 Próximos Passos</h3>
|
| 456 |
-
<p>Para uma análise completa, navegue entre as abas e configure os parâmetros desejados.</p>
|
| 457 |
</div>
|
| 458 |
</div>
|
| 459 |
"""
|
| 460 |
|
| 461 |
-
#
|
| 462 |
if __name__ == "__main__":
|
| 463 |
-
dashboard =
|
| 464 |
-
dashboard.launch(
|
|
|
|
| 3 |
import numpy as np
|
| 4 |
import matplotlib.pyplot as plt
|
| 5 |
import seaborn as sns
|
| 6 |
+
from sklearn.linear_model import LinearRegression
|
| 7 |
+
from sklearn.model_selection import train_test_split
|
|
|
|
|
|
|
|
|
|
|
|
|
| 8 |
from sklearn.metrics import r2_score, mean_squared_error, mean_absolute_error
|
| 9 |
+
from scipy.stats import kruskal, shapiro
|
|
|
|
|
|
|
|
|
|
| 10 |
import io
|
| 11 |
import base64
|
|
|
|
|
|
|
| 12 |
|
| 13 |
+
# Configuracoes
|
| 14 |
+
plt.style.use('default')
|
| 15 |
+
sns.set_style("whitegrid")
|
|
|
|
|
|
|
| 16 |
|
| 17 |
+
def criar_dashboard():
|
| 18 |
+
with gr.Blocks(theme=gr.themes.Soft()) as dashboard:
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 19 |
|
| 20 |
+
# HEADER
|
| 21 |
+
gr.Markdown(
|
| 22 |
+
"""
|
| 23 |
+
<div style="text-align: center; background: linear-gradient(135deg, #667eea 0%, #764ba2 100%); padding: 30px; border-radius: 15px; color: white;">
|
| 24 |
+
<h1 style="margin: 0;">🏠 ANALYTICS IMOBILIÁRIO</h1>
|
| 25 |
+
<p style="margin: 0; opacity: 0.9;">Dashboard de Modelagem Estatística - UnB</p>
|
| 26 |
+
</div>
|
| 27 |
+
"""
|
| 28 |
+
)
|
|
|
|
|
|
|
| 29 |
|
| 30 |
+
# MENU
|
| 31 |
with gr.Row():
|
| 32 |
+
aba = gr.Radio(
|
| 33 |
+
choices=["📊 Análise Exploratória", "📈 ANOVA", "🤖 Regressão Linear"],
|
| 34 |
+
value="📊 Análise Exploratória",
|
| 35 |
+
label="Navegação"
|
| 36 |
+
)
|
|
|
|
|
|
|
| 37 |
|
| 38 |
gr.Markdown("---")
|
| 39 |
|
| 40 |
+
# CONTROLES
|
| 41 |
with gr.Row():
|
| 42 |
with gr.Column(scale=1):
|
| 43 |
+
arquivo = gr.File(
|
| 44 |
+
label="📁 Upload do Dataset (CSV)",
|
| 45 |
+
file_types=[".csv"]
|
| 46 |
+
)
|
| 47 |
+
|
| 48 |
+
gr.Markdown("**⚙️ Configurações de Análise**")
|
| 49 |
+
|
| 50 |
+
with gr.Row():
|
| 51 |
+
var_cat1 = gr.Dropdown(
|
| 52 |
+
label="Variável Categórica 1",
|
| 53 |
+
choices=[],
|
| 54 |
+
interactive=True
|
| 55 |
)
|
| 56 |
+
var_cat2 = gr.Dropdown(
|
| 57 |
+
label="Variável Categórica 2",
|
| 58 |
+
choices=[],
|
| 59 |
+
interactive=True
|
| 60 |
+
)
|
| 61 |
+
|
| 62 |
+
with gr.Row():
|
| 63 |
+
var_cont1 = gr.Dropdown(
|
| 64 |
+
label="Variável Contínua 1",
|
| 65 |
+
choices=[],
|
| 66 |
+
interactive=True
|
| 67 |
+
)
|
| 68 |
+
var_cont2 = gr.Dropdown(
|
| 69 |
+
label="Variável Contínua 2",
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 70 |
choices=[],
|
| 71 |
interactive=True
|
| 72 |
)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 73 |
|
| 74 |
btn_analisar = gr.Button(
|
| 75 |
+
"🚀 Executar Análise",
|
| 76 |
variant="primary",
|
| 77 |
size="lg"
|
| 78 |
)
|
| 79 |
|
| 80 |
+
# RESULTADOS
|
| 81 |
with gr.Column(scale=2):
|
| 82 |
+
resultados = gr.HTML(
|
|
|
|
| 83 |
value="""
|
| 84 |
+
<div style="text-align: center; padding: 50px; background: #f8f9fa; border-radius: 10px;">
|
| 85 |
<h3 style="color: #6c757d;">📊 Área de Resultados</h3>
|
| 86 |
+
<p style="color: #6c757d;">Configure os parâmetros e execute a análise</p>
|
| 87 |
</div>
|
| 88 |
"""
|
| 89 |
)
|
| 90 |
|
| 91 |
+
# FUNÇÕES
|
| 92 |
def atualizar_variaveis(arquivo):
|
|
|
|
| 93 |
if arquivo is None:
|
| 94 |
+
vars_padrao = ['OverallQual', 'FullBath', 'Neighborhood', 'GrLivArea', 'GarageCars', 'BedroomAbvGr']
|
| 95 |
+
return [gr.Dropdown(choices=vars_padrao)] * 4
|
|
|
|
| 96 |
else:
|
| 97 |
try:
|
| 98 |
df = pd.read_csv(arquivo.name)
|
| 99 |
variaveis = df.columns.tolist()
|
| 100 |
+
return [gr.Dropdown(choices=variaveis)] * 4
|
| 101 |
except:
|
| 102 |
+
return [gr.Dropdown(choices=[])] * 4
|
| 103 |
|
| 104 |
+
def executar_analise(arquivo, var_cat1, var_cat2, var_cont1, var_cont2, aba):
|
|
|
|
|
|
|
|
|
|
| 105 |
try:
|
|
|
|
| 106 |
if arquivo is None:
|
| 107 |
df = gerar_dados_exemplo()
|
| 108 |
else:
|
| 109 |
df = pd.read_csv(arquivo.name)
|
| 110 |
|
| 111 |
+
if aba == "📊 Análise Exploratória":
|
| 112 |
+
return analise_exploratoria(df)
|
| 113 |
+
elif aba == "📈 ANOVA":
|
| 114 |
+
return analise_anova(df, var_cat1, var_cat2)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 115 |
else:
|
| 116 |
+
return analise_regressao(df, var_cont1, var_cont2, var_cat1)
|
| 117 |
|
| 118 |
except Exception as e:
|
| 119 |
return f"""
|
| 120 |
+
<div style="background: #f8d7da; color: #721c24; padding: 20px; border-radius: 10px;">
|
| 121 |
+
<h3>❌ Erro</h3>
|
| 122 |
+
<p>{str(e)}</p>
|
|
|
|
| 123 |
</div>
|
| 124 |
"""
|
| 125 |
|
| 126 |
+
# CONEXÕES
|
| 127 |
+
arquivo.change(atualizar_variaveis, [arquivo], [var_cat1, var_cat2, var_cont1, var_cont2])
|
| 128 |
+
btn_analisar.click(executar_analise, [arquivo, var_cat1, var_cat2, var_cont1, var_cont2, aba], [resultados])
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 129 |
|
| 130 |
# FOOTER
|
| 131 |
gr.Markdown("---")
|
| 132 |
gr.Markdown(
|
| 133 |
"""
|
| 134 |
+
<div style="text-align: center; color: #6c757d;">
|
| 135 |
+
<p><strong>Desenvolvido por:</strong> Emily Valkiria | <strong>Disciplina:</strong> SIEP - UnB</p>
|
|
|
|
| 136 |
</div>
|
| 137 |
"""
|
| 138 |
)
|
|
|
|
| 140 |
return dashboard
|
| 141 |
|
| 142 |
def gerar_dados_exemplo():
|
|
|
|
| 143 |
np.random.seed(42)
|
| 144 |
+
n_imoveis = 500
|
| 145 |
|
| 146 |
dados = pd.DataFrame({
|
| 147 |
+
'SalePrice': np.random.normal(300000, 80000, n_imoveis),
|
| 148 |
+
'GrLivArea': np.random.normal(1500, 400, n_imoveis),
|
| 149 |
+
'OverallQual': np.random.randint(1, 11, n_imoveis),
|
| 150 |
+
'Neighborhood': np.random.choice(['Centro', 'Norte', 'Sul', 'Leste'], n_imoveis),
|
| 151 |
+
'FullBath': np.random.randint(1, 4, n_imoveis),
|
| 152 |
+
'GarageCars': np.random.randint(0, 4, n_imoveis),
|
| 153 |
+
'BedroomAbvGr': np.random.randint(1, 6, n_imoveis)
|
|
|
|
|
|
|
|
|
|
| 154 |
})
|
| 155 |
|
| 156 |
+
dados['SalePrice'] = (dados['SalePrice'] +
|
| 157 |
+
dados['GrLivArea'] * 100 +
|
| 158 |
+
dados['OverallQual'] * 15000)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 159 |
dados['SalePrice'] = dados['SalePrice'].astype(int)
|
|
|
|
|
|
|
| 160 |
|
| 161 |
return dados
|
| 162 |
|
| 163 |
+
def analise_exploratoria(df):
|
| 164 |
+
# Criar gráficos com matplotlib
|
| 165 |
+
fig, ((ax1, ax2), (ax3, ax4)) = plt.subplots(2, 2, figsize=(15, 12))
|
| 166 |
|
| 167 |
+
# Gráfico 1: Distribuição de preços
|
| 168 |
+
ax1.hist(df['SalePrice'], bins=20, color='skyblue', edgecolor='black', alpha=0.7)
|
| 169 |
+
ax1.set_title('Distribuição de Preços', fontsize=14, fontweight='bold')
|
| 170 |
+
ax1.set_xlabel('Preço (R$)')
|
| 171 |
+
ax1.set_ylabel('Frequência')
|
| 172 |
+
ax1.grid(True, alpha=0.3)
|
| 173 |
|
| 174 |
+
# Gráfico 2: Área vs Preço
|
| 175 |
+
ax2.scatter(df['GrLivArea'], df['SalePrice'], alpha=0.6, color='coral')
|
| 176 |
+
ax2.set_title('Relação: Área vs Preço', fontsize=14, fontweight='bold')
|
| 177 |
+
ax2.set_xlabel('Área (m²)')
|
| 178 |
+
ax2.set_ylabel('Preço (R$)')
|
| 179 |
+
ax2.grid(True, alpha=0.3)
|
| 180 |
|
| 181 |
+
# Gráfico 3: Qualidade vs Preço
|
| 182 |
+
qualidade_preco = df.groupby('OverallQual')['SalePrice'].mean()
|
| 183 |
+
ax3.bar(qualidade_preco.index, qualidade_preco.values, color='lightgreen', alpha=0.7)
|
| 184 |
+
ax3.set_title('Preço Médio por Qualidade', fontsize=14, fontweight='bold')
|
| 185 |
+
ax3.set_xlabel('Qualidade')
|
| 186 |
+
ax3.set_ylabel('Preço Médio (R$)')
|
| 187 |
+
ax3.grid(True, alpha=0.3)
|
| 188 |
|
| 189 |
+
# Gráfico 4: Boxplot por bairro
|
| 190 |
+
if 'Neighborhood' in df.columns:
|
| 191 |
+
df.boxplot(column='SalePrice', by='Neighborhood', ax=ax4)
|
| 192 |
+
ax4.set_title('Variação de Preços por Bairro', fontsize=14, fontweight='bold')
|
| 193 |
+
ax4.tick_params(axis='x', rotation=45)
|
|
|
|
|
|
|
|
|
|
|
|
|
| 194 |
|
| 195 |
+
plt.tight_layout()
|
|
|
|
| 196 |
|
| 197 |
+
# Converter para base64
|
| 198 |
+
buf = io.BytesIO()
|
| 199 |
+
plt.savefig(buf, format='png', dpi=100, bbox_inches='tight')
|
| 200 |
+
buf.seek(0)
|
| 201 |
+
img_str = base64.b64encode(buf.read()).decode()
|
| 202 |
+
plt.close()
|
| 203 |
+
|
| 204 |
+
# Estatísticas
|
| 205 |
+
stats = df['SalePrice'].describe()
|
| 206 |
|
| 207 |
return f"""
|
| 208 |
+
<div style="font-family: Arial, sans-serif;">
|
| 209 |
+
<h2 style="color: #2E86AB;">📊 ANÁLISE EXPLORATÓRIA</h2>
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 210 |
|
| 211 |
+
<div style="background: #e7f3ff; padding: 20px; border-radius: 10px; margin-bottom: 20px;">
|
| 212 |
+
<h3>📈 Estatísticas Descritivas</h3>
|
| 213 |
+
<div style="display: grid; grid-template-columns: repeat(4, 1fr); gap: 10px;">
|
| 214 |
+
<div style="background: white; padding: 10px; border-radius: 5px; text-align: center;">
|
| 215 |
+
<strong>Média</strong><br>R$ {stats['mean']:,.0f}
|
| 216 |
+
</div>
|
| 217 |
+
<div style="background: white; padding: 10px; border-radius: 5px; text-align: center;">
|
| 218 |
+
<strong>Mediana</strong><br>R$ {stats['50%']:,.0f}
|
| 219 |
+
</div>
|
| 220 |
+
<div style="background: white; padding: 10px; border-radius: 5px; text-align: center;">
|
| 221 |
+
<strong>Desvio Padrão</strong><br>R$ {stats['std']:,.0f}
|
| 222 |
+
</div>
|
| 223 |
+
<div style="background: white; padding: 10px; border-radius: 5px; text-align: center;">
|
| 224 |
+
<strong>Total</strong><br>{len(df)} imóveis
|
| 225 |
+
</div>
|
| 226 |
</div>
|
| 227 |
</div>
|
| 228 |
|
| 229 |
+
<div style="text-align: center;">
|
| 230 |
+
<img src="data:image/png;base64,{img_str}" style="max-width: 100%; border-radius: 10px; box-shadow: 0 4px 8px rgba(0,0,0,0.1);">
|
| 231 |
</div>
|
| 232 |
</div>
|
| 233 |
"""
|
| 234 |
|
| 235 |
+
def analise_anova(df, var_cat1, var_cat2):
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 236 |
resultados = []
|
| 237 |
|
| 238 |
+
for var in [var_cat1, var_cat2]:
|
| 239 |
+
if var and var in df.columns:
|
| 240 |
+
grupos = [grupo['SalePrice'].values for nome, grupo in df.groupby(var)]
|
| 241 |
+
stat, p_valor = kruskal(*grupos)
|
| 242 |
+
|
| 243 |
+
significativo = p_valor < 0.05
|
| 244 |
+
cor_fundo = '#d4edda' if significativo else '#f8d7da'
|
| 245 |
+
cor_borda = '#28a745' if significativo else '#dc3545'
|
| 246 |
+
icone = '✅' if significativo else '❌'
|
| 247 |
+
|
| 248 |
+
resultados.append(f"""
|
| 249 |
+
<div style="background: {cor_fundo}; padding: 15px; border-radius: 10px; margin-bottom: 15px; border-left: 5px solid {cor_borda};">
|
| 250 |
+
<h4 style="margin: 0;">{icone} {var}</h4>
|
| 251 |
+
<p><strong>Estatística Kruskal-Wallis:</strong> {stat:.4f}</p>
|
| 252 |
+
<p><strong>Valor-p:</strong> {p_valor:.6f}</p>
|
| 253 |
+
<p><strong>Conclusão:</strong> {'IMPACTA SIGNIFICATIVAMENTE' if significativo else 'NÃO IMPACTA SIGNIFICATIVAMENTE'} o preço</p>
|
| 254 |
+
</div>
|
| 255 |
+
""")
|
| 256 |
|
| 257 |
if resultados:
|
| 258 |
resultados_html = "\n".join(resultados)
|
| 259 |
else:
|
| 260 |
+
resultados_html = "<p>Selecione variáveis categóricas para análise</p>"
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 261 |
|
| 262 |
return f"""
|
| 263 |
+
<div style="font-family: Arial, sans-serif;">
|
| 264 |
+
<h2 style="color: #A23B72;">📈 ANÁLISE ANOVA</h2>
|
| 265 |
+
<div style="background: #f8f9fa; padding: 20px; border-radius: 10px;">
|
| 266 |
+
<h3>Teste Kruskal-Wallis</h3>
|
| 267 |
+
{resultados_html}
|
| 268 |
+
</div>
|
|
|
|
| 269 |
</div>
|
| 270 |
"""
|
| 271 |
|
| 272 |
+
def analise_regressao(df, var_cont1, var_cont2, var_cat):
|
|
|
|
|
|
|
| 273 |
return """
|
| 274 |
+
<div style="font-family: Arial, sans-serif;">
|
| 275 |
+
<h2 style="color: #18A558;">🤖 REGRESSÃO LINEAR</h2>
|
| 276 |
<div style="background: #d1ecf1; padding: 20px; border-radius: 10px;">
|
| 277 |
+
<h3>Modelo de Regressão Linear Múltipla</h3>
|
| 278 |
+
<p>Selecione variáveis contínuas e categóricas para construir o modelo preditivo.</p>
|
| 279 |
+
<p><strong>Funcionalidades:</strong></p>
|
| 280 |
<ul>
|
| 281 |
+
<li>📊 Métricas de desempenho (R², RMSE, MAE)</li>
|
| 282 |
+
<li>🔍 Diagnóstico de pressupostos</li>
|
| 283 |
+
<li>📈 Interpretação dos coeficientes</li>
|
| 284 |
+
<li>🎯 Recomendações práticas</li>
|
| 285 |
</ul>
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 286 |
</div>
|
| 287 |
</div>
|
| 288 |
"""
|
| 289 |
|
| 290 |
+
# INICIAR
|
| 291 |
if __name__ == "__main__":
|
| 292 |
+
dashboard = criar_dashboard()
|
| 293 |
+
dashboard.launch()
|