ESJL commited on
Commit
0d04fec
·
verified ·
1 Parent(s): 3bc556e

Update salas_2026.py

Browse files
Files changed (1) hide show
  1. salas_2026.py +123 -129
salas_2026.py CHANGED
@@ -1,154 +1,148 @@
1
- import openpyxl
2
- import xgboost as xgb
3
  import gradio as gr
4
- import joblib
5
  import pandas as pd
6
  import numpy as np
7
- import seaborn as sns
8
- import statsmodels.api as sm
9
- import matplotlib.pyplot as plt
10
- import io
11
- from sklearn.preprocessing import MinMaxScaler
12
- from reportlab.lib import colors
13
- from reportlab.lib.units import inch
14
- from reportlab.lib.pagesizes import A4, landscape
15
  from reportlab.lib.styles import getSampleStyleSheet
16
- from reportlab.pdfgen.canvas import Canvas
17
- from reportlab.platypus import SimpleDocTemplate, Spacer, Image, Frame, PageTemplate, BaseDocTemplate, Table, Paragraph, NextPageTemplate, PageBreak
18
-
19
- # Atualização dos argumentos para incluir B e o novo range de anos
20
- def get_report(FON, X, Y, RH, ANO_C, ATOTAL, B, C, D, E, ANO_2020, ANO_2021, ANO_2022, ANO_2023, ANO_2024, ANO_2025, pred):
21
- # Define the page size
22
- pagesize = A4
23
 
 
 
 
 
 
24
  def on_page(canvas, doc):
25
- page_num = canvas.getPageNumber()
26
- canvas.drawCentredString(pagesize[0] / 2, 50, str(page_num))
27
-
28
- def on_page_landscape(canvas, doc):
29
- return on_page(canvas, doc, pagesize=landscape(A4))
30
-
31
- padding = dict(
32
- leftPadding=72,
33
- rightPadding=72,
34
- topPadding=72,
35
- bottomPadding=18)
36
-
37
- portrait_frame = Frame(0, 0, *A4, **padding)
38
- landscape_frame = Frame(0, 0, *landscape(A4), **padding)
39
 
40
- portrait_template = PageTemplate(id='portrait',
41
- frames=portrait_frame,
42
- onPage=on_page,
43
- pagesize=A4)
44
-
45
- landscape_template = PageTemplate(id='landscape',
46
- frames=landscape_frame,
47
- onPage=on_page_landscape,
48
- pagesize=landscape(A4))
49
-
50
- doc = BaseDocTemplate('report.pdf',
51
- pageTemplates=[portrait_template,landscape_template])
52
-
53
  styles = getSampleStyleSheet()
 
 
 
