APARTAMENTOS_E_SALAS / salas_2026.py
ESJL's picture
Update salas_2026.py
eafaa97 verified
import gradio as gr
import pandas as pd
import numpy as np
import joblib
import xgboost as xgb
from reportlab.lib.pagesizes import A4
from reportlab.platypus import Spacer, Frame, PageTemplate, BaseDocTemplate, Paragraph
from reportlab.lib.styles import getSampleStyleSheet
# --- 1. FUNÇÃO GERADORA DE PDF ---
def get_report(FON, X, Y, RH, ANO_C, ATOTAL, PADRAO, ANO_REF, pred):
"""Gera um PDF simples com o resultado da avaliação."""
filename = 'PDF_Avaliacao.pdf'
def on_page(canvas, doc):
canvas.setTitle("Documento de Avaliação")
canvas.drawString(500, 800, "Pág. %d" % doc.page)
# Configuração do Documento
doc = BaseDocTemplate(filename, pagesize=A4)
styles = getSampleStyleSheet()
frame = Frame(doc.leftMargin, doc.bottomMargin, doc.width, doc.height, id='normal')
template = PageTemplate(id='test', frames=frame, onPage=on_page)
doc.addPageTemplates([template])
# Conteúdo
story = [
Paragraph("Salas Comerciais - XGBoost Modelo Híbrido 2026", styles['Title']),
Spacer(1, 12),
Paragraph(f"<b>Ano de Referência:</b> {ANO_REF}", styles['Normal']),
Paragraph(f"<b>Características:</b> Padrão {PADRAO}, {ATOTAL} m², Construído em {ANO_C}", styles['Normal']),
Paragraph(f"<b>Localização:</b> X={X}, Y={Y}, RH={RH}", styles['Normal']),
Spacer(1, 24),
Paragraph(f"<b>Valor Unitário Estimado:</b> R$ {round(pred[0], 2):,.2f}/m²", styles['Heading2']),
Paragraph(f"<b>Valor Total Estimado:</b> R$ {round(pred[0] * float(ATOTAL), -2):,.2f}", styles['Heading2']),
Spacer(1, 24),
Paragraph("Nota: Este valor é uma estimativa estatística com grau de confiança de 80%.", styles['Italic'])
]
doc.build(story)
return filename
# --- 2. FUNÇÃO DE INFERÊNCIA CORRIGIDA ---
def execute_valuation(FON_STR, X, Y, RH, ANO_C, ATOTAL, PADRAO, ANO_REF):
# Mapeamento manual das colunas para garantir ordem correta (O MODELO EXIGE A e B)
cols_v1 = ['FON', 'X', 'Y', 'RH', 'ANO_C', 'ATOTAL',
'A', 'B','C', 'D', 'E',
'ANO_2019', 'ANO_2020', 'ANO_2021', 'ANO_2022', 'ANO_2023', 'ANO_2024']
# 1. Tratamento de Inputs
is_oferta = 1 if FON_STR == "Oferta" else 0
# --- CORREÇÃO AQUI ---
# Precisamos criar as chaves para A, B, C, D, E para o modelo não quebrar.
# Inicializamos todas com 0.
padrões = {k: 0 for k in ['A', 'B', 'C', 'D', 'E']}
# Apenas se o padrão escolhido (C, D ou E) estiver na lista, setamos 1.
# Como A e B nunca serão escolhidos na UI, eles ficarão sempre como 0, o que é correto.
if PADRAO in padrões: padrões[PADRAO] = 1
# ---------------------
# Dummies de Ano V1 (2019-2024)
anos_v1 = {f'ANO_{a}': (1 if str(ANO_REF) == str(a) else 0) for a in range(2019, 2025)}
# Dummy de Ano V2 (2025)
is_2025 = 1 if str(ANO_REF) == '2025' else 0
# Montagem do Dicionário de Dados Brutos
data_dict = {
'FON': [is_oferta],
'X': [float(X)],
'Y': [float(Y)],
'RH': [float(RH)],
'ANO_C': [float(ANO_C)],
'ATOTAL': [np.log(float(ATOTAL))],
**padrões, # Agora isso expande A=0, B=0, C=..., D=..., E=...
**anos_v1,
'ANO_2025': [is_2025]
}
full_df = pd.DataFrame(data_dict)
try:
# --- PIPELINE DE PREDIÇÃO ---
# 1. Carregar Scalers e Modelos
input_scaler = joblib.load("dados/salas/input_scaler_salas_comerciais_2025.save")
output_scaler = joblib.load("dados/salas/output_scaler_salas_comerciais_2025.save")
model_v1_loaded = xgb.Booster()
model_v1_loaded.load_model("dados/salas/SALAS_COMERCIAIS_2020_a_2025.model")
model_v2_loaded = xgb.Booster()
model_v2_loaded.load_model("dados/salas/SALAS_COMERCIAIS_RESIDUAL_2026.model")
# 2. Preparar Input para V1
# Agora full_df TEM as colunas 'A' e 'B' (com valor 0), então não vai dar erro
df_v1_input = full_df[cols_v1].copy()
array_scaled = input_scaler.transform(df_v1_input)
df_v1_scaled = pd.DataFrame(array_scaled, columns=cols_v1)
# 3. Predição Base (V1)
base_pred = model_v1_loaded.predict(xgb.DMatrix(df_v1_scaled))
# 4. Preparar Input para V2
df_v2_input = df_v1_scaled.copy()
df_v2_input['ANO_2025'] = full_df['ANO_2025'].values
cols_v2_final = cols_v1 + ['ANO_2025']
dtest_v2 = xgb.DMatrix(df_v2_input[cols_v2_final])
# 5. Predição Final
dtest_v2.set_base_margin(base_pred)
final_pred_scaled = model_v2_loaded.predict(dtest_v2)
# 6. Inversão da Escala
pred_log = output_scaler.inverse_transform(np.array(final_pred_scaled).reshape(-1,1))
val_unit_real = np.exp(pred_log).flatten()[0]
# Resultados
total_val = val_unit_real * float(ATOTAL)
text_output = f"R$ {total_val:,.2f} (Total)\nR$ {val_unit_real:,.2f}/m²"
# Gerar PDF
pdf_path = get_report(FON_STR, X, Y, RH, ANO_C, ATOTAL, PADRAO, ANO_REF, [val_unit_real])
return text_output, pdf_path
except Exception as e:
return f"Erro no cálculo: {str(e)}", None
def load_inputs():
fon = gr.Radio(["Transação", "Oferta"], label="Fonte", value="Transação")
coord_x = gr.Number(label="Coordenada X (UTM)", value=281554)
coord_y = gr.Number(label="Coordenada Y (UTM)", value=1675418)
rh = gr.Slider(1, 380, label="Região Homogênea (RH)", value=120)
ano_c = gr.Number(label="Ano Construção", value=2020)
area = gr.Number(label="Área Total (Conforme Cadastro Imobiliário)", value=60)
padrao = gr.Radio(["C", "D", "E"], label="Padrão Construtivo", value="C")
ano_ref = gr.Dropdown([str(i) for i in range(2019, 2026)], label="Ano Base da Avaliação", value="2025")
return [fon, coord_x, coord_y, rh, ano_c, area, padrao, ano_ref]
title = 'Salas Comerciais - XGBoost Regressor - 2026'
description = f"""
# <p style="text-align: center;">Modelo para Salas Comerciais - XGBoost Regressor - 2026</p>
<p style="text-align: center;">Dados referentes aos anos 2019 a 2025.</p>
<hr style="color: #333; background-color: #333; height: 1px; border: none;">
"""