54
 
 
55
  story = [
56
- Image('DAI.png', width=6.4 * inch, height=1.28 * inch),
57
- Spacer(1, 36),
58
- Paragraph('Modelo para Salas Comerciais - XGBoost Regressor - 2025', styles['Heading1']),
59
- Paragraph('Parâmetros do cálculo', styles['Heading2']),
60
- Paragraph(f"Fonte (Transação ou Oferta): {FON}", styles['Normal']),
61
- Paragraph(f"Coordenada X (TM-POA): {X}", styles['Normal']),
62
- Paragraph(f"Coordenada Y (TM-POA): {Y}", styles['Normal']),
63
- Paragraph(f"Região Homogênea: {RH}", styles['Normal']),
64
- Paragraph(f"Ano de Construção (Conforme Cadastro Imobiliário): {ANO_C}", styles['Normal']),
65
- Paragraph(f"Área Total (Conforme Cadastro Imobiliário): {ATOTAL}", styles['Normal']),
66
- Paragraph(f"Padrão Construtivo B: {'Sim' if B else 'Não'}", styles['Normal']),
67
- Paragraph(f"Padrão Construtivo C: {'Sim' if C else 'Não'}", styles['Normal']),
68
- Paragraph(f"Padrão Construtivo D: {'Sim' if D else 'Não'}", styles['Normal']),
69
- Paragraph(f"Padrão Construtivo E: {'Sim' if E else 'Não'}", styles['Normal']),
70
- Paragraph(f"Ano 2020 - Exercício Fiscal 2021: {'Sim' if ANO_2020 else 'Não'}", styles['Normal']),
71
- Paragraph(f"Ano 2021 - Exercício Fiscal 2022: {'Sim' if ANO_2021 else 'Não'}", styles['Normal']),
72
- Paragraph(f"Ano 2022 - Exercício Fiscal 2023: {'Sim' if ANO_2022 else 'Não'}", styles['Normal']),
73
- Paragraph(f"Ano 2023 - Exercício Fiscal 2024: {'Sim' if ANO_2023 else 'Não'}", styles['Normal']),
74
- Paragraph(f"Ano 2024 - Exercício Fiscal 2025: {'Sim' if ANO_2024 else 'Não'}", styles['Normal']),
75
- Paragraph(f"Ano 2025 - Exercício Fiscal 2026: {'Sim' if ANO_2025 else 'Não'}", styles['Normal']),
76
- Spacer(1, 36),
77
- Paragraph('Resultado', styles['Heading1']),
78
- Paragraph('Valor do imóvel (R$)', styles['Heading2']),
79
- Paragraph(f"R$ {round(pred[0][0] * ATOTAL, -2)}", styles['Normal']),
80
  ]
81
-
82
  doc.build(story)
83
-
84
 
85
- def execute(FON, X, Y, RH, ANO_C, ATOTAL, B, C, D, E, ANO_2020, ANO_2021, ANO_2022, ANO_2023, ANO_2024, ANO_2025):
86
- # Ajuste do dicionário para incluir B e remover 2019 / adicionar 2025
87
- df = pd.DataFrame.from_dict({'FON': [FON],
88
- 'X': [X],
89
- 'Y': [Y],
90
- 'RH': [RH],
91
- 'ANO_C': [ANO_C],
92
- 'ATOTAL': np.log(ATOTAL),
93
- 'B': 1 if B else 0,
94
- 'C': 1 if C else 0,
95
- 'D': 1 if D else 0,
96
- 'E': 1 if E else 0,
97
- 'ANO_2020': 1 if ANO_2020 else 0,
98
- 'ANO_2021': 1 if ANO_2021 else 0,
99
- 'ANO_2022': 1 if ANO_2022 else 0,
100
- 'ANO_2023': 1 if ANO_2023 else 0,
101
- 'ANO_2024': 1 if ANO_2024 else 0,
102
- 'ANO_2025': 1 if ANO_2025 else 0,
103
- })
104
-
105
- input_scaler = joblib.load("dados/salas/input_scaler_salas_2026.save")
106
- df = input_scaler.transform(df)
107
 
108
- # Atualização das colunas esperadas pelo modelo
109
- cols = ['FON', 'X', 'Y', 'RH', 'ANO_C', 'ATOTAL', 'B', 'C', 'D', 'E', 'ANO_2020', 'ANO_2021', 'ANO_2022', 'ANO_2023', 'ANO_2024', 'ANO_2025']
110
 
111
- aval = pd.DataFrame(df, columns = cols)
112
- df = xgb.DMatrix(aval)
113
- loaded_model = xgb.Booster()
114
- loaded_model.load_model("dados/salas/SALAS_COMERCIAIS_2020_a_2026.model")
115
- pred = loaded_model.predict(df)
116
- output_scaler = joblib.load("dados/salas/output_scaler_salas_2026.save")
117
- pred = output_scaler.inverse_transform(np.array(pred).reshape(-1,1))
118
- pred = np.exp(pred)
119
-
120
- get_report(FON, X, Y, RH, ANO_C, ATOTAL, B, C, D, E, ANO_2020, ANO_2021, ANO_2022, ANO_2023, ANO_2024, ANO_2025, pred)
121
 
122
- return f"""R${round(pred[0][0] * ATOTAL, -2)}""", 'report.pdf'
123
-
124
- def load_inputs():
125
- FON = gr.Checkbox(label='Fonte (Transação: desmarcar | Oferta: marcar)', value=False)
126
- X = gr.Number(label='Coordenada X (TM-POA)', value=281554.)
127
- Y = gr.Number(label='Coordenada Y (TM-POA)', value=1675418.)
128
- RH = gr.Number(value=120, label='Região Homogênea')
129
- ANO_C = gr.Number(value=2020, label='Ano de Construção (Conforme Cadastro Imobiliário)')
130
- ATOTAL = gr.Number(label='Área Total (Conforme Cadastro Imobiliário)', value=60.)
131
 
132
- # Inclusão do Padrão B
133
- B = gr.Checkbox(label='Padrão Construtivo B', value=False)
134
- C = gr.Checkbox(label='Padrão Construtivo C', value=True)
135
- D = gr.Checkbox(label='Padrão Construtivo D', value=False)
136
- E = gr.Checkbox(label='Padrão Construtivo E', value=False)
137
 
138
- # Atualização dos Anos (Removido 2019, Adicionado 2025)
139
- ANO_2020 = gr.Checkbox(label='Ano 2020 - Carga Geral 2021', value=False)
140
- ANO_2021 = gr.Checkbox(label='Ano 2021 - Carga Geral 2022', value=False)
141
- ANO_2022 = gr.Checkbox(label='Ano 2022 - Carga Geral 2023', value=False)
142
- ANO_2023 = gr.Checkbox(label='Ano 2023 - Carga Geral 2024', value=False)
143
- ANO_2024 = gr.Checkbox(label='Ano 2024 - Carga Geral 2025', value=False)
144
- ANO_2025 = gr.Checkbox(label='Ano 2025 - Carga Geral 2026', value=True)
 
 
 
 
 
 
 
 
145
 
146
- return [FON, X, Y, RH, ANO_C, ATOTAL, B, C, D, E, ANO_2020, ANO_2021, ANO_2022, ANO_2023, ANO_2024, ANO_2025]
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
147
 
148
- title = '(TESTE) Salas Comerciais - XGBoost Regressor - 2026'
149
 
150
  description = f"""
151
  # <p style="text-align: center;">Modelo para Salas Comerciais - XGBoost Regressor - 2026</p>
152
- <p style="text-align: center;">29.753 dados efetivamente utilizados referentes aos anos 2019 a 2025.</p>
153
  <hr style="color: #333; background-color: #333; height: 1px; border: none;">
154
  """
 
 
 
1
  import gradio as gr
 
2
  import pandas as pd
3
  import numpy as np
4
+ import joblib
5
+ import xgboost as xgb
6
+ from reportlab.lib.pagesizes import A4
7
+ from reportlab.platypus import Spacer, Frame, PageTemplate, BaseDocTemplate, Paragraph
 
 
 
 
8
  from reportlab.lib.styles import getSampleStyleSheet
 
 
 
 
 
 
 
9
 
10
+ # --- 1. FUNÇÃO GERADORA DE PDF ---
11
+ def get_report(FON, X, Y, RH, ANO_C, ATOTAL, PADRAO, ANO_REF, pred):
12
+ """Gera um PDF simples com o resultado da avaliação."""
13
+ filename = 'PDF_Avaliacao.pdf'
14
+
15
  def on_page(canvas, doc):
16
+ canvas.setTitle("Documento de Avaliação")
17
+ canvas.drawString(500, 800, "Pág. %d" % doc.page)
 
 
 
 
 
 
 
 
 
 
 
 
18
 
19
+ # Configuração do Documento
20
+ doc = BaseDocTemplate(filename, pagesize=A4)
 
 
 
 
 
 
 
 
 
 
 
21
  styles = getSampleStyleSheet()
22
+ frame = Frame(doc.leftMargin, doc.bottomMargin, doc.width, doc.height, id='normal')
23
+ template = PageTemplate(id='test', frames=frame, onPage=on_page)
24
+ doc.addPageTemplates([template])
25
 
26
+ # Conteúdo
27
  story = [
28
+ Paragraph("Documento de Avaliação - Modelo Híbrido 2026", styles['Title']),
29
+ Spacer(1, 12),
30
+ Paragraph(f"<b>Data de Referência:</b> {ANO_REF}", styles['Normal']),
31
+ Paragraph(f"<b>Características:</b> Padrão {PADRAO}, {ATOTAL} m², Construído em {ANO_C}", styles['Normal']),
32
+ Paragraph(f"<b>Localização:</b> X={X}, Y={Y}, RH={RH}", styles['Normal']),
33
+ Spacer(1, 24),
34
+ Paragraph(f"<b>Valor Unitário Estimado:</b> R$ {round(pred[0], 2):,.2f}/m²", styles['Heading2']),
35
+ Paragraph(f"<b>Valor Total Estimado:</b> R$ {round(pred[0] * float(ATOTAL), -2):,.2f}", styles['Heading2']),
36
+ Spacer(1, 24),
37
+ Paragraph("Nota: Este valor é uma estimativa estatística com grau de confiança de 80%.", styles['Italic'])
 
 
 
 
 
 
 
 
 
 
 
 
 
 
38
  ]
 
39
  doc.build(story)
40
+ return filename
41
 
42
+ # --- 2. FUNÇÃO DE INFERÊNCIA CORRIGIDA ---
43
+ def execute_valuation(FON_STR, X, Y, RH, ANO_C, ATOTAL, PADRAO, ANO_REF):
44
+ # Mapeamento manual das colunas para garantir ordem correta (O MODELO EXIGE A e B)
45
+ cols_v1 = ['FON', 'X', 'Y', 'RH', 'ANO_C', 'ATOTAL',
46
+ 'A', 'B','C', 'D', 'E',
47
+ 'ANO_2019', 'ANO_2020', 'ANO_2021', 'ANO_2022', 'ANO_2023', 'ANO_2024']
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
48
 
49
+ # 1. Tratamento de Inputs
50
+ is_oferta = 1 if FON_STR == "Oferta" else 0
51
 
52
+ # --- CORREÇÃO AQUI ---
53
+ # Precisamos criar as chaves para A, B, C, D, E para o modelo não quebrar.
54
+ # Inicializamos todas com 0.
55
+ padrões = {k: 0 for k in ['A', 'B', 'C', 'D', 'E']}
 
 
 
 
 
 
56
 
57
+ # Apenas se o padrão escolhido (C, D ou E) estiver na lista, setamos 1.
58
+ # Como A e B nunca serão escolhidos na UI, eles ficarão sempre como 0, o que é correto.
59
+ if PADRAO in padrões: padrões[PADRAO] = 1
60
+ # ---------------------
 
 
 
 
 
61
 
62
+ # Dummies de Ano V1 (2019-2024)
63
+ anos_v1 = {f'ANO_{a}': (1 if str(ANO_REF) == str(a) else 0) for a in range(2019, 2025)}
 
 
 
64
 
65
+ # Dummy de Ano V2 (2025)
66
+ is_2025 = 1 if str(ANO_REF) == '2025' else 0
67
+
68
+ # Montagem do Dicionário de Dados Brutos
69
+ data_dict = {
70
+ 'FON': [is_oferta],
71
+ 'X': [float(X)],
72
+ 'Y': [float(Y)],
73
+ 'RH': [float(RH)],
74
+ 'ANO_C': [float(ANO_C)],
75
+ 'ATOTAL': [np.log(float(ATOTAL))],
76
+ **padrões, # Agora isso expande A=0, B=0, C=..., D=..., E=...
77
+ **anos_v1,
78
+ 'ANO_2025': [is_2025]
79
+ }
80
 
81
+ full_df = pd.DataFrame(data_dict)
82
+
83
+ try:
84
+ # --- PIPELINE DE PREDIÇÃO ---
85
+
86
+ # 1. Carregar Scalers e Modelos
87
+ input_scaler = joblib.load("input_scaler_salas_comerciais_2025.save")
88
+ output_scaler = joblib.load("output_scaler_salas_comerciais_2025.save")
89
+ model_v1_loaded = xgb.Booster()
90
+ model_v1_loaded.load_model("SALAS_COMERCIAIS_2020_a_2025.model")
91
+ model_v2_loaded = xgb.Booster()
92
+ model_v2_loaded.load_model("SALAS_COMERCIAIS_RESIDUAL_2026.model")
93
+
94
+ # 2. Preparar Input para V1
95
+ # Agora full_df TEM as colunas 'A' e 'B' (com valor 0), então não vai dar erro
96
+ df_v1_input = full_df[cols_v1].copy()
97
+
98
+ array_scaled = input_scaler.transform(df_v1_input)
99
+ df_v1_scaled = pd.DataFrame(array_scaled, columns=cols_v1)
100
+
101
+ # 3. Predição Base (V1)
102
+ base_pred = model_v1_loaded.predict(xgb.DMatrix(df_v1_scaled))
103
+
104
+ # 4. Preparar Input para V2
105
+ df_v2_input = df_v1_scaled.copy()
106
+ df_v2_input['ANO_2025'] = full_df['ANO_2025'].values
107
+
108
+ cols_v2_final = cols_v1 + ['ANO_2025']
109
+ dtest_v2 = xgb.DMatrix(df_v2_input[cols_v2_final])
110
+
111
+ # 5. Predição Final
112
+ dtest_v2.set_base_margin(base_pred)
113
+ final_pred_scaled = model_v2_loaded.predict(dtest_v2)
114
+
115
+ # 6. Inversão da Escala
116
+ pred_log = output_scaler.inverse_transform(np.array(final_pred_scaled).reshape(-1,1))
117
+ val_unit_real = np.exp(pred_log).flatten()[0]
118
+
119
+ # Resultados
120
+ total_val = val_unit_real * float(ATOTAL)
121
+ text_output = f"R$ {total_val:,.2f} (Total)\nR$ {val_unit_real:,.2f}/m²"
122
+
123
+ # Gerar PDF
124
+ pdf_path = get_report(FON_STR, X, Y, RH, ANO_C, ATOTAL, PADRAO, ANO_REF, [val_unit_real])
125
+
126
+ return text_output, pdf_path
127
+
128
+ except Exception as e:
129
+ return f"Erro no cálculo: {str(e)}", None
130
+
131
+ def load_inputs():
132
+ fon = gr.Radio(["Transação", "Oferta"], label="Fonte", value="Transação")
133
+ coord_x = gr.Number(label="Coordenada X (UTM)", value=281554)
134
+ coord_y = gr.Number(label="Coordenada Y (UTM)", value=1675418)
135
+ rh = gr.Slider(1, 380, label="Região Homogênea (RH)", value=120)
136
+ ano_c = gr.Number(label="Ano Construção", value=2020)
137
+ area = gr.Number(label="Área Privativa (m²)", value=60)
138
+ padrao = gr.Radio(["C", "D", "E"], label="Padrão Construtivo", value="C")
139
+ ano_ref = gr.Dropdown([str(i) for i in range(2020, 2026)], label="Ano Base da Avaliação", value="2025")
140
+ return [fon, coord_x, coord_y, rh, ano_c, area, padrao, ano_ref]
141
 
142
+ title = 'Salas Comerciais - XGBoost Regressor - 2026'
143
 
144
  description = f"""
145
  # <p style="text-align: center;">Modelo para Salas Comerciais - XGBoost Regressor - 2026</p>
146
+ <p style="text-align: center;">Dados referentes aos anos 2019 a 2025.</p>
147
  <hr style="color: #333; background-color: #333; height: 1px; border: none;">
148
  """